Ich fand, es war Zeit, mal wieder mein letztes Experiment »Flucht und Verfolgung mit Processing.py« aufzunehmen und weiterzuentwickeln. Für den Anfang hatte ich mir nicht viel vorgenommen, es sollten lediglich statt einem Verfolger viele Verfolger den Spieler jagen. Doch zuerst habe ich in der Klasse Player
eine kleine Verbesserung vorgenommen. Anstelle der etwas umständlichen Abfragen in der update()
-Methode
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
habe ich dies durch die eine Zeile
self.speed.limit(self.max_speed)
ersetzt. Hätte ich eigentlich schon damals drauf kommen sollen.
Ansonsten habe ich der Klasse Player
noch eine Methode zur (Kreis-) Kollisionserkennung spendiert:
def check_collision(self, other):
distance = dist(self.pos.x, self.pos.y, other.pos.x, other.pos.y)
if abs(distance) > self.r + other.r:
return(False)
else:
return(True)
Eine Kollisionserkennung zweier Kreise ist einfacher zu implementieren als eine Kollisionserkennung zweier Rechtecke und die Form der Sprites lud zu einer Approximierung durch zwei Kreise einfach ein.
Die Klasse Enemy
hat sich gegenüber der letzten Version nicht verändert, außer daß ich – wie auch schon für Player
und auch für das Hauptprogramm einige häufig benötigte Konstanten in einen Reiter Settings
ausgelagert habe:
WIDTH = 960
HEIGHT = 540
vel = 2
distance = 100
no_enemies = 7
Daher importieren alle übrigen Reiter mit from settings import *
gnadenlos den komletten Inhalt von settings
.
Ansonsten gab es im Hauptprogramm nur Änderungen in den Funktionen setup()
und draw()
, die statt einem Gegner mehrere Gegner initialisierten und checkten:
def setup():
…
for _ in range(no_enemies):
enemies.append(Enemy())
def draw():
…
for enemy in enemies:
enemy.chase(player.pos)
enemy.update()
enemy.display()
if player.check_collision(enemy):
fill(200, 0, 0)
text(u"Du hast verloren!", 20, 40)
noLoop()
player.update()
player.display()
Ansonsten blieb auch hier das Programm nahezu unverändert.
Für alle, die diese Implementierung nachvollziehen und -programmieren wollen, hier der vollständige Sketch. Zuerst noch einmal den Reiter settings.py
:
WIDTH = 960
HEIGHT = 540
vel = 2
distance = 100
no_enemies = 7
Dann die Klasse Player
(im Reiter player.py
):
from settings import *
class Player():
def __init__(self):
self.pos = PVector(width/2, height/2)
self.w = self.h = self.d = 32
self.r = self.d/2 # Radius
self.max_speed = 3
self.speed = PVector(0, 0)
self.img = loadImage("knt1_fr1.gif")
self.debug = True # False
def update(self):
self.speed.limit(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, distance*2)
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
# Kreis-Kollision
def check_collision(self, other):
distance = dist(self.pos.x, self.pos.y, other.pos.x, other.pos.y)
if abs(distance) > self.r + other.r:
return(False)
else:
return(True)
Gefolgt von der Klasse Enemy
(im Reiter enemy.py
):
from settings import *
class Enemy():
def __init__(self):
self.pos = PVector(random(width - 200) + 100, random(height - 200) + 100)
self.w = self.h = self.d = 32
self.r = self.d/2 # Radius
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) > distance:
self.vel = PVector(0, 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 = vel
self.vel.x = 0
else:
self.vel.y = -vel
self.vel.x = 0
else:
# Horizontal separation is bigger
if self.pos.x < p.x:
self.vel.x = vel
self.vel.y = 0
else:
self.vel.x = -vel
self.vel.y = 0
Und last but not least das Hauptprogramm:
from player import Player
from enemy import Enemy
from settings import *
enemies = []
def setup():
global enemy, player
size(WIDTH, HEIGHT)
this.surface.setTitle("Flucht & Verfolgung v02")
my_font = createFont("American Typewriter", 30)
textFont(my_font)
player = Player()
for _ in range(no_enemies):
enemies.append(Enemy())
def draw():
global enemy, player
background(107, 142, 35)
for enemy in enemies:
enemy.chase(player.pos)
enemy.update()
enemy.display()
if player.check_collision(enemy):
fill(200, 0, 0)
text(u"Du hast verloren!", 20, 40)
noLoop()
player.update()
player.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
Wenn Ihr das Programm durchspielt, werdet Ihr feststellen, daß das Verhalten der Gegner oft nicht sehr intelligent anmutet. Sie laufen meist auf einer geraden Linie auf den Player
zu, um erst kurz vor Erreichen des Zieles auf eine Diagonale einzuschwenken. In einer der nächsten Folgen werde ich dies durch einen besseren Algorithmus optimieren.
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 findet Ihr wie immer auch auf GitHub.
Ü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!