image image


Tutorial: Basic Side Scroller mit Processing.py, Teil 2

Heute möchte ich mein hier begonnenes Tutorial über einen Side-Scroller mit Bewegungsparallaxe abschließen. Die Spielfigur (das kleine rosa Alien) soll hüpfen können und es soll Hindernisse geben, über die sie springen muß. Erfreulicherweise muß dafür nur wenig im bisherigen Code ergänzt werden, denn da die Bewegungsparallaxe schon weitgehend vollständig programmiert ist, bleibt der Reiter backgrounds.py von Änderungen verschont.

image

Den Reiter sprites.py habe ich erst einmal um die Klasse Obstacle() erweitert, die ebenfalls von der Klasse Sprite() erbt1:

class Obstacle(Sprite):
    
            def __init__(self, x, y, im):
                Sprite.__init__(self, x, y)
                self.im = loadImage(im + ".png")
                self.step = -4
           
            def update(self):
                self.x += self.step
                if self.x <= -150:
                    self.x = width + 750
            
            def show(self):
                image(self.im, self.x, self.y)

Die verschiedenen Hindernisse unterscheiden sich im Prinzip nur durch ihre unterschiedlichen Bilder, daher wird der Bildname (ohne Suffix) dem Konstruktor mitgegeben, der sich daraus den Namen der Bilddatei zusammenbastelt. Das Beispielprogramme besitzt drei verschiedene Hindernisse, die – sobald sie den linken Bildrand verlassen haben – weit rechts außerhalb des Bildes wieder ins Spiel kommen. Den Parameter width + 750 habe ich experimentell ermittelt und muß bei mehr oder anderen Hindernissen angepaßt werden.

Auch die Bilder der Hindernisse habe ich der freien Sprite-Sammlung von Kenney.nl entnommen.

Im Hauptprogramm habe ich die Hindernisse dann in der setup()-Funktion – nach den Hügeln, aber vor dem Alien – eingebaut

    obstacles.append(Obstacle(width + 150, 330, "spikes"))
    obstacles.append(Obstacle(width + 650, 330, "spikesBottomAlt"))
    obstacles.append(Obstacle(width + 750, 330, "spikesBottom"))
    obstacles.append(Obstacle(width + 1190, 330, "fence"))
    obstacles.append(Obstacle(width + 1250, 330, "fenceBroken"))
    obstacles.append(Obstacle(width + 1310, 330, "fence"))

und in draw() (ebenfalls nach den Hintergründen aber vor dem Alien) zum Leben erweckt:

    for obstacle in obstacles:
        obstacle.update()
        obstacle.show()

Die Hindernisse sind noch einmal einen Tick (-4) schneller, als die kleinen Hügel im Hintergrund, so daß hiermit eine vierte Parallaxen-Ebene erschaffen wurde.

Damit das kleine rosa Alien nicht in die Hindernisse hineinrennt, muß die Möglichkeit geschaffen werden, daß es darüber springen kann. Dazu mußten in der Klasse Alien() einige Veränderungen vorgenommen werden. Erst einmal wurde die Methode update() überschrieben

    def update(self):
        if self.status == "jumping":
            self.vely += 0.1

und in der Methode show() das Alien zu einem kleinen endlichen Automaten mit zwei Zuständen definiert:

    def show(self):
        if self.status == "walking":
            self.count += 1
            if self.count > 15:
                self.count = 0
            if self.count < 8:
                image(self.im1, self.x, self.y)
            else:
                image(self.im2, self.x, self.y)
        elif self.status == "jumping":
            self.y += self.vely
            image(self.im3, self.x, self.y)
            if self.y >= 320:
                self.y = 320
                self.status = "walking"

Wenn das Alien im Zustand jumping ist, bleibt es so lange in diesem Zustand, bis es wieder den Boden berührt. Dann geht es in den Zustand walking über. Getriggert wird dieser endliche Automat im Hauptprogramm, das um die Funktion keyPressed() ergänzt wurde:

def keyPressed():
    alien = sprites[0]
    if key == CODED:
        if keyCode == UP and alien.status == "walking":
            alien.vely = -5
            alien.status = "jumping"

Wenn die Pfeiltaste nach oben gedrückt wird und das Alien im Zustand walking ist, dann (und nur dann) 2 wird vely auf -5 gesetzt und das Alien geht in den Zustand jumping über.

Mit der Zeile alien = sprites[0] habe ich die Funktion etwas lesefreundlicher gemacht, denn die Liste sprites ist auch hier eigentlich overkill. Aber wenn Ihr das Spiel mal ergänzen wollt, indem Dutzende von Aliens Eure Spielwelt bevölkern, werdet Ihr froh sein, wenn Ihr diese Listenstruktur vorausschauend angelegt habt.

Bevor ich das gesamte Programm noch einmal aufliste, zuerst die verwendeten Bilder, ohne die Ihr das Spiel ja nicht nachprogrammieren könnt:

image image image image image image image image

Der Reiter backgrounds.py ist seit der ersten Version unverändert geblieben:

# coding=utf-8

class Hill(object):
    
    def __init__(self, x, r, s, c):
        self.xpos = x
        self.radius = r
        self.step = s
        self.col = c
    
    def update(self):
        self.xpos += self.step
        if self.xpos <= -self.radius:
            self.xpos = width + self.radius
    
    def show(self):
        fill(self.col)
        circle(self.xpos, 400, 2*self.radius)

