资讯动态

Cocos Creator 3.8 2D人物控制实战:移动、跳跃与碰撞触发

发布时间:2026/9/18 20:34:51 来源:尧图企业网站定制
前阵子用 Cocos Creator 3.8 重做以前一个 2D 横版 Demo主角要能左右跑、跳跃、踩怪触发伤害还要被金币碰撞拾取。我心想这不就是最基础的物理控制吗结果真动手才发现从节点搭建、刚体参数、分组矩阵到回调监听每一步都有“看着对、一运行就翻车”的细节。这篇文章就围绕 Cocos 3.8 里的 2D 人物控制完整流程来写包含移动、跳跃、碰撞和碰撞触发适合刚接触 3.8、或者从 2.x 迁移到 3.x 后对物理接口不熟的开发者。文里所有代码我都按 3.8 的 API 实测跑过配置路径也按 3.8 的编辑器界面描述你跟着搭一遍基本能直接用到自己的项目里。1. 2D人物控制的核心矛盾物理模拟与直观位移的取舍刚开始做人物控制最本能的做法就是拿到节点后node.setPosition(...)或者直接改node.position.x。我第一版也是这么写的键盘按左就往左挪按右就往右挪空格就把 y 加上一段。问题很快就来了——角色撞到墙以后你明明停止了物理碰撞可主角还是能被按键推进墙里因为这句“改坐标”直接绕过了刚体的物理模拟。碰撞体不是摆设它只是检测真正决定“能不能过去”的是刚体系统对碰撞的响应你手动改位置等于把刚体的结果覆盖掉自然穿模。后来才意识到在 Cocos 3.8 里做 2D 人物控制本质上是在“物理引擎的规则”和“手写位移逻辑”之间做取舍。搞清楚这一点后面移动、跳跃、碰撞触发的各种细节就都有了判断标准。1.1 三种移动实现方案对比以 Cocos 3.8 的 2D 物理体系来说常见的移动实现有三种实现方式典型做法优点缺点直接改节点坐标node.position offset简单直观不受物理干扰会绕过物理引擎的碰撞响应容易穿墙与刚体同步容易产生“瞬移”或卡入障碍设置刚体速度rigidBody.linearVelocity new Vec2(...)平滑、贴合物理系统适合平台跳跃速度会被物理模拟影响需要配合地面检测和参数调优施加力/冲量applyForceToCenter(...)/applyLinearImpulse(...)物理真实感强适合“弹射”“击退”手感难调速度不可直接预期普通移动用它容易飘我的最终选择是移动用“设置刚体速度”跳跃也是“设置刚体速度”直接改 y 轴速度。为什么要这样做因为平台跳跃类游戏对手感的要求很高你按下左键角色就应该以稳定的速度向左移动如果你用applyForce力的累计会受质量、阻尼、碰撞影响速度会有延迟感。而直接设置linearVelocity可以精确控制每一帧的水平速度同时保留刚体对碰撞的响应角色撞到墙就会被挡下来而不是穿进去。1.2 物理模块与碰撞矩阵两件小事决定成败在 Cocos 3.8 里2D 人物控制能不能跑起来有两个前置设置经常被忽略。第一检查你的工程是否启用了 2D 物理模块。新建的空工程如果没做过裁剪一般没问题但如果你按“最小包体”或“纯逻辑”模板创建过工程很可能把 Physics 2D 模块裁剪掉了。路径在Project Settings - Feature Cropping需要确保Physics 2D被勾选。没启用的话场景里所有 2D 碰撞体都不会产生任何回调也不会有物理碰撞而且编辑器控制台不一定会报明显错误非常容易让人摸不着头脑。第二碰撞矩阵是“默认全不碰撞”的。这是 3.8 的一个大坑很多初学者把两个碰撞体摆在一起运行后发现角色直接掉穿地面第一反应是代码问题其实只是分组矩阵没配对。Cocos 3.8 里你可以给物体设置 Group分组然后在Project Settings - Physics - Collision Matrix里勾选“哪几个组之间允许碰撞”。如果你没手动勾选那么即使 Player 和 Ground 都有自己的碰撞体它们也不会互相碰撞。我在后面第 5 节会专门讲分组和碰撞矩阵怎么配这里先记住做人物控制前先把这几个开关检查一遍。2. 从零搭建人物节点层级、刚体和碰撞体一次配好Cocos Creator 3.8 的 2D 物理组件和 3D 物理组件是两套体系2D 用RigidBody2D、BoxCollider2D这些带2D后缀的组件。节点结构配置得好不好直接影响到后期写代码的复杂度。我建议直接用下面这套比较稳的结构。2.1 推荐的节点树结构以主角为例我习惯这样组织节点Player空节点挂 RigidBody2D PlayerController 脚本 ├── Sprite子节点只负责显示 ├── BoxCollider2D挂在 Player 节点上作为身体碰撞体 └── GroundCheck子节点挂 BoxCollider2D勾选 Sensor你可能会奇怪为什么 Sprite 是子节点碰撞体却挂在父节点上因为RigidBody2D会统一管理绑定在它身上、以及它所有子节点上的Collider2D碰撞体挂在父节点上可以避免随着子节点的位移、旋转出现物理区域错位。Sprite 单独做子节点是为了后面做动画、翻转、残影效果时有个明确的“视觉层”。GroundCheck 是地面检测用的传感器我会在跳跃那一节详细讲。它的位置应该放在角色的脚底比身体碰撞体略微靠下一点大小不用太大宽度是角色身体宽度的 60%-80% 左右高度可以调成 10-20 像素只要能稳定检测到地面就行。2.2 刚体类型与关键参数在 Player 节点上添加RigidBody2D后需要设置几个关键参数Type设为Dynamic动态刚体受重力影响能与静态刚体产生碰撞响应。这是角色最常用的类型。FixedRotation勾选角色撞墙后不会旋转。如果不勾你的人物撞到障碍物可能会像个盒子一样翻倒除非你是做“滚滚类”游戏一般不勾它。GravityScale默认是 1代表正常重力。想调跳得高一点或下落快一点可以调节这个值后面跳跃手感部分再说。地面、墙壁这类不动的物体用RigidBody2D的Type Static。Static 刚体不受任何力也不会被撞飞是场景地基的标准做法。如果你做移动平台才会用Kinematic它不像 Dynamic 那样受力但可以通过代码移动并且能推动站在上面的角色。2.3 碰撞体形状怎么选2D 碰撞体最常用的三个是BoxCollider2D、CircleCollider2D和PolygonCollider2D。人物身体大多数 2D 像素角色或 Q 版角色用BoxCollider2D就足够调一下Size让它贴合身体。碰撞体稍微比图像小一点比图像大一点手感会好很多——这里往往是调试“莫名其妙被卡住”的关键因为很多人给角色画的碰撞体比视觉大得多。圆形角色或球类用CircleCollider2D。复杂地形、斜坡用PolygonCollider2D或分段 Box。有个细节3.8 里 2D 碰撞体组件本身就带有Tag、Group、Sensor等属性Sensor勾上后这个碰撞体不产生物理阻挡只负责触发回调。我们做跳跃的 GroundCheck就必须勾上Sensor这样角色脚底虽然一直碰到“地面”但不会被一层透明的小盒子撑起来。3. 水平移动把“移动意图”换算成刚体速度配好节点后就可以写控制脚本了。这一步最核心的是“输入监听”和“刚体速度”之间的配合。很多人习惯在update里直接判断“如果按了左键就设置 x 为 -1”这本身没错但如果按键状态管理得不好会出现人物转向迟钝、或者按一下跳两下的问题。3.1 键盘输入监听与状态变量我的做法是维护两个变量_horizontal表示水平输入方向取值 -1、0、1_jumpPressed表示“当前是否刚按下过跳跃键”。监听使用input.on(Input.EventType.KEY_DOWN, ...)和KEY_UP用一个布尔标记来接收跳跃按下事件而不是在update里用input.isKeyDown一把梭。原因是按键的按下是一个“瞬间事件”如果只在某一帧里检查很容易漏掉用事件回调把标记先存下来下一帧执行跳跃就不会漏。import { _decorator, Component, input, Input, EventKeyboard, KeyCode } from cc; const { ccclass, property } _decorator; ccclass(PlayerController) export class PlayerController extends Component { private _horizontal 0; private _jumpPressed false; onLoad() { input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this); input.on(Input.EventType.KEY_UP, this.onKeyUp, this); } private onKeyDown(event: EventKeyboard) { switch (event.keyCode) { case KeyCode.KEY_A: case KeyCode.ARROW_LEFT: this._horizontal -1; break; case KeyCode.KEY_D: case KeyCode.ARROW_RIGHT: this._horizontal 1; break; case KeyCode.SPACE: case KeyCode.KEY_W: this._jumpPressed true; break; } } private onKeyUp(event: EventKeyboard) { switch (event.keyCode) { case KeyCode.KEY_A: case KeyCode.ARROW_LEFT: if (this._horizontal -1) this._horizontal 0; break; case KeyCode.KEY_D: case KeyCode.ARROW_RIGHT: if (this._horizontal 1) this._horizontal 0; break; } } }这里有个小经验onKeyUp里要判断_horizontal -1再清零而不是直接赋 0。如果你按住 A 再按 D此时_horizontal是 -1松开 D 后不应该影响 A 的移动方向。虽然这个例子只有单方向输入时不会触发但养成“只在当前值是自己设置的状态下才清除”的习惯后面做组合按键时不会踩坑。3.2 设置 linearVelocity.x 而不是施加力直接在update中设置刚体速度是最稳定的平台跳跃移动方式update(deltaTime: number) { const rigidBody this.getComponent(RigidBody2D); if (!rigidBody) return; const v rigidBody.linearVelocity; const targetVx this._horizontal * this.moveSpeed; rigidBody.linearVelocity new Vec2(targetVx, v.y); if (this._jumpPressed) { this.tryJump(); this._jumpPressed false; } }这代码的意图很明确保留刚体的 y 轴速度跳跃、重力都由物理系统算只覆盖 x 轴速度。为什么不推荐applyForceToCenter因为它施加的是力角色速度会随着时间逐步增加停止按键后又因为阻尼逐渐减速手感会“飘”。对平台跳跃玩家来说他们更习惯按一下就立刻到指定速度、松开就立刻停用直接设置linearVelocity才符合这种“即停即走”的手感。如果你是做太空类、浮空类游戏希望角色有惯性那applyForce或降低linearDamping才更合适。方案没有绝对的好坏只是场景不同。3.3 人物翻转与视觉层级因为 Sprite 是 Player 的子节点翻转时可以直接改 Player 节点的scale.x视觉效果和碰撞体方向会一起翻转。对绝大多数对称角色比如 Q 版小生物来说这是最高效的写法// 移动到负方向时 this.node.setScale(-1, 1, 1); // 移动到正方向时 this.node.setScale(1, 1, 1);但注意如果你用的是非对称的角色比如一个手持长剑的人那么整个节点翻转会导致手里的武器方向也反过来看起来就别扭。这时候建议只翻转Sprite子节点或者把Sprite的scale.x做反向处理。碰撞体保持物理上的稳定视觉层只负责“看起来面向哪边”。这是渲染层和物理层分离的一个最小例子。4. 跳跃检测“是否能跳”比跳本身更重要跳跃逻辑的核心其实不是“让角色跳起来”而是“判断角色此刻能不能跳”。如果你的角色在空中也能触发跳跃就会实现“无限跳跃”这是平台跳跃里最不能接受的问题之一。4.1 地面检测的通用方案Sensor 碰撞体我首选的检测方式是在角色脚底放一个BoxCollider2D勾选Sensor挂到 GroundCheck 节点上。当这个传感器和地面碰撞体接触时说明角色站在地面上这时跳跃按键才有效当传感器离开地面时_groundCount减到 0说明角色已经在空中。这一段最关键的是用计数器而不是布尔值。如果你只用_isOnGround true/false在角色同时接触两块地面时会出问题当你从一块地面跳到另一块地面的瞬间onEndContact先触发把_isOnGround置为 false实际上角色另一只脚还踩着地就会导致站在两块地板接缝处无法跳跃。计数器方案则不同onBeginContact时_groundCountonEndContact时_groundCount--只要计数大于 0就认为还在地面非常稳。4.2 完整的跳跃实现在 PlayerController 里加入地面检测import { RigidBody2D, Collider2D, Vec2 } from cc; ccclass(PlayerController) export class PlayerController extends Component { property({ type: Collider2D }) groundCheckCollider: Collider2D null; property moveSpeed 400; property jumpSpeed 700; property airControlFactor 0.7; property enableAirControl true; private _groundCount 0; onLoad() { // 省略输入监听... if (this.groundCheckCollider) { this.groundCheckCollider.onBeginContact this.onGroundBegin.bind(this); this.groundCheckCollider.onEndContact this.onGroundEnd.bind(this); } } isOnGround(): boolean { return this._groundCount 0; } private onGroundBegin(selfCollider: Collider2D, otherCollider: Collider2D, contact: any) { this._groundCount; } private onGroundEnd(selfCollider: Collider2D, otherCollider: Collider2D, contact: any) { this._groundCount--; if (this._groundCount 0) { this._groundCount 0; } } private tryJump() { if (!this.isOnGround()) return; const v this._rigidBody.linearVelocity; this._rigidBody.linearVelocity new Vec2(v.x, this.jumpSpeed); } }注意groundCheckCollider需要在编辑器里把 GroundCheck 子节点上的 Collider 拖进去。这种显式拖引用比getComponent更清晰因为你可能有多个碰撞体直接getComponent(Collider2D)会随机拿到任意一个很可能拿错。跳跃本身只有一行把linearVelocity.y设成jumpSpeed。这里包含了“保留当前 x 速度”的逻辑所以角色水平移动和跳跃可以同时进行不会出现跳跃时把水平速度清零的玄学问题。4.3 跳跃手感参数估算与细节调优跳跃高度可以大致估算。Cocos 3.8 的 2D 物理默认重力加速度大约是980 px/s²像素每秒平方假设你给角色设置的GravityScale 1跳跃初速度是700 px/s那么最大跳跃高度大约是h ≈ v² / (2g) 700² / (2 × 980) ≈ 250 px这个公式很实用你不需要反复试跳跃高度先根据场景里的地面间距估算出需要的像素高度再反推jumpSpeed。比如你想跳 300 px那么v ≈ sqrt(2 × 980 × 300) ≈ 767 px/s直接填 770 就行。手感方面除了jumpSpeed我还会调节GravityScale想让角色“重”一点、下落快一点可以设成 1.2-1.5想跳得高、滞空久可以设 0.8-0.9。不要直接在物理设置里动全局重力因为你可能还有其他物体需要不同重力表现。airControlFactor空中转向时的移动速度系数。平台跳跃里我不想让角色在空中像地上一样灵活所以空中的水平速度只乘 0.7。这个参数能解决“跳跃后控制过度”的问题比直接禁止空中移动的手感好很多。跳跃缓冲玩家按住跳跃键快要落地时如果提前按了跳跃系统可以先记下“玩家想跳”这个意愿落地后的 100ms 内自动执行跳跃。这个优化能让手感明显更跟手很多“我明明按了空格却没跳”的抱怨都是缺这个缓冲导致的。5. 碰撞与触发分组矩阵、Sensor 与回调注册人物控制到这里角色已经能跑能跳了。接下来要面对的是碰撞触发踩怪扣血、捡金币、触碰机关、进入传送门等等。这部分的核心不再是“物理阻挡”而是“事件通知”。5.1 “碰撞”和“触发”在物理上的区别在 Cocos 3.8 的 2D 物理里所有Collider2D组件都会产生碰撞事件区别在于是否产生物理响应默认状态Sensor不勾选两个带刚体的碰撞体接触后会产生物理力互相阻挡。比如角色踩到地面、撞到墙就是这种状态。勾选Sensor碰撞体仍然能检测“接触发生”但不会产生实际阻挡效果。角色可以穿过它同时你依然能收到onBeginContact/onEndContact回调。所以做金币、奖励物品时把金币的碰撞体勾上Sensor玩家走过时会触发“拾取”事件又不会被金币挡住。做出口、传送门也同理。5.2 分组和碰撞矩阵怎么配在 Cocos Creator 编辑器里打开Project Settings - Physics - Collision Matrix你会看到一个分组矩阵。先把分组定义好比如分组名用途DEFAULT默认组给没有特殊逻辑的物体GROUND地面、墙、平台PLAYER玩家角色ENEMY敌人物体TRIGGER触发器、道具、机关在“分组管理”里新建这些组之后回到碰撞矩阵把允许互相碰撞的组对勾上。比如PLAYER与GROUND勾选角色能站在地面上。PLAYER与ENEMY勾选角色接触敌人会触发伤害。PLAYER与TRIGGER勾选角色接触道具会触发拾取。这里要记住一个关键点如果你想让碰撞回调触发碰撞矩阵必须允许这两个组碰撞否则物理引擎根本不会计算它们之间的接触。我见过不少人的金币Sensor设好了代码回调也挂了但运行后什么都不触发最后发现是碰撞矩阵的PLAYER和TRIGGER没有勾选。5.3 回调注册的两种方式Cocos 3.8 的碰撞回调有两种注册方式第一种是给单个碰撞体直接赋值回调适合“这个角色由我自己控制”的场景const collider this.getComponent(Collider2D); collider.onBeginContact this.onBeginContact.bind(this); collider.onEndContact this.onEndContact.bind(this);第二种是使用物理系统的全局回调适合“所有碰撞都由同一套代码处理”的场景import { PhysicsSystem2D, Contact2DType } from cc; PhysicsSystem2D.instance.on(Contact2DType.BEGIN_CONTACT, (selfCollider, otherCollider, contact) { // 全局碰撞开始 }, this);我个人的建议是让每个功能组件各自注册自己的回调用otherCollider.group判断碰撞对象是谁这样代码职责清晰不会出现一个全局回调堆满 if else 的情况。金币拾取的一个最小例子import { _decorator, Component, Collider2D } from cc; const { ccclass } _decorator; ccclass(Coin) export class Coin extends Component { onLoad() { const collider this.getComponent(Collider2D); if (collider) { collider.onBeginContact this.onBeginContact.bind(this); } } private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D) { // 金币不关心具体是谁碰到了碰到就消失 this.node.destroy(); } }如果想做更严格的判断比如只允许玩家拾取可以检查otherCollider.group PLAYER_GROUP或者otherCollider.getComponent(PlayerController) ! null。5.4 踩怪伤害需要判断接触方向踩怪是横版游戏最常见的机制但很多人在刚实现时都会写错接触敌人就扣血结果从侧面碰一下也扣血。实际需求通常是“从上方踩到才有效侧面碰到则玩家受伤”。可靠判断方式接触瞬间看玩家的刚体速度 y 轴方向。如果linearVelocity.y 0说明玩家在下落过程中碰到敌人属于“踩踏攻击”否则就是侧面或下方碰撞判定为受伤。private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: any) { if ((otherCollider.group ENEMY_GROUP) 0) return; const rigidBody this.getComponent(RigidBody2D); if (rigidBody rigidBody.linearVelocity.y 0) { // 视为踩踏敌人被消灭或玩家反弹 otherCollider.node.destroy(); rigidBody.linearVelocity new Vec2(rigidBody.linearVelocity.x, this.jumpSpeed * 0.6); } else { // 玩家受伤逻辑 } }这里“玩家反弹”是额外手感优化踩到敌人后不是直接下落而是给一个较小的跳跃速度让角色弹起来很容易产生连踩效果。注意这是基于速度判断的简单写法更精确的做法是拿接触点坐标和玩家/敌人中心点做方向判断毕竟某些情况下角色上升途中也可能有接触。但作为第一版速度判断已经足够简单可用。6. 实测踩坑我把四类问题按“最高频”排了个序如果你已经按上面的步骤搭完应该能跑通基本流程了。但实际开发中真正花时间的是各种“莫名其妙”的问题。这里把我反复踩过的四类坑归纳一下附上排查思路和解决方案。6.1 回调就是不触发场景里毫无反应这是遇到最多的问题。我的排查顺序是现象可能原因解决方案两个碰撞体完全不接触穿透2D 物理模块没启用检查Project Settings - Feature Cropping是否勾选 Physics 2D碰撞体有接触但没有回调碰撞矩阵中 Group 对没有勾选打开Collision Matrix确认两个组之间勾选了只有一方是 Static另一方也是 Static两个 Static 刚体不会产生物理接触事件至少一方改成 Dynamic 或 Kinematic回调触发了但otherCollider不是预期对象分组设置混乱给游戏物体分配独立分组打印otherCollider.group验证在调试碰撞问题时先把编辑器的“物理调试绘制”打开一般情况下能看到碰撞体的轮廓。Cocos 3.8 的编辑器工具栏或场景面板里有一个Physics相关的调试开关打开后运行场景时碰撞体会以绿色或蓝色线框显示。如果线框完全没显示说明物理系统没生效或碰撞体被禁用如果线框位置和视觉明显对不上那就是碰撞体大小偏移问题。这一步能帮你把八成问题直接定位。6.2 跳跃像是有延迟或者按住一个键就连跳跳跃延迟通常不是代码慢而是物理步长和渲染帧不同步造成的。物理回调onBeginContact不一定会立刻在下一帧更新你的_groundCount如果你在“刚好离开地面”的那一两帧里按了跳跃角色可能已经离地但_groundCount还没归零系统允许了第二次跳于是看起来像“空中二段跳”。反过来的情况是刚刚落到地面物理回调还没到你按跳跃没反应就感觉延迟。处理跳跃延迟我建议给“跳跃意图”加一个小的等待窗口。不要把tryJump()放在按键事件的同一帧立即执行而是记录一句“我按过跳跃了”让它在未来 50-100ms 内持续有效如果这段时间内isOnGround()变成 true就执行跳跃。这样既不会误跳也消除了物理回调时序带来的延迟。处理“按一次跳两下”最常见的元凶是按键长按触发。键盘事件里如果玩家按住空格不放系统会周期性发送KEY_DOWN导致每周期都触发一次跳跃。我上面代码里用_jumpPressed标记并在update消费后立即清零就是为了避免这个问题。如果你的游戏必须支持“按住跳跃键自动连续跳”那就不该用_jumpPressed而是用isKeyDown配合冷却时间来控制频率。6.3 角色像被吸在地面上跑起来发黏这个问题多半出在碰撞体的尺寸或物理材质上。角色身体碰撞体如果和地面接触面太大摩擦力和法向力都会异常导致移动速度上不去。检查两点角色身体BoxCollider2D的Size是否明显大于视觉如果是把它缩小到略小于图形边缘。地面和角色是否不小心加了PhysicsMaterial2D如果摩擦系数设得很大角色跑动时会发黏。调试时先把摩擦系数调成 0确认不是摩擦力问题后再慢慢加。另一个常见情况是不平整的地面边缘有锯齿两个 Box 碰撞体之间产生细微的“台阶”角色跑过去像是被一粒粒小沙子卡住。解决方法是地面尽量用长条形的 Box 而不是很多个小 Box 拼接或者用PolygonCollider2D/ 链条碰撞体做完整的地形边。6.4 高速移动时穿墙、穿地板穿墙的本质是物理步长是固定的在一次物理步内角色位移超过了碰撞体厚度引擎没有捕捉到碰撞。这在高跳跃速度、高移动速度、薄碰撞体的情况下最容易出现。处理方法降低单帧最大速度把moveSpeed、jumpSpeed控制在一个合理范围。比如你的角色物理体高度是 100px别让它一帧内位移超过 50px。给碰撞体加一点厚度。薄到 1px 的墙几乎必穿给关键碰撞体设成 5-10px 厚度体验上感知不到却能显著减少穿透。如果确实需要高速移动可以检查RigidBody2D是否提供了bullet或连续碰撞检测相关选项。Cocos 3.8 的 2D 物理封装里高速物体的连续碰撞检测效果有限我的经验是优先限制速度上限比依赖碰撞检测更可靠。7. 从人物控制到真实玩法的落地思路这套人物控制跑通后你已经有了一个稳定的基础可以往外扩散玩法了。我最后简单分享几个扩展方向都是基于上面这套体系的自然延伸。7.1 移动平台与传送带移动平台可以用RigidBody2D的Kinematic类型写脚本移动它。这样角色站在平台上时会被平台带着走不会出现“平台走了角色还留在原地”的割裂感。注意给平台加一个和角色接触的碰撞体分组矩阵里做好配置。7.2 敌人 AI敌人本身的移动逻辑可以和玩家共用同一套“设置linearVelocity”的思路只是在状态机里做巡逻、追击、攻击切换。碰撞伤害用前面踩怪的代码扩展给敌人也挂上PlayerController的isOnGround配合检测就能做“踩敌人弹跳-再踩-再弹跳”的连击手感。7.3 渲染层与物理层的解耦如果你后面要做受击闪白、无敌闪烁、残影、影子拖尾等效果最好在开始写代码时就保持“物理实体节点”和“视觉表现节点”分离。PlayerController 只负责给物理节点设置速度和接收碰撞回调视觉节点由动画/特效系统驱动。这个习惯可以避免以后为了做表现效果在移动代码里塞入大量和物理无关的逻辑。动手做了这个项目后我最大的感受是2D 人物控制的难点不在 API而在“怎么看穿问题”。控制代码只负责把输入翻译成速度碰撞回调只负责记录状态所有依赖上一帧的状态变量尽量用计数而不是布尔量。你在自己的项目里如果碰到奇怪的表现先别急着打日志打开物理调试绘制把碰撞体的形状和位置看清一半问题当场就能看到答案。剩下的一半大部分也都出在分组矩阵和回调时序上按上文顺序排查就行。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价