Unity第三人称控制器搭建:动画混合树+Cinemachine实战指南
做第三人称游戏时角色控制器的搭建是最能拉开开发体验差距的环节之一。我在好几个项目里都踩过同一类坑角色能跑能跳但动画和移动数据对不上镜头一转就晕一进障碍区相机穿墙。后来把动画混合树、Cinemachine这套组合彻底摸透才算是真正把第三人称手感做顺。这篇就我自己的实操记录把我怎么搭第三人称角色控制器、怎么调动画混合树、怎么配置Cinemachine等插件功能的过程拆开讲顺带把那些文档里一般不会写的坑也一起列出来。适合正在做第三人称Demo的Unity开发者尤其是从2D转3D、或者刚开始接触动画状态机和相机的朋友。1. 先想清楚第三人称控制器到底难在哪1.1 三个绕不开的难点第三人称控制器看着简单实际上手才会发现问题的关键不在“让角色往前走”而在“怎么走才不像在滑冰”。第一个难点是输入方向与相机方向的换算。键盘上的W是“朝相机前方走”而不是“朝世界坐标的Z轴走”。相机一转输入方向就要跟着变这里存在严格的三角函数换算关系。很多新手直接拿Input.GetAxis乘以世界坐标轴结果相机转90度后按W角色往侧向跑观感非常割裂。第二个难点是动画状态与移动数据的一致性。角色在游戏里移动了多少距离动画里播放的位移步幅必须匹配。跑步动画一秒能让角色看起来移动6米如果你实际速度只有2米/秒角色就会像在原地踩跑步机。这个匹配关系需要同时调整动画师节奏、混合树阈值和代码里的速度参数。第三个难点是相机第三人称的“空间感”。相机不能死跟在角色背后它会旋转、会靠近、会避障。如果相机逻辑写得太硬墙体一挡屏幕直接变黑写得太软镜头乱晃又让玩家头晕。这块用纯手写代码成本很高Cinemachine的出现就是专门解决这类问题的。1.2 自己写控制器还是用现成插件做这个选择我建议用“性价比”来权衡。数量多、需求刚起步可以直接用资源商店里那些热门第三人称控制器几分钟跑通但后续改动画、改按键、改相机逻辑时你要接受它的黑盒逻辑改起来容易牵一发动全身。项目需要定制手感、联机同步、特殊移动攀爬、闪避、滑铲必须自己写控制器逻辑但没必要从零写相机和动画混合动画混合树和Cinemachine都是Unity内置能力和官方扩展直接用就行。学习目的更推荐自己动手搭一遍这比装任何插件都值。因为这块搞懂了Animator、Input System、Cinemachine三块都有扎实基础。我自己的习惯是移动和物理判断用CharacterController组件动画表现用Animator 混合树相机直接交给Cinemachine FreeLook。这套组合的好处是各层职责清晰出问题时能快速定位是哪一层的锅。1.3 整体架构怎么分层我始终建议把控制器分成三层输入层、逻辑层、表现层。输入层负责把玩家的按键、摇杆、鼠标输入统一转成一个干净的输入向量逻辑层负责把输入向量结合相机方向计算出最终的世界空间移动方向、速度、法线、碰撞结果表现层负责把这些数据喂给Animator参数和Cinemachine相机让角色“看起来”在走、在跑、在转向。这样分层后你会发现调手感时不用碰逻辑代码只改参数换表现时不用重写移动逻辑只用改动画或相机。后续想加冲刺、跳跃、下坡滑行也只是在逻辑层增加几个状态表现层加几个动画片段。2. 移动逻辑的核心实现输入、方向与平滑参数2.1 新输入系统的接入方式我现在的项目基本都用Unity的新输入系统Input System和旧的Input Manager比好处是支持手柄、键盘、触屏的抽象统一还能用InputAction资产做映射。在PlayerInput组件上配置Move动作Vector2类型然后写一个接收方法public void OnMove(InputAction.CallbackContext context) { inputVector context.ReadValueVector2(); }这块有个小坑如果你用键盘按键值本来就是即时的没问题但如果用手柄摇杆默认有死区Input System可以自动处理这个死区别自己再做一层归一化容易把手感搞飘。2.2 相机相对方向的换算这是整个控制器的关键数学点。我们的目标是输入向量x对应左右y对应前后叠加相机朝向输出世界空间这个角色该移动的方向。我用的是最简单也最常见的写法Transform cameraTransform Camera.main.transform; // 将输入向量从相机本地空间转换到世界空间 Vector3 moveDir cameraTransform.TransformDirection(new Vector3(inputVector.x, 0f, inputVector.y)); moveDir.y 0f; moveDir.Normalize();这里要理解一点相机的TransformDirection相当于把“相机自己的前方、右方”当成基准去解释输入向量的前后左右。这样不管你相机怎么转、角色怎么跑玩家按W角色永远朝屏幕正前方跑。我在实际项目里还会把y清零再Normalize因为相机是有俯仰角的。不清零的话你按W跑角色会带着一个向上的分量CharacterController的Move会把角色往天上推。这也是新手最爱踩的隐形坑。2.3 速度、转向与加速度的参数设计移动手感好不好和这三个参数关系最大移动速度MoveSpeed决定角色的最大移动速度。常见第三人称动作游戏里步行大约2~3米/秒跑步5~7米/秒。加速度Acceleration决定从静止到最大速度的快慢。数值太大会让人觉得“轻型飘”太小又觉得“铅球附体”。我一般给10~20。旋转速度RotationSpeed角色身体转向的角速度单位是度/秒。跑动中转向太快会像拧螺丝太慢又会有刹车延迟感。我常用400~720度/秒。角色身体的转向我从来不直接用角色朝向直接朝向移动方向而是用Quaternion.RotateTowards做限速旋转。这样可以模拟人体转向的自然延迟手感比硬转向好非常多。if (moveDir.sqrMagnitude 0.01f) { Quaternion targetRotation Quaternion.LookRotation(moveDir); transform.rotation Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); }速度这块直接用CharacterController.Move驱动角色体感位置用当前速度和最大速度的比值去驱动动画参数而不是拿动画来控制角色位置。这是防止动画与逻辑“打架”的标准做法。完整移动脚本大概是这个形态[RequireComponent(typeof(CharacterController))] public class ThirdPersonController : MonoBehaviour { [Header(移动参数)] public float moveSpeed 6f; public float acceleration 15f; public float rotationSpeed 600f; private CharacterController controller; private Transform cameraTransform; private Vector2 inputVector; private Vector3 currentMoveVelocity; private float currentSpeed; void Start() { controller GetComponentCharacterController(); cameraTransform Camera.main.transform; } public void OnMove(InputAction.CallbackContext context) { inputVector context.ReadValueVector2(); } void Update() { Vector3 moveDir cameraTransform.TransformDirection(new Vector3(inputVector.x, 0f, inputVector.y)); moveDir.y 0f; moveDir.Normalize(); if (moveDir.sqrMagnitude 0.01f) { Quaternion targetRotation Quaternion.LookRotation(moveDir); transform.rotation Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); } float targetSpeed moveDir.magnitude * moveSpeed; currentSpeed Mathf.Lerp(currentSpeed, targetSpeed, acceleration * Time.deltaTime); controller.Move(moveDir * currentSpeed * Time.deltaTime); // 给动画系统提供参数 animator.SetFloat(Speed, currentSpeed); float directionAngle Vector3.SignedAngle(transform.forward, moveDir, Vector3.up); animator.SetFloat(Direction, directionAngle); } }注意动画参数Direction算的是“角色的正前方”与“实际移动方向”之间的夹角。有了这个夹角混合树才能知道角色是在向前、斜前、侧向、还是后退。这也是1D混合树做不到、2D混合树能覆盖的关键信息。2.4 斜坡与地面法线的处理CharacterController自带简单的斜坡处理但如果你只想让角色在平坦地面走不做攀爬默认就够用。如果你希望角色能上坡记得把Move的向量投影到地面切线方向。我记得在某个项目里出现过“角色在坡顶弹跳”的Bug排查到最后是Controller的高度和当前坡面法线共同作用导致的微小穿透反弹。后来用Physics.Raycast往下探出地面法线把移动向量投影到地面上就正常了。这段代码可以直接加在Update里if (Physics.Raycast(transform.position Vector3.up * 0.1f, Vector3.down, out RaycastHit hit, 2f)) { Vector3 projectedMove Vector3.ProjectOnPlane(moveDir, hit.normal); controller.Move(projectedMove * currentSpeed * Time.deltaTime); } else { controller.Move(moveDir * currentSpeed * Time.deltaTime); }3. 动画混合树让角色动作不再“切豆腐块”3.1 为什么非要用混合树动画状态机单独用会有一个很尴尬的问题角色从Idle切到Walk如果直接切换动作像两段视频硬拼接人物肌肉线条是断的。用CrossFade可以缓解但CrossFade解决不了“相同状态下的不同幅度”问题比如走路速度从2变为5单独的状态机要做多个状态来对应别提多烦了。混合树就是为这个场景设计的。它本质是一个多维插值空间你有多个动画片段每个片段姿势的权重由一个或多个参数决定。参数变了权重连续变化动画自然平滑过渡。所以我的Animator结构里一般都只有一个Locomotion状态里面挂一个2D混合树。这个状态常驻角色进入移动表现全靠混合树实时插值。3.2 1D混合树还是2D混合树如果你的角色只做“不跑/走路/跑步”这种线性移动1D混合树就够了。一个Speed参数0是Idle2是Walk6是Run中间阈值自动插值。但第三人称动作游戏通常要处理后退和侧移这时候1D完全不够。你想表达“角色边后退边开火”的状态退后动画和侧移动画要同时参与插值就需要2D技能去处理方向。我的配置经验是移动水平不高、只做Demo先用1D混合树参数Speed最快速度搞通动画和移动的匹配。要做完整第三人称战斗、或想表现真实移动姿态直接上2D混合树Screen Space / Freeform Directional Cartman 风格。参数用Speed和Direction两个float。Direction参数的范围是-180到180-180/180表示正背后0表示正前方正负90是左侧和右侧。3.3 2D混合树的具体配置和阈值设定以“Idle / 向前走 / 向前跑 / 向左跑 / 向右跑 / 向后走”六片段为例IdleSpeed0Direction0向前走Speed2.2Direction0向前跑Speed6Direction0向左跑Speed5Direction-90向右跑Speed5Direction90向后走Speed-2或者你取绝对值但保持负值表示后退这里如果Direction用角度建议把阈值写成整十数且保持对称。我见过有人把向左设为-80、向右设为90结果角色转了一圈回来混合树权重乱跳动画像抽风。混合树内部会自动做权重插值每个运动位置的动画会根据距离参数空间内的距离来计算权重。距离越近该动画的影响越大。这个插值算法很成熟不用手写。3.4 状态机里如何安排混合树Animator结构我建议这样默认状态Locomotion挂在2D混合树。其他状态如Jump、Roll、Attack都可以从Any State进入用Exit Time或者条件触发。返回Locomotion时设置一个较短的过渡时间比如0.1秒避免大招结束后角色在0.001秒内从攻击动画硬切回跑步动画。另外一个容易被大家忽略的点混合树的动画片段导入设置。每个动作的循环时间必须勾选Loop Time不然动画播放一遍就停在那混合树权重再光滑也救不了。3.5 动画速度与真实移动速度的匹配动画师给的动作内部自带位移速度。举例一个“跑步”动作在1秒内角色模型前进了6米这代表动画官方速率。如果你代码里的移动速度也是6正好匹配视觉上脚和地面没有滑动。但这个数字往往不是公开的只能用经验去测。我的测试方法很土但好用把角色模型放在地面网格上播放跑步动画同时看代码里的速度值在地面放一个测距物体跑5秒看模型位移多少。然后调moveSpeed参数让位移量和动画速率一致。动画位移低于代码速度角色就是滑冰动画位移高于代码速度角色就是原地踏步甚至拖脚。混合树里的每个动画片段设置“速度”参数对应动画资源里Motion属性还可以在代码里按当前动画动画Clojure的spped乘以一个multiplier。不过我在多数项目里只是调整代码的moveSpeed就够用混合树内部插值本身就会平滑过渡这个速率。4. Cinemachine 相机从“跟住”升级到“跟好”4.1 为什么用Cinemachine而不是手写相机跟随手写相机跟随很简单LateUpdate里让相机位置等于角色位置加个offset就行。但这样做有两个问题一是相机没有阻尼角色转个身相机瞬间甩过去玩家容易“晕镜头”二是相机穿墙没有任何处理一进走廊就一片漆黑。Cinemachine把这些常见问题全部内置了。它把相机拆成了Body决定相机位置和Aim决定看哪里两个逻辑模块还提供了阻尼、碰撞、多相机切换等扩展。相当于别人已经把相机工程化你不用重复造轮子。4.2 FreeLook第三人称相机的默认选择第三人称控制器最适配的是Cinemachine FreeLook这个相机类型。它沿着角色周围生成三条轨道Top、Middle、Bottom每一条轨道有独立的半径和高度。相机滑动在三条轨道之间玩家在高低起伏地形上跑时镜头会自然抬高压低。关键参数我建议这样设Follow角色的根节点包含CharacterController的物体一般不要直接放相机到角色头部方便统一逻辑。LookAt角色胸部或者头部的一个空物体。LookAt决定相机注视点我一般放在角色胸腔因为放头部会导致镜头随着头部动画晃动容易晕。Top轨道半径小一些比如3米高度高一些适合近距离特写和抬头看。Middle轨道半径中等比如4米高度中等适合常规视角。Bottom轨道半径可再大一点5米高度低适合把角色背影拍得高大也方便看天空。Body模块里最影响手感的是Damping参数X Damping决定左右方向的跟随延迟0.5~1之间比较顺。Y Damping决定上下方向的跟随延迟可以比X稍大一点比如1.0。Z Damping是前后距离的变化率给1.0~1.5避免快速后退时相机猛撞角色。4.3 输入轴的配置调整视角的“手感曲线”FreeLook默认有两条输入轴X轴控制绕角色旋转Y轴控制轨道高度。这两条轴对应的就是手柄右摇杆或鼠标移动。在Cinemachine FreeLook的Input Axis Controller里可以设置X轴的最大左右速度Max Speed我一般设250~300。Y轴的最大速度Max Speed给10~15因为玩家通常只想微调俯仰不像旋转那样高频率。加速度和减速度可以调节相机转动的“跟手感”加速给得快跟手加速给得慢飘逸。如果你用的是鼠标控制视角记得关闭Cinemachine的硬件输入或自己做映射不然会出现在编辑器里能转、打包后鼠标不动的尴尬情况。4.4 相机碰撞防止穿墙的正确姿势Cinemachine处理穿墙靠的是CinemachineCollider扩展。给FreeLook挂上CinemachineCollider后还需要再挂一个CinemachineCameraOffset或者设置碰撞剔除层。具体操作是给相机加CinemachineCollider组件。设置Collide Against为环境碰撞层Environment、Obstacle。设置Radius为0.1~0.3相当于胶囊体包围盒检测墙面边缘。在Body的Framing Transposer/FreeLook的Orbit里开启Minimum Distance From Target给一点最小距离防止相机被挤到角色身体里。这个配置我折腾了很久最难调的其实是碰撞后的相机“回弹”速度。CinemachineCollider本身有个Avoid Clip的机制它会在射线命中时收缩相机距离障碍物离开后再逐步放开。要是不做阻尼角色一转身背靠墙相机会猛地冲到头顶那个瞬间真的能把人晃吐。4.5 多相机切换与Blend很多关卡不是只有第三人称一种镜头。Cinemachine支持多个相机之间的层级切换每个相机可以有一个Priority值。切换时CinemachineBrain会自动做Blend也就是两个相机之间的插值过渡。这个用在剧情演出、进入掩体、死亡回放等场景非常顺手。比如死亡瞬间切换一个慢镜头低机位相机CinemachineBrain会自动从FreeLook平滑过渡到死亡镜头的相机比自己写切换省太多事情。5. 实操环节从空场景到能跑Demo的完整流程5.1 搭场景与角色基础我建议从空场景开始铺一个地面放几个墙壁盒体作为障碍物。角色模型不必太讲究用Unity自带的Capsule作为物理载体模型挂到Capsule下当子物体或者直接把Avatar模型放上去让Animator组件管它。地面记得设好Layer相机碰撞和角色物理都要用层来做区分。我一般把光照、环境放到Environment层角色放到Player层这样后续各种detect和collision都很干净。5.2 添加CharacterController并挂完整移动脚本给Capsule挂CharacterController高度2半径0.4Center略微上移保证模型脚底贴地。然后挂我之前写的那段控制脚本并把Animator引用赋值进去。注意Capsule的中心不要歪了否则角色脚会悬空或者陷地下。我习惯加一个简单的Gizmo或检测Debug.Log去打印脚底位置观察是否贴地。5.3 创建Animator Controller并配置2D混合树右键创建Animator Controller命名Locomotion。进入Animator窗口创建一个Blend Tree节点双击进入把它设置为2D Simple Directional参数选择Speed和Direction。然后按3.3节的阈值表添加6个动画片段。没有动画资源的话大家可以从Unity标准资源包找一个带第三人称老动画模型或者用商店里免费动画。注意动画片段必须是带有“人形或通用骨架”的不然Animator识别不了。把默认入口箭头给这个Locomotion状态退出过渡不用管因为这是常驻状态。5.4 添加Cinemachine FreeLook并绑定在菜单里Cinemachine - Create FreeLook Camera。会自动生成CinemachineBrain在相机上同时创建FreeLook相机。设置FreeLook的Follow为角色CapsuleLookAt为角色胸部空物体然后在轨道和阻尼参数里填我前面给的初始值。再给FreeLook挂上CinemachineCollider并配置Layer。最后把Input Action里“控制视角”的映射接到FreeLook的X/Y Axis上一般用鼠标Delta或右摇杆。5.5 联调把动画参数和移动脚本对接回到移动脚本在Update里已经写了animator.SetFloat(Speed, currentSpeed)。还有一行animator.SetFloat(Direction, directionAngle)。这时要注意一个映射问题混合树的Direction参数单位是角度但你代码里Vector3.SignedAngle返回的也是角度-180到180正好对应。如果混合树用的是“横纵坐标”类型比如Freeform 2D Cartesian那就需要你把Direction换算成横坐标映射的-1到1而不是角度。我强烈建议用Simple Directional类型配合角度直接映射最省事。5.6 详细测试与调参清单跑起来之后至少测这些情况按W直跑看Speed参数能否稳定在6左右角色脚与地面是否相对静止。按住A侧跑看Direction参数是否稳定在-90角色能否保持侧身移动。同时按W和A看混合树权重是否平滑角色是否会先转过去再直跑。相机绕角色旋转360度按W直跑看角色方向是否正确跟随相机变化。调这些参数的时候我建议用Animator窗口的Parameters面板实时看值。你一边按键盘一边看数据比事后导日志直观太多。6. 常见问题与排查思路实录6.1 角色原地打转、无法正常直行这个九成是Direction参数没同步好或者混合树的阈值方向不匹配。检查顺序先看代码里的directionAngle是否正常输出再看Animator窗口里的Direction参数是否在正确的范围内变化最后看混合树里的运动位置和参数方向是否一致。最典型的错法是把Forward运动放在90度但代码输出的是角度0角色一跑就是横着飘。6.2 动画和移动速度不匹配角色滑冰这个我在3.5里提过核心就是代码速度与动画速率的比值。先跑一遍纯移动测试把混合树调到只有一个跑步动画锁死Direction和Speed看角色每帧移动距离和时间除出实际速度。再对照代码里moveSpeed值去改。如果动画跑得快、代码跑得慢就调高moveSpeed反过来就调低。还有一个容易忽视的点动画导入设置里的Sample Rate采样率。如果动画采样率太低比如30fps动画锯齿感明显匹配精度也会受影响。尽量用60fps或更高的动画资源。6.3 相机穿墙后卡在障碍物内部CinemachineCollider能处理大部分墙体但遇到薄墙、高墙组合会失灵。排查思路第一确认CinemachineCollider的Collide Against层里面包含环境层第二把Radius调大一点点避免太小穿过薄墙第三检查FreeLook三条轨道的半径如果Middle轨道半径太小相机离角色太近碰撞半径虽然检测到了但相机还是会被卡在障碍物边缘。实在不行可以额外给相机加一个“粗碰撞”的逻辑用Unity Physics.Raycast从角色中心向相机位置打一条射线探测到障碍就把相机往前拉近物体移开后让相机平滑回到原轨。6.4 手柄输入方向错乱这个问题常见于新输入系统接入后没有把按键映射的Y轴方向同步。比如手柄摇杆前推时InputAction返回的Vector2.y是正还是负取决于你的Action资产配置。如果你在代码里直接把y映射到世界坐标的前后就会遇到“推上是后退”的情况。我的做法是在Input Action资产里把Move动作的Vector2 Y方向按实际期望设好或者在代码里加一个可翻转的bool参数发布平台不同就切换一下。不要小看这个小东西它能让测试人员非常绝望。6.5 相机抖动和画面闪烁多半是因为相机更新时机不对。Cinemachine Brain默认在LateUpdate里工作这是对的。但如果你手写移动相机的逻辑放在Update而Brain在LateUpdate角色先动、相机后动就不会有位置闪烁。如果反之角色还没动相机就先动了就会出现闪烁。检查方法很简单把所有相机相关的移动、旋转都放在LateUpdate或者依赖Cinemachine默认流程不要在Update里又改Cinemachine相机的位置。如果你在代码里给角色模型做动画同步也要在LateUpdate里读取最终位置再同步。6.6 混合树切回Idle时跳变这个很常见原因是混合树里Idle和Walk之间的过渡太短或者Key值设置太密导致Idle权重还没完全消失Walk已经强行拉到高位。解决方法是把Idle运动位置的阈值设定得离最近Walk足够远比如Idle在Speed0Walk在Speed2.2。混合树内部插值会保证在Speed0时Idle权重接近1Walk权重接近0。如果你觉得视觉上还是有跳可以给Animator的状态过渡加0.05~0.1秒的过渡时间让权重变化更柔和。7. 性能优化与手感细节补充7.1 调试阶段就该注意的性能隐患第三人称控制器最容易产生性能坑的其实是相机检测和碰撞判定。CinemachineCollider默认会对相机和障碍物做多次射线检测检测频率不低。如果你的场景建筑复杂、墙又多每帧几十条射线很可能把CPU烧掉。我一般这样优化把环境障碍物作Static Batch。Collider检测的Radius调成0.3左右太大会频繁误触太小会漏检测。CinemachineCollider的Strategy选择Dynamic Camera Collider模式这个模式对动态障碍物处理更好但静态场景下开销也高。静态场景用默认的Static Obstacles模式更省。7.2 动画层的开销控制混合树节点不要过多常见第三人称移动用6~8个片段足够。如果你硬塞20个移动片段进去每一帧都要算20个权重信息量大但效果并不会更好。另外Animator组件开启Animator Only When Visible可以省移动端的开销但注意角色在屏幕边缘但还没完全出镜时动画会暂停有时会露出破绽。调这个属性前先确认你的游戏是否允许边缘模糊。7.3 LateUpdate、FixedUpdate与Update的配合节奏我的固定节奏是Update读取输入、计算移动向量、更新Animator参数。FixedUpdate处理CharacterController.Move相关的物理移动如果有斜坡、碰撞特别依赖物理把修改放在这里。LateUpdate相机位置更新交给CinemachineBrain它默认在LateUpdate执行。如果你在Update里又更新相机又和FixedUpdate抢地方抖动的概率非常大。新手容易犯的一个错是把移动放到Update但又去读取Rigidbody速度导致刷新率不一致。7.4 手感曲线的小细节故意加延迟有时“完美跟手”并不等于“手感好”。比如角色在跑动中急停如果身体立刻停住视觉上很生硬。我会在移动代码里给目标速度围一个额外的加速度曲线让角色在急停时有一个极短的“减速缓冲”。这个缓冲不是线性插值而是在低速度端多花一些时间。做法很简单把Mathf.Lerp换成带有速度敏感度的函数float adaptiveAcceleration currentSpeed 1f ? acceleration * 0.3f : acceleration;当前速度很低时改变速率的节奏变慢角色能呈现出“站定”的真实质感。这个小技巧对动作类手感提升非常明显而且代码就一行。8. 我踩过的一个大坑动画混合树与Rigidbody的冲突这套方案里我最初踩过最惨的一次坑是把角色移动全部用Rigidbody驱动同时Animator层还开了Apply Root Motion。结果角色在斜坡上走动画自带的位移和Rigidbody移动不断争夺同一根身体角色先是被压到地下后来又突然蹦起来。排查了两个小时才发现是两个系统在“打架”。现在我的原则是“二选一”要用Root Motion做动作驱动那就不要用CharacterController或Rigidbody来直接管移动而是让动画的位移成为真实移动控制器只做方向输入和加速度层计算。要用物理移动那必须关闭Animator的Apply Root Motion只保留身体转向和姿态位置的最终来源交给物理组件。第三人称控制器我推荐后者物理移动是基础动画是外观。这样对操作手感可控性更高也方便联机同步。9. 这套方案能扩展到什么地方把移动、动画混合、相机这套搞通以后很多玩法都能顺理成章地在这之上加东西。冲刺加一个速度倍率和FOV变化相机阻尼降低一些启动瞬间的浮动感就很强。闪避翻滚用状态机里的Any State入场配合Cinemachine的虚拟相机切换做一个短暂延迟再回到Locomotion。攀爬和跳跃移动逻辑层增加对垂直速度分量的处理混合树增加空中参数。相机可以保持FreeLook不变但把Y Damping调小让上下跳跃更跟手。战斗系统方向参数会变得更复杂Direction不只是移动方向还要包含“锁定目标时的朝向偏转”。但整个架构不需要推翻只需要在逻辑层多出“面向目标”的优先级。我自己后面做某跨平台动作系统时就是在这套基础架构上加了状态机分层和攻击贯穿逻辑。移动、动画混合、相机基本没改动只是加轴参数和状态条目整个工程就活了。最后分享一个我个人的调参习惯每次调整控制器手感都只动一个参数然后跑同一段测试路线。先调速度再调转向再调相机阻尼最后才调混合树阈值。因为所有参数都会互相影响同时乱改出了问题没法定位。等你把这段路线跑顺再把参数套到不同游戏模式里稍微微调一下就行。这个流程看起来慢实际是最快的路。

