📷 Kamera & Views
Kamera-Konzepte
Eine Kamera definiert, welcher Teil der Spielwelt auf dem Bildschirm sichtbar ist. Arcade trennt dabei zwischen World Space (Spielweltkoordinaten) und Screen Space (Bildschirmkoordinaten).
🌍 World Space
Die Koordinaten der Spielwelt – kann viel größer als der Bildschirm sein. Hier leben alle Sprites und Objekte.
🖥️ Screen Space
Die tatsächlichen Pixel auf dem Bildschirm. Die Kamera transformiert World Space in Screen Space.
📐 View Matrix
Kontrolliert Position und Rotation der Kamera in der Welt.
📏 Projection Matrix
Bestimmt den sichtbaren Bereich (Orthographisch oder Perspektivisch).
Camera2D
Camera2D ist die einfachste und empfohlene Kamera-Klasse für 2D-Spiele.
def setup(self):
# Kamera für die Spielwelt
self.camera = arcade.Camera2D()
# Kamera für die HUD (fixiert am Bildschirm)
self.hud_camera = arcade.Camera2D()
def on_draw(self):
self.clear()
# Spielwelt mit scrollender Kamera zeichnen
with self.camera.activate():
self.wall_list.draw()
self.player_list.draw()
self.coin_list.draw()
# HUD mit fixer Kamera zeichnen
with self.hud_camera.activate():
arcade.draw_text(
f"Score: {self.score}",
10, 580,
arcade.color.WHITE, 20
)
arcade.draw_text(
f"Leben: {self.lives}",
700, 580,
arcade.color.WHITE, 20
)
Gelber Rahmen = Kamera-Viewport. Rechtsoben: Minimap mit Kamera-Bereich (gelb).
Kamera-Scrolling
Die Kamera folgt dem Spieler – aber mit sanfter Interpolation für ein professionelles Gefühl.
def center_camera_on_player(self):
# Zielposition der Kamera = Spieler-Mittelpunkt
target_x = self.player.center_x
target_y = self.player.center_y
# Kamera an Weltgrenzen begrenzen
target_x = max(target_x, self.width / 2)
target_x = min(target_x, self.level_width - self.width / 2)
target_y = max(target_y, self.height / 2)
target_y = min(target_y, self.level_height - self.height / 2)
# Direkte Positionierung
self.camera.position = (target_x, target_y)
def center_camera_smooth(self, delta_time):
# Sanfte Interpolation (lerp) zur Zielposition
SMOOTHING = 0.1 # 0.0 = sehr träge, 1.0 = sofort
current_x, current_y = self.camera.position
target_x = self.player.center_x
target_y = self.player.center_y
# Lineare Interpolation
new_x = current_x + (target_x - current_x) * SMOOTHING
new_y = current_y + (target_y - current_y) * SMOOTHING
self.camera.position = (new_x, new_y)
# Kamera schütteln (nach Explosion etc.)
def shake_camera(self, intensity=5, duration=0.3):
self.shake_intensity = intensity
self.shake_duration = duration
self.shake_timer = 0
def update_camera_shake(self, delta_time):
if self.shake_timer < self.shake_duration:
self.shake_timer += delta_time
offset_x = random.uniform(-self.shake_intensity, self.shake_intensity)
offset_y = random.uniform(-self.shake_intensity, self.shake_intensity)
self.camera.position = (
self.player.center_x + offset_x,
self.player.center_y + offset_y
)
Views – Spielzustand verwalten
Views trennen verschiedene Spielzustände: Hauptmenü, Spielschleife, Pause, Game Over. Jeder View ist eine eigene Klasse mit eigenen draw/update-Methoden.
import arcade
class GameView(arcade.View):
"""Haupt-Spielansicht."""
def __init__(self):
super().__init__()
self.player = None
self.score = 0
def setup(self):
self.player = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png", 0.5)
self.player.center_x = 400
self.player.center_y = 300
def on_draw(self):
self.clear()
arcade.draw_text(f"Score: {self.score}", 10, 580, arcade.color.WHITE, 20)
self.player.draw()
def on_key_press(self, key, modifiers):
if key == arcade.key.ESCAPE:
# Zu Pause-View wechseln
pause_view = PauseView(self)
self.window.show_view(pause_view)
class PauseView(arcade.View):
"""Pause-Menü."""
def __init__(self, game_view):
super().__init__()
self.game_view = game_view # Referenz zum Game-View
def on_draw(self):
self.clear()
arcade.draw_text("PAUSE", 400, 350, arcade.color.WHITE, 50, anchor_x="center")
arcade.draw_text("ESC = Weiterspielen", 400, 280, arcade.color.GRAY, 20, anchor_x="center")
def on_key_press(self, key, modifiers):
if key == arcade.key.ESCAPE:
# Zurück zum Spiel
self.window.show_view(self.game_view)
# App starten
window = arcade.Window(800, 600, "Mein Spiel")
game_view = GameView()
game_view.setup()
window.show_view(game_view)
arcade.run()
Sections – Bildschirmbereiche
Mit Sections kann man einen View in unabhängige Bereiche aufteilen – z.B. Spielbereich + Sidebar oder Split-Screen-Multiplayer.
class GameSection(arcade.Section):
"""Haupt-Spielbereich (links)."""
def on_draw(self):
# Wird nur im definierten Bereich gezeichnet
arcade.draw_lbwh_rectangle_filled(
self.left, self.bottom,
self.width, self.height,
arcade.color.DARK_BLUE_GRAY
)
def on_mouse_press(self, x, y, button, modifiers):
# x, y sind relativ zum Section-Ursprung
print(f"Klick in GameSection: {x}, {y}")
class UISection(arcade.Section):
"""UI-Sidebar (rechts)."""
def on_draw(self):
arcade.draw_lbwh_rectangle_filled(
self.left, self.bottom,
self.width, self.height,
arcade.color.DARK_GRAY
)
class MyView(arcade.View):
def __init__(self):
super().__init__()
# Section: left, bottom, width, height
self.add_section(GameSection(0, 0, 600, 600))
self.add_section(UISection(600, 0, 200, 600))