Tutorial: Roter Baron#
In diesem Kapitel erstellen wir Schritt für Schritt einen Side-Scrolling-Shooter.
The techniques for creating parallax backgrounds, managing speed and movement, and generating enemies are widely used in games. After seeing them here, you should be able to use them in your own projects.
Basierend auf:
https://github.com/kantel/pygamezero/tree/master/tappyplane
Lizenz: Namensnennung-Nicht kommerziell-Weitergabe unter gleichen Bedingungen 4.0 International
Prerequisite: Knowledge in handling class.
Step 1: Create the basic framework#
Create a basic framework: You need a world where actors can be placed.
Deine letzte Zeile muss world.run()
sein.
from miniworlds import World, Actor, timer, Text
world = World(800, 480)
// dein Code hier
world.run()
Ordner vorbereiten#
You need to place images for backgrounds, players, and opponents in the images
directory within your code directory.
my_code
|
|--images
|----planered1.png
|----background.png
|----groundgrass.png
|----shipbeige.png
|----shipblue.png
|----shipgreen.png
|----shippink.png
|----shipyellow.png
(Die Bilder findest du in diesem Repository: miniworlds-cookbook - red baron)
Create backgrounds#
Mit dem folgenden Code kannst du zwei Hintergründe generieren, die einen endlos scrollenden Effekt erzeugen.
Create two backgrounds that fill the entire screen side by side:
back0 = Actor()
back0.add_costume("background")
back0.size = world.width, world.height
back1 = Actor(world.width, 0)
back1.size = world.width, world.height
back1.add_costume("background")
backs = [back0, back1]
Jetzt animieren wir die Hintergründe:
Both backgrounds move constantly from right to left.
When a background leaves the left edge of the screen, it is moved to the right.
@world.register
def act(self):
for back in backs:
back.x -= 1
if back.x <= -world.width:
back.x = world.width
for ground in grounds:
ground.x -= 2
if ground.x <= -world.width:
ground.x = world.width
This creates an endlessly scrolling background.
Schritt 2: Flugzeug-Klasse erstellen#
Create airplane class#
Create a Plane
class as a template for your player:
class Plane(Actor):
def on_setup(self):
self.add_costume("planered1")
Create instance of the airplane class#
Create an instance of this class at the end of your code, before world.run()
:
plane = Plane(100, world.height / 2)
Physik hinzufügen#
Jetzt fügen wir der Flugzeug-Klasse Physik hinzu. Modifiziere die on_setup()
-Methode der Klasse:
def on_setup(self):
self.add_costume("planered1")
self.gravity = 0.1
self.velocity_y = 0
velocity_y
describes the current speed of the aircraft in the y-direction.gravity
repräsentiert die Schwerkraft, die die Geschwindigkeit des Flugzeugs beeinflusst.
Physik simulieren#
The physics is simulated in the act()
method of the class:
def act(self):
self.velocity_y += self.gravity
self.velocity_y *= 0.9 # Reibung
self.y += self.velocity_y
This adds the speed to the y-coordinates of the aircraft. Gravity continuously decreases the speed, while friction smooths the movement.
Add force on key press#
Use the on_key_down
event to apply an upward force to the actor:
def on_key_down_w(self):
self.velocity_y -= 5
Step 3: Add opponent#
Importiere randint
und choice
, um zufällig Gegner zu generieren:
from random import randint, choice
Create Opponent Class#
Add an opponent class as a template:
class Enemy(Actor):
def on_setup(self):
self.add_costume(choice(enemyships))
def reset(self):
self.x = randint(world.width + 50, world.width + 500)
self.y = randint(25, world.height - 85)
The reset()
method randomly sets the opponent’s position within a specific range.
Add opponent to the world#
Create multiple instances of the enemy class with a loop and add them to the world:
enemies = []
for _ in range(10):
enemy = Enemy()
enemy.reset()
enemies.append(enemy)
Move opponent#
Modify the on_setup()
method of the enemy class:
def on_setup(self):
self.add_costume(choice(enemyships))
self.speed = -1.5
The speed
property specifies how many steps the opponent moves in the x-direction in each frame.
Add a act()
method to simulate the movement:
def act(self):
self.x += self.speed
if self.x <= -self.width:
self.reset()
Step 4: Add shooting#
Create a Bullet
class to add the shooting function:
class Bullet(Actor):
def on_setup(self):
self.add_costume("laserred")
self.x = plane.x
self.y = plane.y
self.speed = 25
self.fire = False
def act(self):
self.x += self.speed
def on_detecting_enemy(self, enemy):
enemy.reset()
def on_detecting_not_on_world(self):
self.remove()
With the methods on_detecting_enemy
and on_detecting_not_on_world
, bullets can detect enemies and be removed when leaving the world.
Vollständiger Code:#
from miniworlds import World, Actor, timer, Text
from random import randint, choice
# based on https://github.com/kantel/pygamezero/tree/master/tappyplane
class RedBaronWorld(World):
def on_setup(self):
self.size = (800, 480)
bottom_ground = self.height - 35
nr_enemies = 10
# Add backgrounds
back0 = Actor(origin="topleft")
back0.add_costume("background")
back0.size = self.width, self.height
back1 = Actor((self.width, 0), origin="topleft")
back1.size = self.width, self.height
back1.add_costume("background")
self.backs = [back0, back1]
ground0 = Actor((0, bottom_ground), origin="topleft")
ground0.add_costume("groundgrass")
ground0.width = self.width
ground0.costume.is_scaled = True
ground1 = Actor((self.width, bottom_ground), origin="topleft")
ground1.add_costume("groundgrass")
ground1.width = self.width
ground1.costume.is_scaled = True
self.grounds = [ground0, ground1]
self.ground_level = self.height - 85
self.plane = Plane((100, self.height / 2))
enemies = []
for _ in range(nr_enemies):
enemy = Enemy()
enemy.reset()
enemies.append(enemy)
def act(self):
for back in self.backs:
back.x -= 1
if back.x <= -self.width:
back.x = self.width
for ground in self.grounds:
ground.x -= 2
if ground.x <= -self.width:
ground.x = self.width
def on_key_down_space(self):
if not self.is_running:
self.reset()
self.run()
class Plane(Actor):
def on_setup(self):
self.add_costume("planered1")
self.gravity = 0.1
self.velocity_y = 0
self.fire = False
def act(self):
self.velocity_y += self.gravity
self.velocity_y *= 0.9 # friction
self.y += self.velocity_y
if self.y >= self.world.ground_level:
self.y = self.world.ground_level
self.velocity_y = 0
if self.y <= 20:
self.y = 20
self.velocity_y = 0
def on_key_down_w(self):
self.velocity_y -= 5
def on_key_down_d(self):
if not self.fire:
self.fire = True
bullet = Bullet()
@timer(frames=30)
def downtime():
self.fire = False
def on_detecting_enemy(self, other):
text = Text((self.world.width / 2, self.world.height / 2), "GAME OVER")
text.color = (0, 0, 0)
self.world.stop()
class Bullet(Actor):
def on_setup(self):
self.add_costume("laserred")
self.x = self.world.plane.x
self.y = self.world.plane.y
self.speed = 25
self.fire = False
def act(self):
self.x += self.speed
def on_detecting_enemy(self, enemy):
enemy.reset()
def on_not_detecting_world(self):
self.remove()
class Enemy(Actor):
enemy_ships = ["shipbeige", "shipblue", "shipgreen", "shippink", "shipyellow"]
def on_setup(self):
self.add_costume(choice(self.enemy_ships))
self.speed = -1.5
def reset(self):
self.x = randint(self.world.width + 50, self.world.width + 500)
self.y = randint(25, self.world.ground_level)
def act(self):
self.x += self.speed
if self.x <= -self.world.width:
self.reset()
level1 = RedBaronWorld()
level1.run()