Anleitung: Flappy Bird#

In diesem Kapitel erstellen wir Schritt für Schritt ein Flappy Bird-Spiel unter Verwendung der miniworlds_physics Engine.

Flappy-Bird-Spiel


Step 1: Importing the physics engine#

Installiere das Paket miniworlds_physics genauso, wie du miniworlds installiert hast. Danach importierst du die PhysicsWorld:

import random
from miniworlds import Actor, Number, Text
from miniworlds_physics import PhysicsWorld  # Import der Physik-Engine

world = PhysicsWorld(800, 600)
# Dein Code kommt hier hin
world.run()

Schritt 2: Röhren (Pipes) erstellen#

Add tubes and save in a list#

Add multiple pipes and store them in a list to easily change their properties later:

pipes = [
    Actor(position=(300, world.height - 280)),
    Actor(position=(500, 0)),
    Actor(position=(700, world.height - 280)),
    Actor(position=(900, 0))
]

Explanation:#

  • Four pipes are created and stored in the list pipes[].

Set tube properties#

Use a loop to set properties for all tubes:

for pipe in pipes:
    pipe.direction = 0
    pipe.add_costume("images/pipe1.png")
    pipe.size = (50, 280)
    pipe.passed = False
    pipe.physics.simulation = "manual"  # Manuelle Steuerung
    pipe.physics.velocity_x = -150  # Geschwindigkeit der Pipes
    pipe.origin = "topleft"  # Oben links als Referenzpunkt

For the 2nd and 4th tube (which point from top to bottom), the orientation is rotated by 180 degrees:

pipes[1].costume.orientation = -180
pipes[3].costume.orientation = -180

Explanation:#

  • pipe.physics.simulation: Mit manual wird festgelegt, dass die Schwerkraft keine Wirkung auf das Rohr hat.

  • pipe.physics.velocity_x: Die Bewegung erfolgt durch Ändern der Geschwindigkeit.

  • pipe.origin: Der Referenzpunkt ist die obere linke Ecke des Rohrs, was das Verschieben vereinfacht.

Register methods for the tubes#

Now we add two important methods to the tubes:

for pipe in pipes:
    @pipe.register
    def act(self):
        if self.x < 75 and not self.passed:
            self.passed = True

    @pipe.register
    def on_detecting_left_border(self):
        self.move_to((self.x + random.randint(750, 800), self.y))
        self.passed = False

Explanation:#

  • act: Diese Methode überprüft, ob die Röhre vom Spieler überquert wurde.

  • on_detecting_left_border: When the tube reaches the left edge of the screen, it is moved to a random position on the right.


Schritt 3: Den Vogel (Bird) erstellen#

Set attribute#

The bird is created as an Actor and its attributes are set:

bird = Actor()
bird.position = (75, 200)
bird.add_costume("images/fly.png")
bird.size = (60, 60)
bird.physics.simulation = "simulated"
bird.is_flipped = True
bird.physics.size = (0.8, 0.8)
bird.physics.shape_type = "circle"
bird.is_rotatable = False

Explanation:#

  • bird.physics.simulation: The bird is affected by gravity.

  • bird.physics.size: Ein kleinerer Kollisionsbereich macht das Spiel etwas einfacher.

Register methods for the bird#

Detecting the edge of the playing field#

When the bird leaves the screen, the game ends with a “Game Over” message:

@bird.register
def on_detecting_borders(self, borders):
    if "bottom" in borders or "top" in borders:
        end = Text("Game over!", position=(400, 200))
        world.game_over = True
        world.stop()

Detection von Kollisionen mit Röhren#

If the bird touches a pipe, the game also ends:

@bird.register
def on_detecting_actor(self, other):
    if other in pipes:
        end = Text("Game over!", position=(200, 200))
        world.game_over = True
        world.stop()

Detecting user input#

When the space bar is pressed, the bird moves up:

@bird.register
def on_key_down_space(self):
    self.physics.velocity_y = -200
    if not world.is_running and not world.game_over:
        world.start()

Step 4: Punktestand hinzufügen#

The score display is statically set so that it is not affected by physics:

score = Number()
score.position = (30, 30)
score.size = (40, 40)
score.physics.simulation = "static"

In der act-Methode der Röhren wird der Punktestand jedes Mal erhöht, wenn der Spieler eine Röhre überquert:

@pipe.register
def act(self):
    if self.x < 75 and not self.passed:
        self.passed = True
        score.inc()

Vollständiger Code#

import random
from miniworlds import Actor, Number, Text
from miniworlds_physics import PhysicsWorld

world = PhysicsWorld(800, 600)
world.game_over = False
world.add_background("images/background.png")

pipes = [
    Actor(position=(300, world.height - 280)),
    Actor(position=(500, 0)),
    Actor(position=(700, world.height - 280)),
    Actor(position=(900, 0))
]

for pipe in pipes:
    pipe.add_costume("images/pipe1.png")
    pipe.size = (50, 280)
    pipe.passed = False
    pipe.physics.simulation = "manual"
    pipe.physics.velocity_x = -150
    pipe.origin = "topleft"

    @pipe.register
    def act(self):
        if self.x < 75 and not self.passed:
            self.passed = True
            score.inc()

    @pipe.register
    def on_detecting_left_border(self):
        self.move_to((self.x + random.randint(750, 800), self.y))
        self.passed = False

pipes[1].costume.orientation = -180
pipes[3].costume.orientation = -180

score = Number()
score.position = (30, 30)
score.size = (40, 40)
score.physics.simulation = "static"

bird = Actor()
bird.position = (75, 200)
bird.add_costume("images/fly.png")
bird.size = (60, 60)
bird.physics.simulation = "simulated"
bird.is_flipped = True
bird.physics.size = (0.8, 0.8)
bird.is_rotatable = False

@bird.register
def on_detecting_borders(self, borders):
    if "bottom" in borders or "top" in borders:
        end = Text("Game over!", position=(400, 200))
        world.game_over = True
        world.stop()

@bird.register
def on_detecting_actor(self, other):
    if other in pipes:
        end = Text("Game over!", position=(200, 200))
        world.game_over = True
        world.stop()

@bird.register
def on_key_down_space(self):
    self.physics.velocity_y = -200
    if not world.is_running and not world.game_over:
        world.start()

world.run()