class Cloud(object):
    
    def __init__(self, x, s):
        self.xpos = x
        self.step = s
    
    def update(self):
        self.xpos += self.step
        if self.xpos <= -200:
            self.xpos = width + 200
    
    def show(self):
        fill("#ffffff")
        circle(self.xpos, 150, 100)
        circle(self.xpos, 200, 100)
        circle(self.xpos - 50, 200, 100)
        circle(self.xpos + 50, 200, 100)

Die meisten Veränderungen gab es im Reiter sprites.py:

# coding=utf-8

class Sprite(object):
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.vely = 0
        self.step = 0
        
    
    def update(self):
        pass
    
    def show(self):
        pass

class Alien(Sprite):
    
    def __init__(self, x, y):
        Sprite.__init__(self, x, y)
        self.status = "walking"
        self.im1 = loadImage("alienwalk1.png")
        self.im2 = loadImage("alienwalk2.png")
        self.im3 = loadImage("alienjump.png")
        self.count = 0

    def update(self):
        if self.status == "jumping":
            self.vely += 0.1
               
    def show(self):
        if self.status == "walking":
            self.count += 1
            if self.count > 15:
                self.count = 0
            if self.count < 8:
                image(self.im1, self.x, self.y)
            else:
                image(self.im2, self.x, self.y)
        elif self.status == "jumping":
            self.y += self.vely
            image(self.im3, self.x, self.y)
            if self.y >= 320:
                self.y = 320
                self.status = "walking"


class Obstacle(Sprite):
    
            def __init__(self, x, y, im):
                Sprite.__init__(self, x, y)
                self.im = loadImage(im + ".png")
                self.step = -4
           
            def update(self):
                self.x += self.step
                if self.x <= -150:
                    self.x = width + 750
            
            def show(self):
                image(self.im, self.x, self.y)

Und auch das Hauptprogramm hat einige Veränderungen erfahren:

# Side Scroller 2
from backgrounds import Hill, Cloud
from sprites import Alien, Obstacle

FPS = 60

clouds = []
bighills = []
smallhills = []
sprites = []
obstacles = []

def setup():
    size(800, 450)
    this.surface.setTitle("Side Scroller 2: Jump, Ally, jump!")
    clouds.append(Cloud(400, -1))
    for i in range(3):
        bighills.append(Hill(i*400, 200, -2, "#63e06b"))
    for i in range(6):
        smallhills.append(Hill(i*200, 100, -3, "#217424"))
    obstacles.append(Obstacle(width + 150, 330, "spikes"))
    obstacles.append(Obstacle(width + 650, 330, "spikesBottomAlt"))
    obstacles.append(Obstacle(width + 750, 330, "spikesBottom"))
    obstacles.append(Obstacle(width + 1190, 330, "fence"))
    obstacles.append(Obstacle(width + 1250, 330, "fenceBroken"))
    obstacles.append(Obstacle(width + 1310, 330, "fence"))
    sprites.append(Alien(66, 320))
    frameRate(FPS)
    noStroke()

def draw():
    background(64, 176, 226)
    # Wolke(n) im Hintergrund
    for cloud in clouds:
        cloud.update()
        cloud.show()
    # Große Hügel im Hintergrund
    for hill in bighills:
        hill.update()
        hill.show()
    # Kleine Hügel im Vordergrund         
    for hill in smallhills:
        hill.update()
        hill.show()
    # Erdboden    
    fill("#ffd05e")
    rect(0, 400, width, 50)
    # Obstacles
    for obstacle in obstacles:
        obstacle.update()
        obstacle.show()
    # Sprites (erst einmal nur das rosa Alien)
    for sprite in sprites:
        sprite.update()
        sprite.show()

def keyPressed():
    alien = sprites[0]
    if key == CODED:
        if keyCode == UP and alien.status == "walking":
            alien.vely = -5
            alien.status = "jumping"

Das war es dann erst einmal mit dem kleinen Sidescroller-Programm. Natürlich ist es kein fertiges Spiel, es sollte nur zeigen, wie man einen Sidescroller mit Parallaxenverschiebung programmiert. Der nächste Schritt wäre, Kollisionserkennung und komplexere Hindernisse einzubauen.

Einen interessanten Ansatz zur Kollisionserkennung – speziell für Sidescroller und Platformer – hat jemand, der sich DaFluffyPotato nennt, in obigem Video veröffentlicht. Seine Programmierumgebung ist Pygame, aber das Prinzip läßt sich sicher leicht nach Processing.py portieren. Wer weiß, vielleicht wird dies mein nächstes Tutorial? Still digging!

  1. Das ist momentan ein wenig Overkill, aber wenn Ihr später zum Beispiel Kollisionserkennung einbauen wollt, dann ist es sinnvoll, diverse Sprite-Listen zu verwalten, um sie auf Kollision überprüfen zu können. 

  2. Durch das logische und wird verhindert, daß das Alien durch das Drücken der Pfeiltast weiter nach oben katapultiert werden kann, wenn es sich schon in der Luft befindet. Denn das Alien soll nur springen können, wenn es festen Boden unter den Füßen hat. 


(Kommentieren) 

image image



Über …

Der Schockwellenreiter ist seit dem 24. April 2000 das Weblog digitale Kritzelheft von Jörg Kantel (Neuköllner, EDV-Leiter, 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


Werbung


image  image  image
image  image  image


image