FEATURED · 精选文章

Unity 2D跳跃游戏核心实现:Rigidbody2D物理、手感调优与PlayerController详解

发布时间 / 2026/9/2 1:55:07
来源 / 创域科博编辑部
栏目 / 资讯中心
Unity 2D跳跃游戏核心实现:Rigidbody2D物理、手感调优与PlayerController详解 简介Unity2D跳跃游戏demo源码是一套面向Unity2018及以上版本开发者的平台跳跃类游戏工程以可运行的完整项目演示了2D角色移动、跳跃、碰撞与动画表现等核心机制。项目围绕跳跃玩法组织代码包含角色控制器参数设定、物理材质摩擦与弹跳调节、碰撞检测处理、跳跃动画状态切换、UI分数显示、关卡切换流程和音效播放管理等模块结构清晰适合正在学习2D游戏开发或准备制作小游戏原型的读者参考。压缩包共3810个文件主要包含C#脚本、prefab预设、asset数据资源、png贴图、mp3音频及dll依赖库等整体打包大小约13.94MB下载后可以直接与本地工程对照学习。目前已有2239人学习下载。通过这份源码可以具体弄清跳跃力度如何计算、落地状态如何判定、二段跳怎样扩展、碰撞事件如何响应并从目录结构中理解一个完整2D小游戏的场景组织与逻辑流转。在此基础上继续添加新机关、新角色或替换美术素材都很方便是一份兼具教学性与扩展性的Unity2D入门资源。 我一直觉得2D跳跃游戏是Unity入门最值得做的第一个完整Demo没有之一。它的代码量不大但几乎覆盖了做游戏会碰到的所有核心问题输入处理、物理反馈、碰撞检测、动画状态切换、相机跟随、关卡搭建。换句话说你把这个Demo吃透了后面做任何2D动作游戏、平台跳跃、甚至解谜游戏底子基本就有了。这篇就把我整理的这套可运行源码的思路、关键实现和调优经验完整拆开讲清楚。1. 为什么用Rigidbody2D而不是CharacterController物理型跳跃的核心决策很多新手第一次做2D角色控制最容易纠结的问题是角色移动到底该用Rigidbody2D加力、直接改transform还是像做3D那样用CharacterController。这里直接给结论2D平台跳跃场景首选Rigidbody2D而且是把它当物理体来用不是当传送门。原因在于跳跃这个行为本质上是给物体一个瞬间速度然后让重力接管。如果你直接改transform的position来实现上下移动重力、碰撞反弹、斜坡滑行这些效果全都得自己写物理公式最后做出来的手感大概率是僵硬的、飘的。而Rigidbody2D会替你处理重力加速度、碰撞响应、摩擦力、反弹系数这些底层计算你只需要给它正确的初速度剩下的物理引擎全包了。理解了这点角色控制器的核心逻辑就清晰了水平方向用速度控制垂直方向用重力加跳跃初速度。这里有个容易踩的坑——很多人喜欢把角色放在Sorting Layer或者用Sprite独立移动导致角色和场景碰撞体对不上。正确的做法是让根节点挂Rigidbody2D和Collider2D子节点只负责表现层如Sprite、动画、特效。这样物理计算和视觉表现是两个层级逻辑不混乱。具体到Rigidbody2D的配置我是这样设置的Gravity Scale默认1通过全局调大或调小来调整游戏整体重力感。Collision Detection设为Continuous防止角色高速下落时穿透薄地板。Interpolate设为Interpolate消除低帧率下的抖动尤其相机跟随角色时作用明显。Constraints锁定Z轴旋转2D游戏里Z旋转是画面内的旋转不然角色碰到斜坡会翻倒。这套配置做完物理底层就稳了。下一步是设计角色的移动和跳跃逻辑这部分直接决定游戏好不好玩。2. 跳跃高度、重力加速度与初速度的关系把手感变成可调参数跳跃手感是跳跃游戏Demo的灵魂。很多人做完之后觉得角色跳得很奇怪又说不出哪里不对其实就是没搞清楚重力、跳跃初速度和目标高度这三个参数之间的数学关系。这里用高中物理就能算清楚。角色跳起来后重力会让它的速度线性衰减直到最高点速度为0。设目标跳跃高度是h重力加速度是g那么跳跃初速度v0和它们的换算关系就是v0 sqrt(2 * g * h)。反过来如果你设定了初速度和重力h v0² / (2 * g)。在Unity里Rigidbody2D的Gravity Scale是乘以默认重力-9.81的系数。举个例子我想让角色跳2.5米高重力保持默认那么初速度就是sqrt(2 * 9.81 * 2.5) ≈ 7.0。这时候你直接把velocity.y设为7角色正好跳到2.5米。但实际做项目很少直接算这个我习惯在Inspector面板里暴露Jump Height和Gravity Scale两个参数然后根据它们反推初速度。这样调手感非常直观不用每次改数都去翻代码。关键是心里要有数跳跃高度由初速度和重力共同决定调整任意一个都会改变手感。初速度大、重力小跳得高且飘初速度小、重力大跳得低且重。这里再展开说一个可变跳跃高度的技巧这是专业平台跳跃游戏马里奥、蔚蓝手感的精髓玩家按住跳跃键时跳到全高轻点一下就只跳一小段。实现方式不复杂跳跃初速度分两段起跳瞬间给一个大的初速度如果玩家在上升过程中松开跳跃键立刻把垂直速度减半或者加一个额外的向下加速度让角色快速下落。代码逻辑大概是这样if (Input.GetButtonUp(Jump) rb.velocity.y 0) { rb.velocity new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); }这个0.5的系数就是松手衰减系数调得越小轻点跳跃就越矮。我把这个也做成参数暴露出来方便后续自己试手感。这套参数体系做完跳跃的核心手感就有了下面的普通移动和碰撞检测可以在这基础上继续补充。3. 从空场景到完整Demo搭建步骤、图层规则与相机跟随做Demo最忌讳一上来就堆代码。我先讲一套自己常用的搭建顺序按照这个顺序来逻辑会清晰很多。3.1 第一步切割素材和设置图层不管你是用免费素材还是自己画的图先把素材切好是关键。在Project窗口选中图片把Texture Type改为Sprite(2D and UI)Sprite Mode选Multiple然后打开Sprite Editor切分帧图。这一步不需要做得很精细但至少要把角色待机、跑步、跳跃三组动画的帧图切好。图层设置我建议用两类Tag/Layer一类是Ground一类是Player。为什么非要用Layer因为后续做地面检测判断角色是否站在地上时你需要用LayerMask精确指定只检测地面层否则角色站在敌人身上、站在道具上也算落地跳跃逻辑就全乱了。这是个很小的细节但能避免很多莫名其妙的问题。3.2 第二步搭建测试场景Demo不需要大世界一个简单的测试平台就够了。我用Tilemap画了大概三块不同高度的地面中间留出有挑战性的跳跃间距。场景里必放的有一块地面作为出生点一个高台用于测试跳跃高度是否达标之间留一条小沟测试水平移动速度是否顺手背景随便铺一层色块保证相机跟随时有视觉参照这个测试场景会在后续调参时反复用到。做得越方便调起来越省心。3.3 第三步角色Prefab结构设计Prefab的结构我强烈建议按下述层级设计Player根节点挂Rigidbody2D BoxCollider2D PlayerController脚本Sprite子节点挂SpriteRenderer和AnimatorGroundCheck子节点一个空的Transform放在角色脚底GroundCheck单独用子物体表示是因为地面检测是一个点的概念而不是Collider的整体范围。点检测的判定位置更精确、更可控也方便调整检测范围。3.4 第四步相机跟随2D平台跳跃的相机跟随其实比3D简单得多核心就是一个Lerp平滑。但直接用Update里做相机跟随会有个问题相机一抖角色看起来就在滑。我的做法是在LateUpdate里做插值因为LateUpdate在物理更新之后执行能保证相机看到的画面是最新状态。同时用一个Offset变量控制相机和角色的相对位置给玩家留出看前方的视野空间。具体代码很简短void LateUpdate() { Vector3 targetPos player.position offset; transform.position Vector3.Lerp(transform.position, targetPos, smoothTime * Time.deltaTime); }到这里场景、角色、相机这套骨架就通了。接下来把角色控制器和动画状态机填进去才算真正能玩。4. 角色移动、地面检测、跳跃缓冲与土狼时间四合一PlayerController实现网上的跳跃Demo成千上万但多数只是简单写了按空格跳一下手感非常粗糙。我这里给的这套实现把平台跳跃游戏真正必要的四个模块都做了进去代码量不大但提升效果非常明显。4.1 移动逻辑加速度与摩擦力水平移动不要直接把速度设为目标速度那样起步和停止都太生硬。要给一个加速度让角色在0.1秒内达到最大速度和一个摩擦力松手后在0.05秒内停下来。简单实现就是使用MoveTowards在水平和实际速度之间做插值float targetSpeed Input.GetAxisRaw(Horizontal) * maxSpeed; float speedDif targetSpeed - rb.velocity.x; float accelRate (Mathf.Abs(targetSpeed) 0.01f) ? acceleration : deceleration; rb.velocity new Vector2(rb.velocity.x speedDif * accelRate, rb.velocity.y);这里面加速度和减速度我设为不同的值。减速度通常比加速度大这样角色急停时手感更利落。有经验的玩家能明显感到响应跟手。4.2 地面检测一个点的事地面检测是跳跃判断的前提。网上常见做法是在Update里持续判断Physics2D.OverlapCircle是否碰到地面层碰到就把isGrounded设为true。我用Physics2D.OverlapCircle圆心放在GroundCheck点半径设为0.1左右根据角色大小微调LayerMask只勾选Ground层。这里有个经验细节不要把所有可站立的物体都塞进Ground层否则敌人、可破坏物都会导致角色逻辑误判。Demo阶段就单独建一层Ground只放平台和地面。4.3 跳跃缓冲手感和容错的关键跳跃缓冲和土狼时间是高级平台跳跃游戏手感的两大支柱很多人第一次听到会觉得玄但实际实现很简单。跳跃缓冲解决的是玩家在角色落地前就按了跳但角色落地后没有跳起来的挫败感。做法是玩家按下跳跃键时记录一个时间戳跳跃判断时检查当前时间与按下时间的间隔是否小于缓冲窗口通常设0.1秒如果是就允许跳跃。这样玩家在空中提前半拍按跳落地后角色会立刻跳起感受上是跟手的。4.4 土狼时间给玩家的宽容度土狼时间解决的是相反的问题角色刚刚走出平台边缘玩家迟了半拍按跳按物理逻辑已经不能跳了但允许在一个极短的时间窗口内通常0.1秒仍然可以跳跃。名字来源于《疯狂动物城》里土狼跌出悬崖还能踩两脚空气的动画桥段。实现也不难isGrounded变为false时记录一个时间戳跳跃判断时检查离开地面时间是否在土狼窗口内。这两个机制合在一起手感会有质的飞跃。玩家会觉得这个游戏很宽容很顺手而不会觉得这游戏判定太严格了玩得火大。四合一控制器合起来核心代码骨架大致如下void Update() { if (Input.GetButtonDown(Jump)) jumpBufferTimer jumpBufferTime; else jumpBufferTimer - Time.deltaTime; if (Physics2D.OverlapCircle(groundCheck.position, checkRadius, groundLayer)) coyoteTimer coyoteTime; else coyoteTimer - Time.deltaTime; if (jumpBufferTimer 0 coyoteTimer 0) { rb.velocity new Vector2(rb.velocity.x, jumpForce); jumpBufferTimer 0; coyoteTimer 0; } if (Input.GetButtonUp(Jump) rb.velocity.y 0) rb.velocity new Vector2(rb.velocity.x, rb.velocity.y * jumpCutMultiplier); }5. 动画状态机与表现细节让角色看起来真的在跳跃物理和逻辑搞定后最影响观感的就是动画。很多Demo代码没问题但玩起来像个纸片人平移因为角色没有根据状态切换动画。我建议至少做待机Idle、跑步Run、跳跃上升Jump、下落Fall四个动画状态。Animator的状态机不要做得太复杂一个AnyState到四个状态的转换就够。关键在转换条件的设置待机→跑步Speed 0.1跑步→待机Speed 0.1任意状态→跳跃上升isGrounded false verticalVelocity 0跳跃上升→下落verticalVelocity 0任意状态→待机isGrounded true关于动画有几个容易忽略的细节跳跃前倾起跳瞬间给Sprite一个略微前倾的旋转或倾斜动画能极大增强发力感。落地压缩落地时把Sprite scale的y短暂压到0.95再还原。这是几行代码的事但效果立竿见影。动画发射方向角色左右移动时翻转Sprite的localScale.x不要用旋转因为旋转会影响子节点的相对坐标。除此之外还有一个小细节地面检测点要和角色的脚底动画位置对齐。如果动画里角色脚底有个上下晃动的动作GroundCheck点就必须跟着动画的脚底位置移动否则会出现明明看起来站在台子上却判定为空中的bug。解决方式是把GroundCheck作为一个子物体在动画关键帧里微调它的位置或者干脆把检测半径加大一点容忍动画晃动幅度。6. 从能跑到好玩手感参数调优实测Demo做到这里功能已经完整了但能玩和好玩之间差的是一组精心调过的手感参数。我给自己定了一个调优顺序按这个顺序来不会越调越乱。第一优先级是跳跃高度和移动速度的匹配。先定移动速度再定跳跃高度。跳跃高度要能轻松跳到平台高度一段间距的程度这个间距就是角色能达到的最高点和平台边缘的差值。如果平台高度2米跳跃高度2.5米玩家会感觉很顺如果跳跃高度刚好2.1米玩家就得每次都精确踩点非常累。第二优先级是加速度和减速度。加速度过大起步瞬间像瞬移过小则角色像在冰面上滑。我实测下来4-6的加速度在32像素/单位的分辨率下比较舒服具体数值因角色大小而异建议多做几次AB对比。第三优先级才是跳跃缓冲和土狼时间的具体数值。缓冲0.1秒、土狼0.1秒是很多游戏的常用值但大家设备刷新率不同有些人觉得0.08更像自己跳的有些人需要0.12才觉得不迟滞。这个真的没有标准答案只能你亲自试。下面是我调过的一套基准参数可以作为起点参考参数意义参数名推荐基准值备注最大水平速度Max Speed6单位逻辑像素/帧加速度Acceleration5越大响应越快减速度Deceleration8一般比加速度大跳跃高度Jump Height2.5逻辑单位重力缩放Gravity Scale1调整为0.8-1.2对比松手衰减系数Jump Cut Multiplier0.5越小轻点越矮跳跃缓冲窗口Jump Buffer Time0.1秒土狼时间Coyote Time0.1秒这里再额外说一个我在调试时发现的点土狼时间的起始时刻应该从物理上角色脱离地面开始算而不是从isGrounded变成false开始算。因为物理碰撞的检测频率和玩家操作不在同一个帧率上。如果你的isGrounded是FixedUpdate里更新的那么土狼窗口计时也应该在FixedUpdate里做不然窗口会多算或少算一帧。很多手感差异其实就出在这个一帧上。7. 关于Demo源码的使用建议和常见坑最后聊聊拿到这套Demo源码后怎么高效地改成自己的东西。第一个坑是分辨率适配。很多人在做这个Demo的时候用的是默认的Free Aspect分辨率角色尺寸、平台大小、跳跃高度全按这个分辨率调好了结果一发布到手机端或者固定窗口整个画面比例全变了手感全部作废。Unity在2D游戏的标准做法是Camera的Projection设为Orthographic然后通过调整Size来控制可视高度这样不管窗口多宽世界坐标里的高度永远可见只是宽度会裁切。角色参数调好后不要频繁改分辨率否则所有手感参数都得重来一遍。第二个坑是Rigidbody2D的Interpolate和相机跟随的Lerp产生二次平滑。如果启用了Interpolate相机的Lerp速度就别设太低否则画面会看起来黏黏的。我习惯关掉Rigidbody的Interpolate全靠相机插值或者反过来两者只用一层平滑。第三个坑是Time.timeScale。很多人在做暂停功能或者慢动作效果时会设置Time.timeScale 0但在Update里直接读Input做跳跃缓冲时Time.deltaTime是0jumpBufferTimer不会衰减导致角色暂停结束后会突然跳一下。解决方案是把计时器相关的累加放到UnscaledDeltaTime里或者干脆用GameState判断。这个坑很隐蔽我做了好几版才想起来。如果你想把Demo扩展成更完整的游戏我建议优先考虑加以下三个模块移动平台在平台上加一个往返移动的脚本角色站在上面会被带着走这是平台跳跃关卡设计的重要元素。可收集物加一个简单的收集物和计数UI让玩家有目标感。死亡与重生加一个死亡判定的坑比如Y坐标低于某值就重生Demo就有了循环玩法测试起来也更省心。我个人的经验是Demo的核心意义不在完整而在循环。一个能让玩家玩10秒、挂掉、再重来的循环比一个有20个关卡但没调好手感的产品更接近游戏。把基础手感调到让自己舒服再往外扩展后面会省很多事。这套源码我保留了很大的参数可调空间只要你愿意在Inspector里一个个试很快就能调出属于自己的手感。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