Ein Neustart mit der Python Arcade Bibliothek?

Spieleprogrammierung
Python
Arcade
Autor:in

Jörg Kantel

Veröffentlichungsdatum

10. Februar 2025

Vor einigen Jahren hatte ich ja schon einmal mit der Python Arcade Bibliothek experimentiert. Damals (2021) waren meine Versuche eingeschlafen, weil das Teil für meinen Geschmack noch zu viele Glitches aufwies. Doch am Wochenende kam mir ein erstes Video einer geplanten Tutorialreihe unter, und das versprach, daß mit der brandneuen Version 3 von Arcade – erschienen vor zwei Wochen – die Kinderkrankheiten endlich überwunden sein sollten. Das machte mich natürlich neugierig.

Doch zuerst einmal zur Erinnerung: Was ist eigentlich Arcade? Arcade ist eine freie (MIT-Lizenz) Python-Bibliothek, die Paul Vincent Craven vor etwa sieben Jahren aus Frust über den damaligen Stillstand und die dadurch nie gefixten Bugs von PyGame entwickelt hatte. Außerdem sollte sie für Anfänger einfach zu lernen (ohne Pygames großen Boilerplate Overkill – das hat die Arcade-Bibliothek mit Pygame Zero gemeinsam), aber dennoch leistungsfähig sein. Arcade setzt auf Pyglet (und damit auf OpenGL) auf und kann mit der Physik-Engine Pymunk verkuppelt werden.

In der aktuellen Version 3 von Arcade wurde die Bibliothek vollständig überarbeitet (was sich auch an einem neuen GitHub-Repositorium und einer neu erstellten Dokumentation festmachen lässt). Daher liefen meine alten Versuche mit der neuen Version der Bibliothek nicht mehr, ich mußte also komplett neu beginnen.

Die Neuaufstellung hat aber der Arcade-Bibliothek gut getan, sie wurde einerseits schlanker und andererseits leistungsfähiger und offener. Ein minimales, aber dennoch lauffähiges Template kann zum Beispiel nun so aussehen:

import arcade

class GameWorld(arcade.Window):

    def __init__(self):
        super().__init__(640, 480, "Arcade Herdplatte")
        self.background_color = (128, 128, 128, 255)

    def setup(self):
        pass

    def on_draw(self):
        self.clear()

def main():
    window = GameWorld()
    window.setup()
    arcade.run()

if __name__ == "__main__":
    main()

Das ist ein vollständiges Arcade-Programm und öffnet ein Fenster mit einem grauen Hintergrund. Es ist zwar noch nicht ganz der Minimalismus von Pygame Zero, aber gegenüber dem Overhead, den Pygame verlangt, doch schon eine Vereinfachung. Und nach einer ersten Durchsicht der Arcade-Dokumentation ist die Bibliothek bedeutend leistungsfähiger als Pygame Zero. Und Pygame Zero hat seit 2018 kein echtes Update mehr erfahren, während die Python Arcade Bbliothek aktuell 149 Kontributoren besitzt, die an der Weiterentwicklung arbeiten.

Das hat mich natürlich angefixt und ich mußte – in Anlehnung an das »offizielle« Beginner-Tutorial – mit Arcade spielen. Meine (noch nicht fertige – ich bin gestern bis Step 6 gekommen) Version sieht so aus (siehe Screenshot im Bannerbild oben):

"""
Simple Platformer Game with Arcade
"""
import arcade

# Constants
WINDOW_WIDTH = 840
WINDOW_HEIGHT = 360
WINDOW_TITLE = "Erste Schritte mit Arcade 3.0"

TILE_SCALING = 0.5
TILE_SIZE = 32

PLAYER_SPEED = 5
GRAVITY = 1
PLAYER_JUMP = 20

