Home › Kategorie 06

📷 Kamera & Views

INTERMEDIATE ⏱ ~35 Min

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.

PYTHON – Camera2D
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
        )
💡 Nutze zwei Kameras: Eine für die scrollende Spielwelt, eine für das HUD (Score, Leben, Minimap). Das HUD bleibt immer an derselben Bildschirmposition.
Kamera-Follow Simulator – mit Minimap
Smoothing: 0.10
Speed: 1.5

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.

PYTHON – Kamera folgt Spieler
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.

PYTHON – View-System
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.

PYTHON – Section
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))