相关新闻

Jev点击、Codex思考:完整解读jev-browser-use——让AI Agent快5-10倍的浏览器自动化Skill

Jev点击、Codex思考:完整解读jev-browser-use——让AI Agent快5-10倍的浏览器自动化Skill

【免费下载链接】jev-browser-use 5–10x faster browser operations: Jev clicks, Codex thinks and verifies. Built at EZCollegeApp. 项目地址: https://gitcode.com/gh_mirrors/je/jev-browser-use 点击查看 免费下载 jev-browser-use 是一个让 AI Agent 快 5…

2026/10/17 6:20:45 阅读更多 →
前端代码混淆实战:从Terser到javascript-obfuscator的攻防与取舍

前端代码混淆实战:从Terser到javascript-obfuscator的攻防与取舍

前端代码“裸奔”这个问题,干得越久越觉得不能回避。很多团队上线第一个版本时根本意识不到,自己写的 JavaScript 代码打包之后,逻辑几乎等于全部透明地交到用户手里。接口怎么调、签名怎么算、密钥藏在哪、业务规则是什么,只要能…

2026/10/17 6:20:45 阅读更多 →
从零搭建SQL注入靶场:Docker容器化环境与实战测试指南

从零搭建SQL注入靶场:Docker容器化环境与实战测试指南

