当前位置: 首页 > article >正文

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

  • 【Unity实战笔记】第二一 · 基于状态模式的角色控制——以UnityChan为例
  • 【Unity学习笔记】第十一 · 动画基础(Animation、状态机、root motion、bake into pose、blendTree、大量案例)

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

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

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

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赋值一次变量,减少没必要的损耗。

  • Unity StateMachineBehaviour : Start function called once?

下坡鬼畜

在这里插入图片描述

下坡鬼畜
  

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

解决办法也简单,用射线检测检查斜面坡度,移动时基于斜面法线即可。(原先默认是vector3.up
注意旋转还是基于vector3.up,否则移动后就垂直于斜面上了。

在这里插入图片描述

  • 获取斜面法线

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

    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);
        }
    }
    

在这里插入图片描述

下坡鬼畜修复
  
  • 角色控制器斜坡与楼梯简易处理方法
  • Player “bouncing” down slopes

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

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

  • 添加Player_State_SO 脚本,标明CreateAssetMenu,然后右键添加SO文件
    [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
    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方法都会执行

// 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添加个过滤
    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的动画好。

在这里插入图片描述

先到这吧。


http://www.kler.cn/a/375812.html

相关文章:

  • Jupyter lab 打开时默认使用 Notebook 而不是浏览器
  • 大数据新视界 -- 大数据大厂之大数据环境下的网络安全态势感知
  • Redis高频面试题
  • R语言机器学习与临床预测模型79--机器学习总览
  • Spring Cloud Sleuth(Micrometer Tracing +Zipkin)
  • Docker 部署 Jaeger
  • (五)关于InternVL2的模型训练二(如何训练目标定位模型)
  • IDEA中通义灵码的使用技巧
  • Python 游戏开发库比较与示例
  • vue当中的$使用方法
  • leetcode71:简化路径
  • uniapp开发小程序【简单的实现点击下拉选择性别功能】
  • c++11(下篇)
  • Node.js 发展史
  • 3.2 页面异常-2
  • Redis读性能慢问题排查和调优
  • PAT甲级-1133 Splitting A Linked List
  • Chromium 中chrome.topSites扩展接口定义c++
  • Unity中有什么情况下是需要用UniTask替代其他异步方式的吗?
  • kdd比赛方案
  • C++【string的模拟实现】
  • Llama 3.2 Vision Molmo:多模态开源生态系统基础
  • 《双指针篇》---移动零
  • 「Mac畅玩鸿蒙与硬件20」鸿蒙UI组件篇10 - Canvas 组件自定义绘图
  • Spring Boot 与 Vue 共筑电影院选票新体验
  • Kong Gateway 指南