image image


image

Tutorial: Flucht und Verfolgung mit Processing.py

Ein Sonntag ohne ein wenig mit Processing.py zu spielen, ist in Zeiten der Pandemie kein Sonntag für mich. Also habe ich mir das erste der gestern vorgestellten Videos vorgenommen und mir überlegt, was man daraus schickes entwickeln kann. Herausgekommen ist ein kleines Experiment, das zeigt, wie man »Flucht und Verfolgung« (Chasing and Evading) für Spiele in Python implementieren kann.

image image

Ich habe zuerst einen kleinen Spieler implementiert, der mit der Tastatur im Bildschirmfenster bewegt werden, aber dieses nicht verlassen kann. Natürlich bekam er eine eigene Klasse Player spendiert:

class Player():
    
    def __init__(self):
        self.pos = PVector(width/2, height/2)
        self.w = self.h = 32
        self.max_speed = 6
        self.speed = PVector(0, 0)
        self.img = loadImage("knt1_fr1.gif")
        self.debug = True # False
        
    def update(self):
        if self.speed.x >= self.max_speed:
            self.speed.x = self.max_speed
        if self.speed.y >= self.max_speed:
            self.speed.y = self.max_speed
        self.pos.x += self.speed.x
        self.pos.y += self.speed.y
        self.check_boundaries()
        
    def display(self):
        image(self.img, self.pos.x, self.pos.y)
        if self.debug:
            fill(255, 255, 0, 100)
            circle(self.pos.x + self.w/2, self.pos.y + self.h/2, 400)
    
    def check_boundaries(self):
        if self.pos.x >= width - self.w:
            self.pos.x = width - self.w
        elif self.pos.x <= 0:
            self.pos.x = 0
        if self.pos.y >= height - self.h:
            self.pos.y = height - self.h
        elif self.pos.y <= 0:
            self.pos.y = 0

Bis auf die Tastaturabfrage ist die gesamte Logik für das Spiel hier implementiert. Die Tastaturabfrage im Hauptprogramm lenkt und beschleunigt den Spieler:

def keyPressed():
    global player
    if key == CODED:
        if keyCode == UP:
            player.speed.y -= vel
        elif keyCode == DOWN:
            player.speed.y += vel
        elif keyCode == LEFT:
            player.speed.x -= vel
        elif keyCode == RIGHT:
            player.speed.x += vel
            
def keyReleased():
    global player
    if key == CODED:
        if keyCode == UP or keyCode == DOWN:
            player.speed.y = 0
        elif keyCode == LEFT or keyCode == RIGHT:
            player.speed.x = 0

Der Spieler wird natürlich nicht unbegrenzt beschleunigt, sondern seine Maximalgeschwindigkeit wird durch die Variable max_speed nach oben begrenzt. Eine Besonderheit in der Player-Klasse ist auch noch der Debug Mode. Wird nämlich debug = True gesetzt, wird um die Spielerfigur ein (halb-) transparenter Kreis gezeichnet, der zeigt, ab wann er vom Gegner gesehen und verfolgt werden kann.

Ansonsten ist die Klasse straight forward: Die Methode update() holt sich die Beschleunigung von der Tastaturabfrage ab und display() zeigt das Bild des Spielers an der aktuellen Position an. Außerdem ruft update() auch noch die Methode check_boundaries() auf, die dafür sorgt, daß die Spielerfigur nicht über die Ränder des Spielefensters verschwinden kann.

Die Klasse Enemy ist der Klasse Player sehr ähnlich, der Konstruktor ist nahezu identisch, außer daß die Startposition des Gegeners per Zufall festgelegt wird:

class Enemy():
    
    def __init__(self):
        self.pos = PVector(random(width - 200) + 100, random(height - 200) + 100)
        self.w = self.h = 32
        self.vel = PVector(0, 0)
        self.img = loadImage("npc3_fr1.gif")
                    
    def update(self):
        self.pos.x += self.vel.x
        self.pos.y += self.vel.y
    
    def display(self):
        image(self.img, self.pos.x, self.pos.y)

    def chase(self, p):
        if dist(self.pos.x, self.pos.y, p.x, p.y) > 200:
            self.vel.x = self.vel.y = 0
            return
        if abs(self.pos.x - p.x) < abs(self.pos.y - p.y):
            # Vertical separation is bigger
            if self.pos.y < p.y:
                self.vel.y = 2
                self.vel.x = 0
            else:
                self.vel.y = -2
                self.vel.x = 0
        else:
            # Horizontal separation is bigger
            if self.pos.x < p.x:
                self.vel.x = 2
                self.vel.y = 0
            else:
                self.vel.x = -2
                self.vel.y = 0