1. 从“看教程手痒”到“自己动手”:为什么你需要一个本地注入靶场说实话,很多人学 Web 安全的第一步都是抱着《SQL 注入笔记》或者某个平台的视频课猛啃,看到 or 11 --这种经典 payload 就来劲。但真到实战面试题、护网简历或者自己复盘的时…

2026/10/17 6:19:45 阅读更多 →

最新新闻

Harbor私有镜像仓库部署与运维实践:从架构到避坑指南

Harbor私有镜像仓库部署与运维实践:从架构到避坑指南

一套业务系统跑了一两年,环境中几十上百个容器,镜像分散在各台机器甚至每个人电脑里,等到要统一发版、回滚版本的时候,才发现连一个准确的镜像清单都凑不出来。这种场景我想很多团队都经历过。所以当我的某位合作伙伴第一次提出“…

2026/10/17 6:54:01 阅读更多 →
Multi-Raft架构设计:从单Raft瓶颈到分布式分片扩展实践

Multi-Raft架构设计:从单Raft瓶颈到分布式分片扩展实践

1. 项目概述与核心目标如果你维护过一个采用 Raft 共识的分布式数据库,大概率会碰到这个场景:单组 Raft 的写吞吐量撑不住业务增长,CPU 和磁盘的扩展能力已经到顶,数据量稍微上来一些,日志复制的压力就像滚雪球一样越滚…

