Phaser 横版跑酷关卡节奏:障碍密度、镜头预告和失败反馈要一起设计

用 Phaser 构建横版跑酷时,如何把障碍生成、速度曲线、镜头预告、碰撞容错和复盘数据组合成可调的关卡节奏系统。

跑酷不是无限往右移动

横版跑酷看起来是最容易用 Phaser 做的类型:玩家固定在屏幕左侧,地面向左滚动,障碍从右边生成,按一下跳跃。真正难的是节奏。前 30 秒太简单,玩家觉得无聊;第 45 秒突然塞三个坑,玩家觉得被设计师偷袭;速度越来越快但镜头没有提前量,玩家来不及反应;失败后只弹一个重新开始,没人知道自己是跳早、跳晚还是被碰撞盒冤枉。跑酷关卡的质量,不是靠障碍数量堆出来的,而是由“信息出现、玩家决策、动作执行、反馈复盘”这条链路决定。

Phaser 里可以很快搭出一个 endless runner,但如果想让它可运营、可扩展,就不能把障碍生成写成 Math.random() < 0.02。你需要一套节奏系统:速度曲线决定压力上升,段落模板决定障碍组合,安全间距保证动作可完成,镜头和预告保证玩家能看见,失败日志帮助你修正关卡。本文会按真实项目拆解,目标不是做一个演示,而是做一个可以调参、可以复盘、可以交给策划配置的跑酷骨架。

用段落而不是随机数管理节奏

跑酷关卡可以无限,但节奏不应该完全随机。一个实用做法是把内容拆成 8 到 15 秒的段落,每个段落有主题:热身、单跳、连续跳、滑铲、空中收集、窄平台、组合考验。段落内部可以有少量随机,比如金币位置、装饰物、同类障碍变体,但核心动作链应该可控。这样玩家会感到变化,同时设计师能保证难度曲线。

段落模板可以包含长度、推荐速度范围、障碍列表、最小反应时间和复用冷却。生成器每次选择下一个段落时,先看当前速度和玩家表现,再从候选池中挑选。玩家刚连续失败时,可以降低组合密度;玩家连续无伤时,可以提高奖励线或插入高风险路线。注意,这种动态调整不要太明显,否则玩家会觉得游戏在作弊。它应该像关卡导演一样微调节奏,而不是临时移动障碍。

flowchart LR
  A["RunState:速度、距离、失败次数"] --> B["SegmentSelector:选择段落模板"]
  B --> C["ConstraintChecker:安全间距与动作可达性"]
  C --> D["Spawner:生成地形、障碍、奖励"]
  D --> E["CameraCue:镜头提前量与警示标记"]
  E --> F["PlayerAction:跳跃、滑铲、二段跳"]
  F --> G["FailureLog:记录死亡位置和原因"]
  G --> B

速度曲线要服务动作窗口

很多跑酷项目的速度曲线只有一个公式:每 10 秒加一点速度。这个公式不一定错,但它必须和动作窗口绑定。玩家跳跃有起跳延迟、上升时间、滞空时间、落地恢复;障碍之间的距离必须允许这些动作完成。如果速度提高了,同样的世界距离对应更短反应时间,原本合理的双坑可能突然变成必死组合。因此速度不能只看距离,还要看当前段落动作需求。

可以把速度分成三层。基础速度由距离决定,让游戏有长期压力;段落速度修正由模板决定,比如教学段略慢、奖励段略快;临时速度修正由状态决定,比如受伤后 1 秒内略微放缓,但不能影响判定公平。最终速度用于地面、障碍和背景视差,但玩家物理参数未必跟着同比例变化。跑酷的“快感”很多时候来自画面和信息密度,而不是把重力也调到离谱。

反应时间比像素距离更重要

设计障碍间距时,不要只说“两个坑间隔 180 像素”。在不同速度下,180 像素可能是 450ms,也可能是 250ms。更好的配置单位是毫秒或动作窗口。比如一个基础跳跃需要 120ms 反应、80ms 起跳缓冲、480ms 滞空、100ms 落地恢复,那么连续跳之间至少要留出这些时间。生成器可以把模板里的障碍点转换为世界坐标,同时检查相邻动作是否可达。

这个检查不要求做完整物理模拟,至少要有保守规则。坑宽不能超过当前跳跃能力;滑铲障碍前要有可见预告;落地点后不能立刻接高障碍;空中金币线不能诱导玩家撞到不可见平台。策划配置新段落时,工具应该直接标出风险,而不是等 QA 玩 30 次才发现第 17 秒有一个理论上过不去的组合。

Phaser 中的段落生成器

在 Phaser 实现上,建议把地面滚动、障碍池、段落选择和玩家控制拆开。RunnerScene 只负责生命周期和对象挂载;RunDirector 负责决定下一个段落;SegmentSpawner 把段落数据实例化成 Sprite、TileSprite 或 Matter/Arcade 碰撞体;PaceModel 计算速度;FailureTracker 记录碰撞上下文。这样当你做每日挑战、固定种子或活动关卡时,不需要复制 Scene。

type ActionKind = "jump" | "slide" | "doubleJump";

interface SegmentTemplate {
  id: string;
  durationMs: number;
  minSpeed: number;
  maxSpeed: number;
  actions: Array<{ atMs: number; kind: ActionKind; variant: string }>;
  cooldownAfterMs: number;
}

export class RunDirector {
  private lastSegmentIds: string[] = [];

  constructor(private readonly templates: SegmentTemplate[]) {}

  choose(ctx: { speed: number; recentFails: number; distance: number }) {
    const candidates = this.templates.filter((tpl) => {
      const speedOk = ctx.speed >= tpl.minSpeed && ctx.speed <= tpl.maxSpeed;
      const notRepeated = !this.lastSegmentIds.includes(tpl.id);
      const forgiving = ctx.recentFails === 0 || tpl.actions.length <= 3;
      return speedOk && notRepeated && forgiving;
    });

    const picked = candidates[Math.floor(Math.random() * candidates.length)] ?? this.templates[0];
    this.lastSegmentIds.push(picked.id);
    this.lastSegmentIds = this.lastSegmentIds.slice(-3);
    return picked;
  }
}

这段代码故意没有把随机权重写复杂。真实项目可以加入权重、标签、固定种子和活动配置,但第一版先保证“不重复、不越速、失败后不继续压迫”。跑酷项目常见的坏味道是生成器越来越聪明,但没有任何可解释规则。结果玩家死了,团队也说不清为什么那里生成了双滑铲接窄平台。

镜头预告是难度的一部分

跑酷的镜头通常锁定玩家,但横向提前量非常关键。速度越快,玩家越需要看到右侧更远的内容。Phaser Camera 可以跟随玩家,也可以让玩家在屏幕中的位置随速度略微左移,让右侧视野变大。这个调整要平滑,不能突然拉镜头。对于高风险障碍,还可以在屏幕边缘加一个轻量警示,比如飞行敌人进入前的影子、地面震动、颜色提示。预告不是降低难度,而是让失败归因变得公平。

背景视差也会影响节奏感。远景移动太快会造成视觉噪音,玩家不容易读障碍;近景装饰太多会和碰撞体混淆。建议把“可碰撞物”和“装饰物”在颜色、层级、轮廓上明确区分。玩家在高速状态下只会读大形状,不能指望他分辨一个漂亮但危险的细节。跑酷画面越花,越需要可读性规则。

碰撞盒要给玩家一点尊严

跑酷失败很容易让玩家生气,因为一次碰撞就终止。本体碰撞盒可以比角色贴图略小,坑边也可以有 2 到 4 帧的宽容。这个宽容不是作弊,而是补偿动画和触摸延迟。尤其在移动端,玩家按下到事件到达 Phaser 可能有几十毫秒,如果碰撞盒完全贴合美术边缘,游戏会显得刻薄。关键是宽容要一致,不能某些障碍宽容、某些障碍严格。

Arcade Physics 足够处理大多数跑酷。若使用 Matter Physics,要避免复杂多边形地形造成不可预期弹跳。地面、墙、滑铲障碍、飞行敌人最好都使用简单碰撞体。每个危险物需要带上 hazardType,失败时记录是撞头、撞身体、掉坑还是被追兵追上。没有失败分类,就无法分析关卡问题。

失败反馈要能指导下一次

失败后只显示“再来一次”是不够的。至少应该告诉玩家死亡距离、原因、最近一次输入和推荐动作。比如“第 428 米,滑铲过晚,距离障碍 0.18 秒”。这类反馈不一定全部展示给普通玩家,但在调试面板和埋点里必须存在。开发阶段可以在死亡点附近画出最近 2 秒的输入轨迹、玩家碰撞盒和障碍碰撞盒,很多争议会立刻消失。

复活点设计也要和节奏系统配合。复活后不应立刻回到高压组合中间,最好回滚到当前段落的安全入口,清掉屏幕内不可反应的障碍,给 1 秒无敌或半透明预备。否则广告复活会变成“看完广告又死一次”,这是运营和体验双输。

可测试的跑酷才可调

跑酷关卡可以用固定随机种子生成。每日挑战、排行榜和 QA 复现都依赖这个能力。生成器输入同样的种子、版本和配置,必须得到同样的段落序列。否则玩家成绩无法比较,失败日志也无法复现。Phaser 里不要直接使用全局 Math.random(),可以注入一个种子随机函数,让所有选择都可追踪。

还可以做离线可达性检查:模拟速度曲线,遍历段落模板,检查动作间距是否小于最低反应时间,坑宽是否超过跳跃能力,奖励线是否穿过危险区。这个检查无法替代试玩,但可以提前拦住明显不合理的配置。内容越多,越不能靠人工记忆维护规则。

上线前检查清单

确认段落生成不是纯随机;确认速度曲线和动作窗口有共同单位;确认高风险障碍在进入可操作区域前有足够预告;确认失败日志记录距离、段落 id、障碍 id、玩家速度、最近输入;确认碰撞盒在调试模式可视化;确认复活点不会把玩家放回必死组合;确认固定种子能复现同一局;确认移动端横屏、刘海屏和低帧率下右侧视野仍然足够;确认所有装饰物不会被误读为碰撞物。

如果你的跑酷项目已经“能玩但不好玩”,优先检查节奏系统,而不是继续加角色和皮肤。玩家真正记住的是某个险些跳过的坑、一次刚好滑过去的压迫感、一次失败后知道自己能改进的冲动。Phaser 能把跑酷原型做得很快,但要把它做成可信的游戏,需要把速度、生成、镜头和反馈放在同一个设计闭环里。

关卡调参要留下版本记录

跑酷调参经常发生在上线前最后几天:坑宽缩短 12 像素,滑铲障碍提前 0.2 秒,金币线从高处改到低处。如果这些改动只存在策划表格或口头沟通里,线上问题很难追。建议每次发布都记录段落模板版本、速度曲线版本和碰撞参数版本。失败日志里也带上这些版本号。玩家反馈“昨天还能过,今天过不去”时,你能立刻知道是哪次参数改动影响了他。

还可以为每个段落设置设计意图字段,比如“训练短跳”“奖励大胆路线”“压迫滑铲反应”。这个字段不一定展示给玩家,但对团队沟通很有用。QA 发现某段失败率高时,可以判断它是否符合意图:如果本来是热身段却有 60% 死亡率,就该降;如果是后期挑战段且失败后玩家愿意重试,可能只是需要更清楚的预告。节奏设计最终要服务玩家体验,而版本记录让体验问题有证据可查。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页