Der Gegner soll natürlich ein halbwegs intelligentes Verhalten zeigen, das in der Methode chase() implementiert ist. Dort wird zuerst überprüft, ob der Spieler überhaupt im Sichtradius des Gegners ist. Wenn nicht, wird die Methode chase() vom Gegner wieder verlassen (mit return). Ist der Spieler aber in Sichtweite, überprüft die Methode, ob der x- oder der y-Abstand zwischen den beiden der größere ist und versucht, den größeren Abstand zwischen den beiden Protagonisten zu verkürzen (dabei wird am Vorzeichen des Abstandes festgestellt, ob sich der Spieler rechts oder links respektive oberhalb oder unterhalb des Gegners befindet.)

In der draw()-Funktion des Hauptprogramms werden erst die update()- und draw()-Methoden für den Spieler und dann die chase()-, update()- und draw()-Methoden des Gegners aufgerufen. Das komplette Hauptprogramm sieht so aus:

from player import Player
from enemy import Enemy

WIDTH = 800
HEIGHT = 600
vel = 2

def setup():
    global enemy, player
    size(WIDTH, HEIGHT)
    this.surface.setTitle("Flucht & Verfolgung")
    player = Player()
    enemy = Enemy()

def draw():
    global enemy, player
    background(107, 142, 35)
    player.update()
    player.display()
    
    enemy.chase(player.pos)
    enemy.update()
    enemy.display()

def keyPressed():
    global player
    if key == CODED:
        if keyCode == UP:
            player.speed.y -= vel
        elif keyCode == DOWN:
            player.speed.y += vel
        elif keyCode == LEFT:
            player.speed.x -= vel
        elif keyCode == RIGHT:
            player.speed.x += vel
            
def keyReleased():
    global player
    if key == CODED:
        if keyCode == UP or keyCode == DOWN:
            player.speed.y = 0
        elif keyCode == LEFT or keyCode == RIGHT:
            player.speed.x = 0

Die beiden Sprites sind aus der »Last Guardian Sprite Collection« von Philipp Lenssen, die dieser unter der CC-BY 3.0-Lizenz auf OpenGameArt.org veröffentlicht hat. Die Lizenz verlangt einen Link auf die Seite des Schöpfers und die Namensnennung. Das ist hiermit geschehen.

Den Quellcode und die Assets habe ich auch auf GitHub hochgeladen. Habt mindestens ebensoviel Spaß damit, wie ich beim Programmieren hatte.


(Kommentieren) 

image image



Über …

Der Schockwellenreiter ist seit dem 24. April 2000 das Weblog digitale Kritzelheft von Jörg Kantel (Neuköllner, EDV-Leiter Rentner, Autor, Netzaktivist und Hundesportler — Reihenfolge rein zufällig). Hier steht, was mir gefällt. Wem es nicht gefällt, der braucht ja nicht mitzulesen. Wer aber mitliest, ist herzlich willkommen und eingeladen, mitzudiskutieren!

Alle eigenen Inhalte des Schockwellenreiters stehen unter einer Creative-Commons-Lizenz, jedoch können fremde Inhalte (speziell Videos, Photos und sonstige Bilder) unter einer anderen Lizenz stehen.

Der Besuch dieser Webseite wird aktuell von der Piwik Webanalyse erfaßt. Hier können Sie der Erfassung widersprechen.

Diese Seite verwendet keine Cookies. Warum auch? Was allerdings die iframes von Amazon, YouTube und Co. machen, entzieht sich meiner Kenntnis.


Werbung

Diese Spalte wurde absichtlich leergelassen!


Werbung


image  image  image
image  image  image


image