
简介这是一份适用于 Unity 引擎的无限跑酷项目源码面向想快速上手休闲跑酷玩法、学习完整项目制作流程的开发者。项目包含角色控制、障碍生成、计分与复活等核心机制并配有演示视频便于对照效果。整包共 2003 个文件大小 600.57MB主要文件包括 C# 脚本、Prefab 预制体、Shader 着色器、FBX 模型、材质、图片、音频及 JSON/TXT 配置另有 553 个 Markdown 笔记辅助理解模块结构8 个场景配合预制体已构成可运行框架。工程设置中已包含输入、物理、质量等常用参数避免从零搭建环境的成本。大量预制体与场景让玩法扩展更直观Markdown 与配置资料帮助梳理逻辑降低了学习和定制的门槛。已有 468 人学习/下载适合 Unity 学习者、毕设学生和独立开发者作为完整示例既能理解跑酷类游戏的设计流程也能作为原型快速迭代。1. 为什么一套 Unity 无限跑酷源码值得拆开看如果只是下载一套 Unity 无限跑酷源码打开工程按一下 Play看到角色跑起来就算完那你会漏掉这个项目里最有价值的部分自动地形生成、对象池复用、角色状态机和手机端性能策略。单看标题它像是个小游戏 Demo但真正能用于商业立项的跑酷项目恰恰需要把这些机制吃到透。很多开发者第一次拿到这类源码会遇到地形生成不连续、内存持续涨、手机真机掉帧这类问题如果你知道这些坑在哪这套源码就是你搭建跑酷类原型的骨架。适合刚接触完整跑酷流程的新手也适合直接拿来做项目往上叠功能的客户端开发。2. 核心机制拆解跑酷角色为什么能“自动向前跑”提示这一章涉及源码里最常见的三类脚本。如果你只想通过改参数调手感也得先明白哪个脚本控制移动、哪个控制跳跃、哪个管生成。2.1 自动跑动的实现角色留在原地环境往后退很多跑酷项目的移动核心并不是让角色真的向前跑而是让角色停留在局部原点附近把地面和障碍物整体向镜头方向平移或者给角色刚体一个恒定速度。两种方案各有使用场景平移场景组件适合不需要物理碰撞的简单 2D 跑酷而大多数 3D 无限跑酷源码用的还是刚体恒定速度因为这样可以直接复用重力、碰撞和跳跃的物理反馈。我习惯在 FixedUpdate 中强制写入 z 方向速度而不是在 Update 里给 Transform 加位移。原因是跑酷中跳跃和落地都依赖物理引擎如果用 Transform 直接改 z很可能在碰撞回调里出现“卡进”障碍物的情况。下面是这类源码里最常见的自动移动核心using UnityEngine; public class AutoRunner : MonoBehaviour { public float runSpeed 8f; public Rigidbody rb; void FixedUpdate() { Vector3 vel rb.velocity; vel.z runSpeed; rb.velocity vel; } }代码逻辑很简单每一帧物理更新前把 z 方向速度强制设定为固定值其他方向保留物理引擎的结果。这样角色会保持向前跑同时 y 方向的速度由重力控制。参数 runSpeed 是所有手感参数里第一个要调的项它决定障碍物出现在视野后的反应时间。如果 runSpeed 设成 12而跳跃高度只有 2那很多跳不过去的障碍就会出现碰撞实际调参时跑速和跳跃力必须一起调。与直接移动场景组件的方式相比刚体方案有个好处当你想做加速带或者被弹飞道具时只需要改变速度的插值而不需要处理整个场景的坐标系偏移。常见做法是在场景里放一个 GameManager把 AutoRunner 的速度暴露成属性由 GameManager 统一管理加速节奏。2.2 角色状态机跑步、跳跃、滑铲如何切换跑酷源码里第二个核心是角色状态机。最简单的实现是枚举加每帧条件判断稍复杂的会挂 Animator 状态机。源码里我看到的多半是枚举加状态锁因为这种实现好调试、好扩展。关键点在于跳跃的判定必须严格区分“空中”和“地面”否则容易出现二段跳、无限跳。下面的代码是一个简化版但能跑的状态切换逻辑public enum PlayerState { Running, Jumping, Sliding } public class PlayerController : MonoBehaviour { public PlayerState state PlayerState.Running; [Header(GroundCheck)] public Transform groundCheck; public float checkRadius 0.2f; public LayerMask groundLayer; void Update() { if (state PlayerState.Jumping IsGrounded()) state PlayerState.Running; if (Input.GetKeyDown(KeyCode.Space) IsGrounded()) state PlayerState.Jumping; if (Input.GetKeyDown(KeyCode.S) state PlayerState.Running) state PlayerState.Sliding; } bool IsGrounded() { return Physics2D.OverlapCircle(groundCheck.position, checkRadius, groundLayer); } }这段代码使用 OverlapCircle 做一个临时碰撞判定。groundCheck 必须放在角色脚底偏下的位置而且最好单独建一个 Ground 层把地面和障碍物分开否则角色会把自己的碰撞体误判成地面。参数 checkRadius 影响手感半径太小会导致落地后要过十几毫秒才进入 Running半径太大会在地面边缘提前触发跳跃。在复杂跑酷项目里滑铲不只是换一个动画而是要换碰撞体大小否则角色滑铲时头部还是会撞到上方障碍。源码里如果保留了 crouch collider记得在状态切换时同步禁用或启用对应子物体。2.3 无限地图的生成策略预制体切片和对象池无限地图不是真的无限而是用有限的预制体无限拼接。很多跑酷源码会把一整段跑道路径做成一个 Segment每个 Segment 中包含地面、障碍物、装饰物、金币。生成逻辑由 GameManager 根据玩家位置判断是否需要在前面再放一个 Segment同时把身后的 Segment 回收掉。这里最容易写出的问题是每帧生成、每帧销毁会导致实例化开销和 GC 峰值。所以成熟源码里一定会有对象池。下面是一个通用对象池脚本同样适用于障碍物和地面段using System.Collections.Generic; using UnityEngine; public class ObjectPool : MonoBehaviour { public GameObject prefab; private QueueGameObject pool new QueueGameObject(); public GameObject Spawn(Vector3 position) { GameObject go pool.Count 0 ? pool.Dequeue() : Instantiate(prefab); go.transform.position position; go.SetActive(true); return go; } public void Recycle(GameObject go) { go.SetActive(false); pool.Enqueue(go); } }Spawn 时优先从队列拿隐藏对象拿不到才 InstantiateRecycle 时只是关闭并放入队列没有销毁。这样做能够避免反复创建对象造成的卡顿。使用时要注意如果池里对象带有 Rigidbody回收时要把速度清零否则刚体会带着速度继续参与物理运算内存和 CPU 都会有额外开销。生成位置的判断一般是当最后一段的结束 z 坐标小于摄像机前方预期生成距离时从池里取一段挂到那个坐标当最早一段的结束 z 坐标跑到摄像机后方若干个单位时回收它。这个生成距离不宜太近否则玩家会看到障碍凭空出现也不宜太远否则同一屏内对象太多Draw Call 会更高。常见做法是让它略大于摄像机远裁剪面或者控制在玩家视野内约 2 个屏宽。3. 把源码跑通环境、场景、参数三者对齐3.1 导入前的环境对齐输入系统、渲染管线、重力设置下载源码后直接打开最容易遇到的是 Input System 冲突。现在新建 Unity 工程可能默认启用新的 Input System而跑酷源码如果还在用老的 Input Manager打开后会出现双击报错、按钮无响应甚至 Project Settings 里面找不到对应输入条目。操作上我一般先做一次环境统一Edit Project Settings Player Active Input Handling选中 Both。这样新旧输入 API 都能用不用着急重构源码。如果源码明确说只支持旧输入就把 Active Input Handling 改成 Input Manager然后在 Player Controller 里检查 Input.GetKeyDown 是否有反应。还要检查渲染管线。很多存量跑酷源码用的还是内置渲染管线材质是标准 Shader。如果你把工程升到 URP所有材质会变成紫色。遇到这种情况要么把项目迁回内置管线要么给材质重新选 URP 对应 Shader。做这一步要看清项目依赖不是所有贴图都兼容。最后是 Gravity 和物理步长。跑酷项目对重力依赖很强Project Settings Physics 里默认的 Gravity 参数不要乱改。常见做法是保持默认重力在角色自身的 Rigidbody 上调整 Gravity Scale这样场景里其他物理特效不会跟着受影响。3.2 场景节点检查清单Player、Camera、GameManager、PoolManager跑通前先用场景视图检查四个关键节点。Player 节点必须有 Rigidbody 和碰撞体并且固定旋转、固定 z 轴位移。Camera 节点通常挂在 Player 的某个子节点下或者由脚本跟随 Player如果是脚本跟随注意有没有设定跟随偏移量。GameManager 节点上挂着游戏状态控制、距离计数、失败判定。PoolManager 节点下挂着多个 ObjectPool 子物体每一个对应一种预制体。下面这个表格是我常用的排查清单照着看能少翻车节点必须检查的设置容易踩的坑PlayerRigidbody 非 Kinematic碰撞体大小适中碰撞体过大导致“空气墙”Camera跟随脚本的偏移量正确z 轴抖动GameManager生成距离、回收距离一致生成与回收重叠PoolManager池内 prefab 引用正确多个 prefab 共用一个池场景里的碰撞层级也要单独检查。跑酷项目通常让 Player 只跟 Ground 和 Obstacle 碰撞金币和触发区域用 Trigger不参与物理阻挡。如果项目没有单独建 Ground 层而是让 Player 跟所有 Trigger 都发生碰撞就有概率出现金币卡住角色、跳跃判定报错这些奇怪现象。3.3 手感参数默认值往往不是你的目标跑酷游戏的手感基本就是跑速、跳跃力、重力和判定半径的组合。一般以默认参数为基础按“先跑通、再调整”的顺序。先确认跑速合适再让跳跃能稳定跳过标准障碍最后调滑铲时长。下面这些参数最值得从源码的 Inspector 面板里找出来调参数作用调参建议runSpeed角色前移速度5-10 比较适合新手高于 12 会很难反应jumpForce跳跃初始速度与重力配合跳到最高点要能越过最高障碍jumpGravityScale跳跃时的额外重力调大落地更干脆调小能“飘一点”checkRadius地面检测半径0.1-0.3过大会让玩家感觉还没跳完就落地laneSwitchTime换道插值时间0.1-0.3过短僵硬过长手感太绵跳跃高度的计算关系是 h v² / (2g)。如果你的 jumpForce 是 8重力是 9.81理论最高高度约为 3.26 米。这时如果最高障碍是 3 米看着能跳过去但连续跳跃时会因为起跳瞬间的位置误差卡到边缘。所以我一般会把 jumpForce 值调得偏高一点再把跳跃后的那几帧输入屏蔽掉让手感更宽容。调参方法上不要用 Game 视图边跑边改效率太低。建议把 runSpeed、jumpForce、GravityScale 这几个参数在 Player 脚本里序列化然后把 Inspector 窗口放到旁边Play 时直接改值观察角色动作和 Debug.Log 输出。遇到边界值先暂停再调整减少变量干扰。4. 避坑与常见问题排查五条血泪经验4.1 碰撞判定不稳角色偶尔被“空气墙”卡住现象角色在地面上正常跑某一小段路面突然弹起或者跳跃高度明显变矮像是撞到看不见的物体。原因最常见的是地面段与地面段之间存在重叠的碰撞体或者碰撞体比地面模型大了一点造成微小的凸起。另一个原因是地面材质使用了带碰撞体的装饰物开发者以为是装饰实际上它参与了物理碰撞。解决检查每段地面的 Box Collider 尺寸把 Y 轴厚度压到贴近模型表面Z 轴方向让前后两段之间保留 0.01 的间隙不要重叠。如果依然有凸起在场景视图里把 Player 碰撞体透明显示逐帧看哪段路径有问题。注意修复后要重新运行一遍完整路径不要只测试起点附近。4.2 对象池回收不及时导致内存持续上涨现象跑酷运行十几分钟后帧率开始波动内存占用持续上升Profiler 中 GC Alloc 一帧比一帧高。原因很多源码只在障碍物离开摄像机视野时回收却没有在整段地面后移时统一回收同段内所有子物体。当 Segment 回收后其子物体内的金币和装饰物没有被回池而是继续被 Instantiate 重复创建等价于每跑一段就漏一行内存。解决把地面段、障碍物、金币分别放进对应的 Pool并且让 Segment 在回收时主动调用所有子物体的 Recycle。为了更稳定我一般会再写一个监控如果池内对象数量长期不下降就在 GameManager 里输出一个警告。注意回收时要重置对象的旋转和缩放否则下一个 Spawn 出来的对象可能角度不对。4.3 电脑流畅但手机上一跑步就掉帧现象编辑器里跑 60 帧很稳定打包到安卓或 iOS 之后碰到障碍生成瞬间就会掉到 30 帧以下发热明显。原因手机上主要瓶颈在 Draw Call 和物理开销。编辑器开发时往往开着 Profiler但实际设备上没有观察跑酷场景里大量金币和装饰物都是独立 Mesh每颗金币一个 Draw Call一个屏放几十个就会把移动端 GPU 打爆。解决先把所有同种小物件合并进同一个图集或者在每个 Segment 内设置 LOD远处小物件直接隐藏。再看相机裁剪范围跑酷游戏可以把 Camera Far Plane 从 500 改到 100减少很多不可见物体。最后检查是否有空 Update 循环比如每个金币都在做旋转动画会导致大量 Transform 更新把旋转逻辑搬到 Shader 里或统一合批可以明显改善。4.4 二段跳变成无限跳现象角色明明在空中连按跳跃键却可以连续触发很多次二段跳功效变成了无限跳。原因状态机没有保存“已连续跳跃次数”只是用 IsGrounded 判断是否允许起跳。当角色在地面边缘下落时GroundCheck 的检测圆因为半径设置过大在空中依然能触碰到地面边缘导致状态机认为还在“地面”。解决在 PlayerController 里增加 jumpCount 字段。每次从 Running 进入 Jumping 时 jumpCount 1在空中再次起跳时 jumpCount 2落地后归零。这样无论 GroundCheck 有没有误报都不会超过二段跳。还有另一个容易忽略的点GroundCheck 的 transform 尽量放在角色脚底中心不要放在脚尖否则斜坡边缘误判概率会很高。4.5 碰到障碍不触发失败流程现象角色碰撞体已经碰到障碍但 GameManager 没有播放失败动画也没有停止跑动角色穿过障碍物继续跑。原因障碍物碰撞体没有勾选 Is TriggerPlayer 又是 Kinematic 刚体OnTriggerEnter 不会被触发。或者项目里只写了 OnCollisionEnter但障碍物没有碰撞体组件。解决统一约定玩家撞到障碍用 OnCollisionEnter金币用 OnTriggerEnter。给障碍物加 Box Collider 且不勾 Is Trigger给金币加 Box Collider 且勾选 Is Trigger。然后在 GameManager 里分别监听 OnCollisionEnter 和 OnTriggerEnter。如果遇到个别障碍物“穿透”重点检查这个障碍物的 Collider 有没有被禁用、Rigidbody 是不是非 Kinematic。跑酷场景里的障碍物通常不需要物理模拟给它们挂上 Is Trigger 会省很多物理开销。5. 从源码到自己的玩法三处最值得改造的地方5.1 地图生成器改造加入难度曲线和分段节奏默认源码里的障碍生成往往是固定间隔跑一会儿就觉得无聊。要变成能商用的小游戏得让障碍密度和类型随距离变化。最简单的方式是做一个难度等级表按已跑距离切换生成参数。[System.Serializable] public class DifficultyLevel { public float startDistance; public float obstacleInterval; public float flyingObstacleRate; } public class DifficultyManager : MonoBehaviour { public DifficultyLevel[] levels; public DifficultyLevel GetLevel(float distance) { DifficultyLevel result levels[0]; foreach (DifficultyLevel level in levels) { if (distance level.startDistance) result level; } return result; } }代码里 levels 是有序数组startDistance 从小到大排列。GetLevel 用线性扫一遍就行距离超过某个节点就返回对应等级。实际接入时GameManager 在生成下一个 Segment 前先通过该接口拿到当前等级然后决定障碍间隔和飞行障碍概率。难度曲线不要做成线性单调递增最好每 500 米升一级中间留 200 米的喘息距离不然玩家压力太大容易流失。5.2 角色控制扩展左右三车道换道与蓄力跳很多 3D 跑酷是上下跳跃和滑铲但如果要做跑酷比赛玩法需要加上左右换道。换道不能用刚体直接改位置否则会穿模我一般用协程插值移动保证每一帧位置都是平滑的。public IEnumerator SwitchLane(float targetX, float duration) { Vector3 startPos transform.position; float elapsed 0f; while (elapsed duration) { elapsed Time.deltaTime; float t Mathf.Clamp01(elapsed / duration); Vector3 newPos Vector3.Lerp(startPos, new Vector3(targetX, startPos.y, startPos.z), t); transform.position newPos; yield return null; } }这里 switchLane 的高层逻辑是在外部判断左右方向计算目标 X 坐标直接调用协程。duration 默认 0.15 秒手感比较合适如果跑速很快可以缩到 0.1。换道过程中建议屏蔽跳跃输入因为同时换道和跳跃会让碰撞判定变得复杂。另一个常用扩展是“蓄力跳”长按跳跃会增加跳跃高度松开时按当前蓄力值给刚体一个速度。实现上不需要改状态机只在 Update 里记录按键时间然后在 Jump 函数里把额外速度叠加到 jumpForce 上。5.3 存档系统从 PlayerPrefs 到结构化 JSON源码里常见的是用 PlayerPrefs 存储跑到的最远距离和金币数。这样做简单但跨版本更新时很容易丢字段特别是添加新道具后旧存档没有对应字段会导致读取异常。我习惯在源码里直接改成 JSON 序列化即使未来加字段也好兼容。[System.Serializable] public class SaveData { public int lastDistance; public int coins; public string version; } public class SaveManager { public static void Save(SaveData data) { string json JsonUtility.ToJson(data); PlayerPrefs.SetString(RunSave, json); PlayerPrefs.Save(); } public static SaveData Load() { string json PlayerPrefs.GetString(RunSave); if (string.IsNullOrEmpty(json)) return new SaveData(); return JsonUtility.FromJsonSaveData(json); } }这个方案的坑在于从旧存档读取时没有 version 字段会直接变成默认值。更稳妥的做法是 Load 后先检测 version如果是旧的就为缺失字段补默认值。不要直接信任 PlayerPrefs 的本地数据玩家清理缓存是常态。如果是移动端上线版本存档里尽量别放敏感数值只放可恢复的进度本地存档要做到“删掉也能重新开始”服务器同步是后面再考虑的事。5.4 小地图和暂停菜单的联动跑酷源码的另一处值得改的是暂停和恢复。很多简单 Demo 一暂停就直接 Time.timeScale 0但这样做会导致对象池回收逻辑和协程被打断。重回游戏时角色仍在跑但所有池对象的回收判断停了一拍后续可能一连串报错。最好做一个 GamePause 管理器暂停时只停止 PlayerController 和 GameManager 的更新不把整个时间轴停掉或者是显式处理协程。一个小技巧是暂停时把所有在跑的对象池身上的 Rigidbody.isKinematic 临时设成 true暂停结束再恢复能避免很多意外的物理穿透。6. 手感调优与性能验证最后要做的三个实操作源码本身能跑通之后更重要的是确认它在你目标设备上的表现。第一步是用 Unity Profiler 的 CPU Usage 模块跑 3 分钟重点看 FixedUpdate 里的物理耗时和 GC Alloc。如果发现 GC Alloc 一直在涨立刻检查对象池回收路径不要等优化完再查。第二步是给场景里所有动态生成物体关闭 shadow casting或者限制阴影距离跑酷场景里阴影是最容易被忽视的性能刺客。第三步是做一个“输入回放”测试把玩家在一段路径里的输入序列记录下来再回放同样的输入确认每次起跳和落地结果一致。跑酷源码里最怕的是随机地形生成让手感不可复现使用固定随机种子在测试阶段尤其重要上线前再换成随机种子即可。我在调这类项目时发现很多开发者会把大量时间花在改美术和 UI 上忽略了 FixedUpdate 里不断实例化对象这种基础问题。其实跑酷手感好不好第一看跑速和跳跃的响应窗口第二看碰撞体是否干净第三就是性能余量。从那以后我每次拿到跑酷源码都会先强制自己打开 Profiler把物理开销和 GC 涨势看一遍确认没有坑再开始改玩法逻辑。希望帮到你。本文还有配套的精品资源点击获取