2026/10/17 6:54:01 阅读更多 →
AI大模型面试:RAG怎么处理PDF中的表格?AI不会管你的成本,不会管实现难度

AI大模型面试:RAG怎么处理PDF中的表格?AI不会管你的成本,不会管实现难度

文章目录 一、面试现场:一个问题暴露真实水平 二、表格为什么是RAG的"噩梦"? 2.1 切片即毁灭:表头和数据行被生生拆开 2.2 真实企业场景:表格远比你想象的复杂 2.3 网上那些"方案"为什么不切实际? 三、企业级三大解决方案(核心干货) 3.1 方案一:多…

2026/10/17 6:54:01 阅读更多 →
Linux-多文件工程

Linux-多文件工程

vsp 文件名 :分屏操作Ctrl ww :切换屏幕Ctrl w :平分大小wq:退出1. 其他 文件 .c只需包含自己的 .h 用 “ ” 包含2. 此时对应的 .h定义自己那一套 包别人 声明(结构体,函数)eg&#xff…

2026/10/17 6:54:01 阅读更多 →
西邮Linux兴趣小组2024纳新面试题

西邮Linux兴趣小组2024纳新面试题

西邮Linux兴趣小组2024纳新面试题 0.聪明的吗喽 猴子每走一米会消耗掉三根香蕉,分别是往前走一米会消耗一根,返回起点会消耗一根,再搬另外50根会再消耗一根,一共消耗三根,所以我们只需要找到一个中转站,使香…

2026/10/17 6:54:01 阅读更多 →
Linux本地NTP时间服务器搭建与高可用运维实践

Linux本地NTP时间服务器搭建与高可用运维实践

1. 项目概述:为什么你需要一个本地时间服务器,而不是直接靠公网NTP?“ntpd建立本地时间服务器供所有本地linux服务器使用”——这句话背后藏着一个被很多运维新人忽略的硬伤:时间不同步不是小问题,而是系统性风险的导火…

2026/10/17 6:53:01 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →