【Unity实战笔记】第二十二 · 基于SMB的角色控制中遇到的一些问题(斜坡移动鬼畜、落地卡顿、角色突进、头发动画失效等)

注: 本文紧接上一篇 Unity实战笔记 · 第二一,补录后续遇到的一些问题。

SMB无法使用awake,变量无法只进行一次初始化

解决办法:在OnStateEnter中添加入Initiate(animator)方法,Initiate中检测是否完成过初始化,有就直接跳过。

csharp 复制代码
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
    Initiate(animator);
}
// 只进行一次的初始化
private void Initiate(Animator animator)
{
    if (_playerInput != null)
    {
        return;
    }
    _playerInput = animator.GetComponentInParent<PlayerInput>();
    _playerController = animator.GetComponentInParent<PlayerController>();
    _playerTransform = _playerController.transform;
    _playerRig = animator.GetComponentInParent<Rigidbody>();
    _camTransform = Camera.main.transform;
}

这样就避免每次进入OnStateEnter都要GetComponent赋值一次变量,减少没必要的损耗。

下坡鬼畜


下坡鬼畜

鬼畜产生的原因在于:移动角色时没有考虑地面坡度,还是水平移动,导致在斜面上的运动实际上是 "水平移动→坠落→水平移动",这就产生鬼畜效果。

解决办法也简单,用射线检测检查斜面坡度,移动时基于斜面法线 即可。(原先默认是vector3.up

注意旋转还是基于vector3.up,否则移动后就垂直于斜面上了。

  • 获取斜面法线

    csharp 复制代码
    public Vector3 GetSlopeNormal()
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, Vector3.down,out hit,radius,layerMask))
        {
            return hit.normal;
        }
        return Vector3.zero;
    }
  • 角色移动

    csharp 复制代码
    protected void DoMoveInPhysics()
    {
        if (_playerInput.moveInput != Vector2.zero)
        {
            Vector3 moveInput = new Vector3(_playerInput.moveInput.x, 0, _playerInput.moveInput.y);
            // slopeNormal用于计算地面坡度
            var slopeNormal = _playerController.slopeNormal();
            // 相对主摄的移动(注意最后需要投影到水平面,否则会有上下位移导致镜头波动)
            Vector3 _camMoveWithSlope = Vector3.ProjectOnPlane(_camTransform.TransformDirection(moveInput).normalized,
                slopeNormal != Vector3.zero ? slopeNormal : Vector3.up);
            Vector3 _camMoveWithoutSlope = Vector3.ProjectOnPlane(_camTransform.TransformDirection(moveInput).normalized,Vector3.up);
            // 转向
            _playerRig.MoveRotation(Quaternion.RotateTowards(_playerTransform.rotation,
                Quaternion.LookRotation(_camMoveWithoutSlope), 30));
            // 移动
            _playerRig.MovePosition(_playerRig.position + _camMoveWithSlope * playerStateSo.runSpeed * Time.fixedDeltaTime);
        }
    }


下坡鬼畜修复

SMB脚本中public变量无法运行时修改

解决办法:将参数用ScriptableObject封装,运行时修改SO即可。

  • 添加Player_State_SO 脚本,标明CreateAssetMenu,然后右键添加SO文件

    csharp 复制代码
    [CreateAssetMenu(menuName = "Data/SO/Player_State_SO",fileName = "Player_State_SO")]
    public class Player_State_SO : ScriptableObject
    {
        public float jumpForce = 200f;
        public float runSpeed = 3f;
    }
  • 状态SMB中引入上面的SO

    csharp 复制代码
    public class Player_Base_SMB : StateMachineBehaviour
    {
    	[Tooltip("在project中右键添加对应SO,并在状态机状态中添加SO,那样运行时就可在SO中调整参数")]
    	public Player_State_SO playerStateSo;
    }

落地卡顿


落地卡顿

经过按帧观察,中间黑字出现的几帧比较卡顿。

原本 fall clip是 12-18.5帧,land是19帧-45帧。

我思考可能是 clip裁截不够合理。

因为降落速度比较快,着陆前几帧不应该紧接fall,中间抽掉两帧,利用人眼视觉暂留,使整体动能衔接更顺畅。

解决办法: fall clip是 12-18帧,land是21帧-45帧。


落地衔接更丝滑

但中间几帧能看出来落地时有轻微上移过程,原因待继续分析。

中间几帧逐帧分析

Idle→fall 切换时动画上移,视觉上造成卡顿


Idle→fall 切换时动画上移,视觉上造成卡顿

从中间两帧可以看到fall动画会比idle动画高,原因在于fall clip的帧是跳到最高点附近的动画,想要跳跃和Idle动画本身高度一致,就用到bake into pose了,generic如果不设置root 几点,没有这个选项,所以首先得给动画设置 " 大根 "。

但是generic动画如果未apply root motion,似乎root transform的设置没有用。

所以将所有动画改成humanoid,也方便未来动画复用。

将三个跳跃相关的动画 base upon 都设置成feet。

这样Idle→fall 切换就非常丝滑了,顺带之前落地卡顿问题也一并解决


丝滑降落切换和落地切换

跳跃到fall有突然前移现象


跳跃到fall有突然前移现象


切到fall有突然前移现象(在scene窗口现象更明显)

测试发现将jump→fall的过渡置0,就没有这中切换突进问题,过渡时长越大,突进越明显。

猜测问题出自过渡时两个两个状态都执行update导致两倍移动?

打印执行顺序,发现确实过渡时两个状态的OnStateUpdate方法都会执行

csharp 复制代码
// 1. 开始跳跃,进入Jump状态,执行jump enter,然后执行jump update
进入JumpUp state,当前帧:815153
执行jumpUp SwitchState,当前帧:815165

执行jumpUp DoStateJob,当前帧:815165

...

执行jumpUp SwitchState,当前帧:815632

执行jumpUp DoStateJob,当前帧:815632

// 2. 跳跃→降落 过渡开始,进入fall状态,执行fall enter,然后执行fall update
进入fall State,当前帧:815632
执行jumpUp SwitchState,当前帧:815644

执行jumpUp DoStateJob,当前帧:815644

// 可以发现fall 的update方法也会执行
执行fall SwitchState,当前帧:815644

执行fall DoStateJob,当前帧:815644

...

执行jumpUp SwitchState,当前帧:815852

执行jumpUp DoStateJob,当前帧:815852

执行fall SwitchState,当前帧:815852

执行fall DoStateJob,当前帧:815852

// 3. 过渡结束,执行jump exit,跳跃状态退出
退出jumpUp state,当前帧:815865

// 4. 继续执行fall update
执行fall SwitchState,当前帧:815865

执行fall DoStateJob,当前帧:815865

... 

执行fall SwitchState,当前帧:815888

执行fall DoStateJob,当前帧:815888

// 5. fall结束,执行fall exit
退出fall,当前帧:815901
当前fall状态持续时间:0.44

解决方法:

  • 可以取消 JumpUp→Fall 过渡

  • 或者给JumpUp执行update添加个过滤

    csharp 复制代码
    protected override void DoStateJob(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // 因为 jump 到 fall 过渡时会同时执行两个状态的update方法,
        //防止DoMoveInPhysics执行两次导致突然加速,进行特殊处理。或者取消jump to fall过渡,不过动画切换就显得生硬了些
        if (animator.IsInTransition(layerIndex) && animator.GetNextAnimatorStateInfo(layerIndex).shortNameHash == PLAYER_STATE_FALL)
        {
            return;
        }
        DoMoveInPhysics();
    }

当然两者结合也可以,最终跳跃降落的突进迁移问题就顺利解决了

但是还存在一个微位移问题。

单独跳跃切降落时依然发现角色动画有轻微位移。

原因不太清楚,我给JumpUp 和 Fall添加过渡(JumpUpState.length * 0.3)效果会好一点。

使用humanoid导致角色头发动画失效


头发摆动的是generic模型和动画,不动的是humanoid模型和动画

查看generic 模型和动画,可以发现它的骨骼还是比较复杂的 ,一层套一层,头发是是head下一级

而humanoid动画的骨骼都是打平的,且只有标准的十几个骨骼节点

去avatar 上去看,就是其中圈出来的重要关节节点,像非标准的发型是不包含的,所以Unity chan切到hunmanoid头发动画就失效了。

那之前为什么要用humanoid模型和动画呢?回顾了下,是为了解决fall切land卡顿问题,因为fall动画存在一个偏移,而generic rig 在未apply root motion时,是无法调整root transition相关参数的。但是一旦用了humanoid rig,头发之类的动画又失效了。所以这里还要重新尝试用generic rig。

首先勾选 apply root motion

然后将跳跃3个动画所有root transform的bake into pose都勾选,这样就可以利用里面的offset参数调整fall与地面的偏移了(我这里调整为0.3,根据实际视觉效果来定)。

一定要注意JumpUp、Fall、Land 的Bake into pose全部置为true,不然可能会越跳越高(jump up root tranform position Y未勾选bake into pose就会这样)。

最终效果比humanoid rig的动画好。

先到这吧。

相关推荐
心前阳光2 小时前
Unity WebGL文本输入
unity·游戏引擎·webgl
天涯过客TYGK3 小时前
unity A星寻路
unity·游戏引擎
KhalilRuan3 小时前
Unity Demo——3D平台跳跃游戏笔记
笔记·游戏·unity·游戏引擎
AA陈超1 天前
虚幻引擎UE5专用服务器游戏开发-20 添加基础能力类与连招能力
c++·游戏·ue5·游戏引擎·虚幻
ttod_qzstudio1 天前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊1 天前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云1 天前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
mit6.8242 天前
[shad-PS4] Vulkan渲染器 | 着色器_重新编译器 | SPIR-V 格式
c++·游戏引擎·ps4
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质