🔬 Advanced Topics
Tilemaps mit Tiled Map Editor
Arcade unterstützt das TMX-Format des Tiled Map Editors. Level können grafisch erstellt und direkt in Arcade geladen werden.
def setup(self):
# Tilemap laden
layer_options = {
"Platforms": {"use_spatial_hash": True},
"Moving Platforms": {"use_spatial_hash": False},
"Coins": {"use_spatial_hash": True},
"Enemies": {"use_spatial_hash": False},
}
self.tile_map = arcade.load_tilemap(
map_file="assets/maps/level_01.tmx",
scaling=0.5,
layer_options=layer_options
)
# Scene aus der Tilemap erstellen
self.scene = arcade.Scene.from_tilemap(self.tile_map)
# Hintergrundfarbe aus Tiled übernehmen
if self.tile_map.background_color:
arcade.set_background_color(self.tile_map.background_color)
# Weltgrenzen berechnen
self.level_width = (
self.tile_map.width * self.tile_map.tile_width * 0.5
)
# Physik-Engine mit Tilemap-Layers
self.physics_engine = arcade.PhysicsEnginePlatformer(
player_sprite=self.player,
platforms=self.scene["Platforms"],
gravity_constant=1.0,
ladders=self.scene["Ladders"]
)
def on_draw(self):
self.clear()
with self.camera.activate():
self.scene.draw() # Alle Layer zeichnen
Pathfinding mit A*
Arcade hat eingebautes A*-Pathfinding. Gegner können damit den kürzesten Weg zum Spieler finden, Hindernisse umgehen inklusive.
def setup(self):
# Hindernis-Liste für Pathfinding
self.barrier_list = arcade.AStarBarrierList(
moving_sprite=self.enemy,
blocking_sprites=self.wall_list,
grid_size=32, # Gittergröße
left=0, right=1600, # Weltgrenzen
bottom=0, top=1600
)
def on_update(self, delta_time):
# Pfad zum Spieler berechnen
path = arcade.astar_calculate_path(
start_point=self.enemy.position,
end_point=self.player.position,
astar_barrier_list=self.barrier_list,
diagonal_movement=False # Keine Diagonalen
)
if path and len(path) > 1:
# Nächsten Schritt im Pfad ansteuern
next_x, next_y = path[1]
dx = next_x - self.enemy.center_x
dy = next_y - self.enemy.center_y
speed = 2
distance = ((dx**2) + (dy**2))**0.5
if distance > 0:
self.enemy.change_x = dx / distance * speed
self.enemy.change_y = dy / distance * speed
# Sichtlinie prüfen (für KI)
can_see = arcade.has_line_of_sight(
point_1=self.enemy.position,
point_2=self.player.position,
walls=self.wall_list
)
Performance-Optimierung
⚡ Spatial Hashing
use_spatial_hash=True für statische Wände/Plattformen. Macht Kollisionsabfragen O(1) statt O(n).
📦 ShapeElementList
Für statische Hintergrundelemente – alles in einem GPU Draw Call statt einzelner draw_*()-Aufrufe.
🎯 Texture Atlas
Alle Sprites in einer Textur zusammenführen. Arcade macht das automatisch, aber Customisierung möglich.
📊 FPS Monitoring
arcade.get_fps() und arcade.PerfGraph für Echtzeit-Performance-Analyse.
# FPS anzeigen
def on_draw(self):
self.clear()
self.scene.draw()
# FPS Counter
fps = arcade.get_fps()
arcade.draw_text(f"FPS: {fps:.0f}", 10, 10, arcade.color.WHITE, 14)
# Performance-Timing aktivieren
arcade.enable_timings()
def on_update(self, delta_time):
# Timings ausgeben
if self.frame_count % 60 == 0: # Jede Sekunde
arcade.print_timings()
# Sprites außerhalb des Bildschirms nicht updaten
def on_update(self, delta_time):
for enemy in self.enemy_list:
# Nur nahe Gegner updaten
dist = arcade.get_distance_between_sprites(enemy, self.player)
if dist < 1000:
enemy.update()
# Lazy SpriteLists (nur wenn benötigt geladen)
lazy_list = arcade.SpriteList(lazy=True) # Initialisierung verzögern
Shader-Grundlagen
Arcade bietet direkten OpenGL-Zugriff für Post-Processing-Effekte wie Glow, CRT-Filter, Lichteffekte und mehr.
from arcade.experimental import LightLayer, Light
def setup(self):
# Licht-Layer erstellen
self.light_layer = LightLayer(800, 600)
self.light_layer.set_background_color(arcade.color.BLACK)
# Licht beim Spieler
self.player_light = Light(
400, 300, # Position
radius=200, # Leuchtradius
color=arcade.csscolor.WHITE,
mode="soft" # "hard" oder "soft"
)
self.light_layer.add(self.player_light)
# Fackel-Licht
torch_light = Light(200, 100, radius=100,
color=(255, 100, 0), mode="soft")
self.light_layer.add(torch_light)
def on_update(self, delta_time):
# Licht folgt dem Spieler
self.player_light.position = self.player.position
def on_draw(self):
self.clear()
with self.light_layer:
# Alles in diesem Block wird beleuchtet
self.scene.draw()
# Licht-Layer rendern
self.light_layer.draw()
Spiel verteilen: PyInstaller
Mit PyInstaller kann ein Arcade-Spiel in eine eigenständige .exe (Windows) oder App (Mac/Linux) verpackt werden.
# PyInstaller installieren
pip install pyinstaller
# Einfaches Bundling (eine .exe)
pyinstaller --onefile --windowed main.py
# Mit Asset-Dateien
pyinstaller --onefile --windowed \
--add-data "assets;assets" \
--add-data "maps;maps" \
main.py
# Windows: Eigenes Icon
pyinstaller --onefile --windowed --icon=icon.ico main.py
import sys
import os
def resource_path(relative_path):
"""Korrekte Pfade für PyInstaller-Bundle und Entwicklung."""
if hasattr(sys, '_MEIPASS'):
# Im Bundle: Temp-Verzeichnis
base_path = sys._MEIPASS
else:
# In Entwicklung: Projektverzeichnis
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
# Verwendung:
sprite = arcade.Sprite(resource_path("assets/player.png"))
Scenes
arcade.Scene ist ein organisiertes Container-Objekt für mehrere SpriteLists – ideal für Level mit Tilemaps.
def setup(self):
# Manuelle Scene erstellen
self.scene = arcade.Scene()
# Layer hinzufügen (Reihenfolge = Zeichenreihenfolge)
self.scene.add_sprite_list("Background")
self.scene.add_sprite_list("Platforms", use_spatial_hash=True)
self.scene.add_sprite_list("Coins")
self.scene.add_sprite_list("Enemies")
self.scene.add_sprite_list("Player")
# Sprites zu Layer hinzufügen
self.scene.add_sprite("Player", self.player)
self.scene.add_sprite("Coins", coin)
# Layer abrufen
coin_layer = self.scene["Coins"]
platform_layer = self.scene.get_sprite_list("Platforms")
def on_draw(self):
self.clear()
# Alle Layer in richtiger Reihenfolge zeichnen
self.scene.draw()
# Oder einzelne Layer:
self.scene.draw("Background")
self.scene.draw("Platforms")