class GameWorld(arcade.Window):
    """
    Main application class.
    """

    def __init__(self):

        # Call the parent class to set up the window
        super().__init__(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
        # Position of the window
        self.set_location(1980, 80)
        
        # Variable to hold our texture for our player
        self.player_texture = None
        
        # Separate variable that holds the player sprite
        self.player_sprite = None
        
        # SpriteList for our player
        self.player_list = None
        
        # SpriteList for our boxes and ground
        self.wall_list = None

    def setup(self):
        """Set up the game here. Call this function to restart the game."""
        
        self.player_texture = arcade.load_texture("data/gameboy_idle.png")
        
        self.player_sprite = arcade.Sprite(self.player_texture, scale = 0.65)
        self.player_sprite.center_x = 64
        self.player_sprite.center_y = WINDOW_HEIGHT + 100
        
        # SpriteList for our player
        self.player_list = arcade.SpriteList()
        self.player_list.append(self.player_sprite)
        
        self.wall_list = arcade.SpriteList(use_spatial_hash = True)
        
        # Create the ground
        # This shows using a loop to place multiple sprites horizontally
        for x in range(0, WINDOW_WIDTH, TILE_SIZE):
            wall = arcade.Sprite("data/grass001.png", scale = TILE_SCALING)
            wall.center_x = x
            wall.center_y = TILE_SIZE/2
            self.wall_list.append(wall)
        
        # Put some crates on the ground
        # This shows using a coordinate list to place sprites
        coordinate_list = [[480, 48], [240, 48], [720, 48]]
        
        for coordinate in coordinate_list:
            # Add a crate on the ground
            wall = arcade.Sprite("data/box01.png", scale = TILE_SCALING)
            wall.position = coordinate
            self.wall_list.append(wall)
        
        # Create a Platformer Physics Engine.
        # This will handle moving our player as well as collisions between
        # the player sprite and whatever SpriteList we specify for the walls.
        self.physics_engine = arcade.PhysicsEnginePlatformer(
            self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
        )

        self.background_color = (128, 128, 128, 255)

    def on_draw(self):
        """Render the screen."""

        # The clear method should always be called at the start of on_draw.
        # It clears the whole screen to whatever the background color is
        # set to. This ensures that you have a clean slate for drawing each
        # frame of the game.
        self.clear()

        # Code to draw other things will go here
        self.wall_list.draw()
        self.player_list.draw()
    
    def on_update(self, delta_time):
        """Movement and Game Logic"""

        # Move the player using our physics engine
        self.physics_engine.update()
    
    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed."""
        
        if key == arcade.key.ESCAPE:
            self.setup()
        
        if key == arcade.key.UP or key == arcade.key.W:
            if self.physics_engine.can_jump():
                self.player_sprite.change_y = PLAYER_JUMP

        if key == arcade.key.LEFT or key == arcade.key.A:
            self.player_sprite.change_x = -PLAYER_SPEED
        elif key == arcade.key.RIGHT or key == arcade.key.D:
            self.player_sprite.change_x = PLAYER_SPEED

    def on_key_release(self, key, modifiers):
        """Called whenever a key is released."""
        
        if key == arcade.key.LEFT or key == arcade.key.A:
            self.player_sprite.change_x = 0
        elif key == arcade.key.RIGHT or key == arcade.key.D:
            self.player_sprite.change_x = 0

def main():
    """Main function"""
    window = GameWorld()
    window.setup()
    arcade.run()

if __name__ == "__main__":
    main()

Bei aller Lobhudelei habe ich zwei Dinge zu bemängeln. Erstens: Mein Thonny gibt eine seltsame und für mich unverständliche Meldung bei jedem Start eines Arcade-Skripts aus:

2025-02-10 17:05:47.825 Python[3168:94568] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/bz/7v6zs8_x2rgb97_7tfb77x8h0000gp/T/org.python.python.savedState

Das scheint zwar den Programmablauf nicht zu beeinflussen (und die Meldung klingt auch eigentlich harmlos), aber ich würde trotzdem gerne wissen, was sie bedeutet.

Der zweite Einwand ist gewichtiger: Im Gegensatz zu Pygame scheint es keine Möglichkeit zu geben, Arcade-Skripte webtauglich zu machem so daß die damit erstellten Spiele auch im Browser laufen. Zwar verspricht die FAQ

For the moment, the Arcade and pyglet teams are eagerly watching ongoing developments in WebGPU and its WASM integrations

aber in absehbarer Zeit rechne ich hier mit keinem Fortschritt.

Dennoch, im Gegensatz zu meinen früheren Versuchen macht das Entwickeln mit Arcade mir mittlerweile richtig Spaß. Daher könnt Ihr mit Sicherheit in der nächsten Zeit weitere Arcade-Experimente in diesem Blog Kritzelheft erwarten. Still digging!