Scenes, Audio e Tilemaps

Curso de Desenvolvimento de Jogos com Pygame

Felipe A. Moreno-Vera

Nota

Nestes módulos você vai aprender:

  • Scene management, game state machines e transitions
  • Música de fundo, sound effects, channels e spatial audio
  • Tile maps, sistemas de camera, scrolling e fundos com parallax

Scenes & Game States

O problema: o que é um “game state”?

Um jogo nunca está em apenas um modo. Ele alterna entre states:

┌─────────┐   ENTER    ┌──────────┐   PAUSE    ┌─────────┐
│  MENU   │ ─────────► │   GAME   │ ─────────► │  PAUSE  │
└─────────┘            └──────────┘            └─────────┘
                            │                       │ RESUME
                          DEAD                      ▼
                            │                  ┌──────────┐
                            └────────────────► │ GAME     │
                                               └──────────┘
                                                    │ 0 lives
                                                    ▼
                                               ┌──────────┐
                                               │ GAME OVER│
                                               └──────────┘

Sem um state system adequado, você acaba com uma selva de checks if game_state == "menu" espalhados por todo lado. O Scene pattern resolve isso de forma limpa.

The Scene Base Class

Cada tela do jogo é uma Scene. Cada scene sabe como:

class Scene:
    def enter(self):        # Called once when scene becomes active
        pass

    def exit(self):         # Called once when scene is deactivated
        pass

    def handle_event(self, event):   # Process pygame events
        pass

    def update(self, dt):   # Update logic; dt = delta time in seconds
        pass

    def draw(self, surface): # Render to the screen
        pass

Essa interface obriga você a manter a lógica de cada scene self-contained. Uma MenuScene nunca toca nas variáveis da GameScene.

The Scene Manager

O Scene Manager possui uma stack de scenes. A scene que está no topo da stack é a que é executada.

class SceneManager:
    def __init__(self):
        self._stack = []

    def push(self, scene):
        """Add a scene on top — previous scene stays in memory."""
        if self._stack:
            self._stack[-1].exit()
        self._stack.append(scene)
        scene.enter()

    def pop(self):
        """Remove the top scene — reveals the scene below."""
        self._stack.pop().exit()
        if self._stack:
            self._stack[-1].enter()

    def replace(self, scene):
        """Swap the current scene — no going back."""
        if self._stack:
            self._stack.pop().exit()
        self._stack.append(scene)
        scene.enter()

Push vs Replace vs Pop

Method Efeito na stack Caso de uso
push(PauseScene) [Menu, Game, Pause] Overlay — o jogo permanece em memória
pop() [Menu, Game] Resume — volta para a scene anterior
replace(GameOver) [Menu, GameOver] Transição completa — sem volta

Push vs Replace vs Pop

=== PUSH Menu ===
  ► MenuScene.enter()
  Stack: ['MenuScene']

=== PUSH Game ===
  ◄ MenuScene.exit()
  ► GameScene.enter()
  Stack: ['MenuScene', 'GameScene']

=== PUSH Pause (overlay) ===
  ◄ GameScene.exit()
  ► PauseScene.enter()
  Stack: ['MenuScene', 'GameScene', 'PauseScene']

=== POP (resume) ===
  ◄ PauseScene.exit()
  ► GameScene.enter()
  Stack: ['MenuScene', 'GameScene']

=== REPLACE with GameOver ===
  ◄ GameScene.exit()
  ► GameOverScene.enter()
  Stack: ['MenuScene', 'GameOverScene']

Compartilhando dados entre scenes

As scenes são decoupled — elas não mantêm referências entre si. Use o shared dict do manager para passar dados:

# In GameScene — store the score when dying
self.manager.shared['score'] = self.score
self.manager.replace(GameOverScene(self.manager))

# In GameOverScene — read it back
self.score = self.manager.shared.get('score', 0)

Scene Transitions

Uma transition é um efeito visual que é reproduzido enquanto as scenes são trocadas. A ideia principal: renderizar ambas as scenes em surfaces e depois fazer blend entre elas.

class TransitionEngine:
    def start(self, surf_from, surf_to, mode="fade", duration=0.5):
        self.surf_from = surf_from.copy()
        self.surf_to   = surf_to.copy()
        self.progress  = 0.0
        self.active    = True

    def update(self, dt):
        self.progress += dt / self.duration
        if self.progress >= 1.0:
            self.active = False

    def draw(self, surface):
        p = self.progress * self.progress * (3 - 2 * self.progress)  # Ease

        if self.mode == "fade":
            if p < 0.5:
                alpha = int(255 * (1 - p * 2))
                self.surf_from.set_alpha(alpha)
                surface.blit(self.surf_from, (0, 0))
            else:
                alpha = int(255 * ((p - 0.5) * 2))
                self.surf_to.set_alpha(alpha)
                surface.blit(self.surf_to, (0, 0))

        elif self.mode == "slide_left":
            offset = int((1 - p) * WIDTH)
            surface.blit(self.surf_from, (-offset, 0))
            surface.blit(self.surf_to, (WIDTH - offset, 0))

Scene Transitions

t        linear   ease_in  ease_out  ease_inout
----------------------------------------------------
  0.0     0.00    0.00     0.00      0.00
  0.1     0.10    0.01     0.19      0.03
  0.2     0.20    0.04     0.36      0.10
  0.3     0.30    0.09     0.51      0.22
  0.4     0.40    0.16     0.64      0.35
  0.5     0.50    0.25     0.75      0.50
  0.6     0.60    0.36     0.84      0.65
  0.7     0.70    0.49     0.91      0.78
  0.8     0.80    0.64     0.96      0.90
  0.9     0.90    0.81     0.99      0.97
  1.0     1.00    1.00     1.00      1.00

Scene transitions — Resumo

  • A base class Scene obriga a implementar enter / exit / handle_event / update / draw
  • SceneManager possui uma stack: push sobrepõe, pop retorna, replace substitui
  • Compartilhe dados entre scenes através do manager.shared dict
  • As transitions pré-renderizam ambas as scenes em surfaces, e depois fazem blend entre elas
  • As easing functions (ease_inout) fazem as transitions parecerem smooth