(十四)从暂停与重开得到游戏状态
1. 不同画面共享一个场景状态
开始画面要等待操作,进行中才移动和生成,暂停要保留本局,结束则展示分数。用四个状态表达这些差异,比为“已经开始”“暂停了”“撞到了”分别维护多个布尔量更容易跟踪。
| 状态 | 继续响应的操作 | 是否推进游戏运动 |
|---|---|---|
| TITLE | 开始、声音、退出 | 否 |
| PLAYING | 移动、暂停、声音、退出 | 是 |
| PAUSED | 恢复、声音、退出 | 否 |
| GAME_OVER | 重开、声音、退出 | 否 |
2. 先确定单位与数据,再连接操作
完整游戏使用宽 800、高 600 的窗口;顶部留给标题和分数,玩家在游戏区域底部左右移动:
# 项目文件:dodge_project/src/dodgegame/config.py
# 距离用像素,速度用像素/秒;游戏区域位于标题栏下方。
WIDTH, HEIGHT = 800, 600
LEFT, RIGHT = 32, 768
TOP, BOTTOM = 108, 568
PLAYER_W, PLAYER_H = 48, 36
PLAYER_Y = 508
PLAYER_SPEED = 320.0
SPAWN_INTERVAL = 0.75
FIXED_STEP = 1 / 120模型中用 Enum 为四个状态取明确名称:Mode.PLAYING 表示进行中,.name 可以显示 PLAYING。它把场景状态集中到一组名字中,避免字符串拼写散落在不同分支。
# 项目文件:dodge_project/src/dodgegame/models.py
# 状态和坐标不依赖窗口:可以单独计算,也能在测试里准确复盘。
from dataclasses import dataclass
from enum import Enum
import math
import random
from .config import (
BOTTOM,
LEFT,
PLAYER_H,
PLAYER_SPEED,
PLAYER_W,
PLAYER_Y,
RIGHT,
SPAWN_INTERVAL,
TOP,
)
from .geometry import collision_time
class Mode(Enum):
TITLE = "title"
PLAYING = "playing"
PAUSED = "paused"
GAME_OVER = "game_over"
@dataclass
class Player:
x: float = (LEFT + RIGHT - PLAYER_W) / 2
y: float = PLAYER_Y
width: int = PLAYER_W
height: int = PLAYER_H
def box(self):
return self.x, self.y, self.width, self.height
@dataclass
class Obstacle:
uid: int
x: float
y: float
width: int
height: int
speed: float
def box(self):
return self.x, self.y, self.width, self.heightPlayer 和 Obstacle 继续使用第四章的数据类。box 方法交回四个浮点几何字段;障碍额外保存 uid 和下落速度,用于连接显示对象与本次碰撞。
3. 重置本局与保留最高分
# 续写同一文件:dodge_project/src/dodgegame/models.py
# 每轮重开都重置障碍、计分和计时;历史最高分仍由同一个 World 对象保留。
class World:
def __init__(self, seed=None, best=0):
self.seed = seed
self.best = best
self.mode = Mode.TITLE
self.reset_round()
def reset_round(self):
self.rng = random.Random(self.seed)
self.player = Player()
self.obstacles = []
self.score = 0
self.elapsed = 0.0
self.spawn_elapsed = 0.0
self.next_uid = 1
self.hit_uid = None
def start(self):
self.reset_round()
self.mode = Mode.PLAYING
def toggle_pause(self):
if self.mode == Mode.PLAYING:
self.mode = Mode.PAUSED
elif self.mode == Mode.PAUSED:
self.mode = Mode.PLAYINGreset_round 清空障碍、分数、累计时间与碰撞记录;best 留在 World 中。start 调用重置后进入 PLAYING;toggle_pause 只在 PLAYING 与 PAUSED 之间切换,不重建当前玩家和障碍。
Random 是本局自己的随机数发生器。给定相同 seed 会重新得到同样的障碍序列,方便复盘;默认没有指定 seed 时,每局重新取得随机序列。