Python 现代开发学习笔记

(十六)把移动、碰撞、计分按次序推进

1. 同一个逻辑步中的完整动作

继续放入 World.step。输入是本步秒数和横向方向,返回这一步实际产生的动作消息:

# 续写同一文件:dodge_project/src/dodgegame/models.py
    def step(self, dt, axis):
        if not math.isfinite(dt) or dt < 0 or not math.isfinite(axis):
            raise ValueError("时间与输入方向应为有限数,时间不能为负")
        if self.mode != Mode.PLAYING:
            return []
        old_player = self.player.box()
        target_x = self.player.x + max(-1, min(1, axis)) * PLAYER_SPEED * dt
        target_x = max(LEFT, min(RIGHT - PLAYER_W, target_x))
        dx = target_x - self.player.x
        hits = []
        for obstacle in self.obstacles:
            # 障碍向下移动,等价于在它的旧位置观察玩家相对向上移动。
            when = collision_time(old_player, obstacle.box(), dx, -obstacle.speed * dt)
            if when is not None:
                hits.append((when, obstacle.uid))
        if hits:
            when, self.hit_uid = min(hits)
            self.player.x += dx * when
            for obstacle in self.obstacles:
                obstacle.y += obstacle.speed * dt * when
            self.elapsed += dt * when
            self.mode = Mode.GAME_OVER
            return ["hit"]
        self.player.x = target_x
        for obstacle in self.obstacles:
            obstacle.y += obstacle.speed * dt
        # 先检查碰撞,再判断离开;撞上的障碍不能因为同一步已离屏而加分。
        passed = [obstacle for obstacle in self.obstacles if obstacle.y >= BOTTOM]
        self.obstacles = [
            obstacle for obstacle in self.obstacles if obstacle.y < BOTTOM
        ]
        self.score += len(passed)
        self.best = max(self.best, self.score)
        self.elapsed += dt
        self.spawn_elapsed += dt
        while self.spawn_elapsed + 1e-12 >= SPAWN_INTERVAL:
            self.spawn_elapsed = max(0.0, self.spawn_elapsed - SPAWN_INTERVAL)
            self.spawn()
        return ["score"] * len(passed)

先取得玩家旧位置,计算目标位置,再限制在游戏区域;最终 dx 用限制后的新旧位置相减。对每个障碍,玩家相对位移是 (dx, −下落距离),交给前面已经推导的 collision_time。

所有障碍的候选接触比例一起比较,选择本步最早的一次。发生碰撞时,玩家和全部障碍都只走到同一个比例,记录 hit_uid 并进入 GAME_OVER。这样画面中的其他障碍也不会在结束后又推进一整步。

本步从旧位置算目标,比较全部接触时间,最早接触使所有对象同步停下并进入结束

2. 未发生碰撞时再结算离屏分数

没有碰撞就完成整步移动,然后找出顶部已经到 BOTTOM 的障碍。它们完整离开游戏区域,每块加 1 分,再从列表移除。下一步它们已经不在列表中,不会再次加分。

最后更新时间与生成累计器。顺序为“碰撞 → 离屏计分 → 生成下一批”:一块快速跨过玩家又离屏的障碍,会先进入碰撞分支结束本局,不会同时被算作躲避成功。

一块障碍完整离屏只加一次分并移除,穿过玩家的高速障碍则先进入结束分支,不再结算通过

笔记正文与全部题解 · Ctrl / ⌘ K