Animacer
本文最后更新于2 天前,其中的信息可能已经过时,如有错误请留言

Official document:Animancer – Home

Unity自带的画控制器Mecanim动存在许多设计缺陷,内部逻辑不透明,使用时需反复试验,手动操作且无法重用,修改可能导致其他问题。
相比之下,Animacer完全允许播放动画并通过代码控制,简化了使用流程。在检查器中提供实时控制,

Istate

IState 是一个状态接口,在有限状态机(FSM, Finite State Machine)的架构中,用于定义一个状态类所需实现的标准行为。每个实现了 IState 接口的类都代表一个具体的状态,并定义在进入、更新、动画变化和退出该状态时要执行的逻辑

public interface IState
{
    //状态被激活或进入时调用,初始化状态相关的数据、播放动画、重置参数等。
    void OnEnter();
    //每帧更新时调用。通常用于持续检测输入、控制状态行为或判断是否应当切换状态。
    void OnUpdate();
    //在动画更新时调用(例如每帧同步动画位移或动画事件)。常用于处理动画驱动的逻辑。
    void OnAnimationUpdate();
    //状态退出前调用。可在这里清理资源、重置标志或停止动画等。
    void OnExit();
    //动画播放结束时调用,适合做状态转换、触发下一动作等操作。
    void OnAnimationEnd();
}

StateMachineBase

public class StateMachineBase
{
    public IState currentState;
    public IState lastState;

    /// <summary>
    /// 状态切换的API
    /// </summary>
    /// <param name="targetState"></param>
    public virtual void ChangeState(IState targetState)
    {
        //调用当前状态的 OnExit()(如果当前状态存在)
        currentState?.OnExit();
        //更新lastState和currentState
        lastState = currentState;
        currentState = targetState;
        //调用当前状态的OnEnter()(如果当前状态存在)
        currentState?.OnEnter();
    }
    /// <summary>
    /// 动画状态退出的接口
    /// </summary>
    public void OnAnimationEnd()
    {
        currentState.OnAnimationEnd();
    }
    /// <summary>
    /// Update状态API
    /// </summary>
    public void OnUpdate()
    {
        currentState?.OnUpdate();
    }
    /// <summary>
    /// 按动画帧来更新
    /// </summary>
    public void OnAnimationUpdate()
    {
        currentState?.OnAnimationUpdate();
    }
}

CharactorBase

CharacterBase 是一个通用的“角色物理控制核心类”,处理了角色的重力、落地、动画运动、空中运动、斜坡适配等逻辑,供子类Player 使用

using System;
using UnityEngine;

[RequireComponent(typeof(Animator),typeof(CharacterController))]
public class CharacterBase : MonoBehaviour
{
    public CharacterController controller { get; private set; }
    public  Animator animator { get; private set; }
    //重力的配置
    [Header("重力设置")]
    [SerializeField] public float gravity = -12;
    [SerializeField] public Vector2 velocityLimit = new Vector2(-20, 60);
    [SerializeField] public LayerMask whatIsGround;
    [SerializeField] private float groundDetectedOffset = -0.06f;
    [SerializeField] private float groundRadius = 1.2f;
    private Vector3 detectedOrigin;
    public BindableProperty<bool> isOnGround { set; get; } = new BindableProperty<bool>();
    //角色垂直速度
    public float verticalSpeed { get; set; }
    private Vector3 verticalVelocity;
    //角色的水平速度:不包含动画位移
    private Vector3 horizontalVelocityInAir;
    private Vector3 animationVelocity;
    public Vector3 AnimationVelocity => animationVelocity;
    //角色的运动
    private Vector3 moveDir;
    public Vector3 animatorDeltaPositionOffset{ get; set; }
    public bool applyFullRootMotion { get; set; } = false;
    [SerializeField,Range(0.1f,10)] public float moveSpeedMult =1;
    public bool disEnableRootMotion { get; set; }//不采用任何根运动信息,禁用OnAnimatorMove方法
    public bool ignoreRootMotionY { get; set; } = false;//忽视根运动的Y量
    public bool disEnableGravity { get; set; } = false;//是否禁用程序重力
    public bool ignoreRotationRootMotion { get; set; } = false;//是否忽略根运动的转向
    protected virtual void Awake()
    {
        animator = GetComponent<Animator>();
        controller = GetComponent<CharacterController>();
    }
    protected virtual void Update()
    {
        CheckOnGround();
        CharacterGravity();
        CharacterVerticalVelocity();
        ResetHorizontalVelocity();
    }
 
    #region 重力的处理
    /// <summary>
    /// 地面检测
    /// </summary>
    private bool CheckOnGround()
    {
        //获取检测球体的中心位置
        detectedOrigin = transform.position - groundDetectedOffset * Vector3.up;
        //检测是否与 whatIsGround 图层有接触,不受那些“只触发、不碰撞”的 Trigger Collider 影响。
        var isHit = Physics.CheckSphere(detectedOrigin, groundRadius, whatIsGround, QueryTriggerInteraction.Ignore);

        /*
        verticalVelocity在 CharacterGravity() 方法中更新,每帧都会加速下落,直到最大速度 velocityLimit.x
        */
        isOnGround.Value = isHit && verticalSpeed < 0;
        return isOnGround.Value;
    }
    private void CharacterGravity()
    {
        //如果当前角色设置为不受重力影响(如飞行、跳跃帧锁等状态),直接返回,不执行下面逻辑。
        if (disEnableGravity)
        {
            return;
        }
        //如果角色站在地面上,verticalSpeed 设置为 -2(略微向下)这是一种常见技巧,防止角色贴地后“悬空”或者和地面产生穿插。
        if (isOnGround.Value)
        {
            verticalSpeed = -2;
        }
        else
        {
            //模拟自由落体
            verticalSpeed += Time.deltaTime * gravity;
            //限制下落速度不要超过最大限制(避免速度过快穿模)
            verticalSpeed = Mathf.Clamp(verticalSpeed, velocityLimit.x, velocityLimit.y);
        }
        verticalVelocity = new Vector3(0, verticalSpeed, 0);
    }

    #endregion

    #region 玩家移动
    /// <summary>
    /// 角色落地后清零空中速度,防止跳跃或空中位移的惯性导致角色落地后仍然“漂移”
    /// </summary>
    private void ResetHorizontalVelocity()
    {
        if (isOnGround.Value)
        {
            if (horizontalVelocityInAir!= Vector3.zero)
            {
                horizontalVelocityInAir = Vector3.zero;
            }
        }
    }
    /// <summary>
    /// 应用垂直与空中水平速度,驱动角色移动
    /// </summary>
    private void CharacterVerticalVelocity()
    {
        //如果当前角色处于“无重力状态”
        if (disEnableGravity)
        {
            verticalVelocity = Vector3.zero;
        }
        //CharacterController组件处于启用状态,则将垂直方向速度和水平方向的速度加在一起,得到完整的运动方向 + 速度。
        if (controller.enabled)
        {
            controller.Move((verticalVelocity + horizontalVelocityInAir) * Time.deltaTime);
        }

    }

    /// <summary>
    /// 控制角色是否使用动画自带的移动Root Motion,或在禁用 Root Motion 时,手动采样动画位移数据并进行地形适应处理
    /// </summary>
    protected virtual void OnAnimatorMove()
    {
        //Unity 会在播放动画且 Animator 有启用 RootMotion 时每帧调用此方法
        //不启用角色根运动
        if (disEnableRootMotion)
        {
            return;
        }
        //开启角色的根运动,应用动画自带的位移量
        if (applyFullRootMotion) 
        {
            animator.ApplyBuiltinRootMotion();
        }
        //不启用根运动,但是采样的也是角色根运动信息(位移)
        else
        {
            Vector3 animationMovement = animator.deltaPosition+ animatorDeltaPositionOffset;
            if (ignoreRootMotionY)
            {
                animationMovement.y = 0;
            }
            moveDir = SetDirOnSlop(animationMovement) * moveSpeedMult;
            UpdateCharacterMove(moveDir,animator.deltaRotation);
        }
    }
    public void UpdateCharacterMove(Vector3 deltaDir,Quaternion deltaRotation)
    {
        if (!ignoreRotationRootMotion)
        {
            if (deltaRotation != Quaternion.identity)
            {
                transform.rotation = deltaRotation * transform.rotation;
            }
        }
        //每帧移动Dir个单位
        if (controller.enabled == true)
        {
            animationVelocity = deltaDir;
            controller.Move(deltaDir);
        }
      
    }
    public float ChangeVerticalSpeed(float verticalSpeed)
    {
        return this.verticalSpeed = verticalSpeed;
    }
    public void AddHorizontalVelocityInAir(Vector3 vector3)
    {
        horizontalVelocityInAir = new Vector3(vector3.x,0, vector3.z);
    }
    public void ClearHorizontalVelocity()
    {
        horizontalVelocityInAir = Vector3.zero;
    }

    #endregion

    #region 斜坡的处理
    private Vector3 SetDirOnSlop(Vector3 dir)
    {
        //检测脚下是碰撞体
        if (Physics.Raycast(transform.position, Vector3.down, out var hitInfo, 1))
        {
            //有碰撞体,返回碰撞体的法向量。
            if (Vector3.Dot(hitInfo.normal, Vector3.up) != 1)
            {
                //法向量和世界坐标中的竖直向上方向点积不是 1,说明碰撞体不是水平的
                //因此将[移动的方向]投影到碰撞体平面上。
                return Vector3.ProjectOnPlane(dir, hitInfo.normal);
            }
        }
        //法线是 (0,1,0),平面是水平面
        return dir;
    }
    #endregion

    private void OnDrawGizmos()
    {
        if (CheckOnGround())
        {
            Gizmos.color = Color.green;
        }
        else
        {
            Gizmos.color = Color.red;
        }

        Gizmos.DrawWireSphere(transform.position - groundDetectedOffset * Vector3.up, groundRadius);
    }
}

PlayerStateMachine : StateMachineBase

有限状态机,缓存状态,驱动并更新状态类

public class PlayerStateMachine : StateMachineBase
{
   //缓存状态
    public Player player;
    public PlayerIdleState idleState;
    public PlayerMoveStartState moveStartState;
    public PlayerMoveLoopState moveLoopState;
    public PlayerMoveEndState moveEndState;
    public PlayerJumpState jumpState;
    public PlayerClimbState climbState;
    public PlayerLedgeClimbState ledgeClimbState;
    public PlayerMoveToWallState moveWallState;
    public PlayerFallLoopState fallLoopState;
    public PlayerPlatformerUpState platformerUpState;
    public PlayerLandState landState;
    public PlayerStateMachine(Player player)
    {
        this.player = player;
        idleState = new PlayerIdleState(this);
        moveStartState = new PlayerMoveStartState(this);
        moveLoopState = new PlayerMoveLoopState(this);
        moveEndState = new PlayerMoveEndState(this);
        jumpState= new PlayerJumpState(this);
        climbState = new PlayerClimbState(this);
        ledgeClimbState = new PlayerLedgeClimbState(this);
        moveWallState = new  PlayerMoveToWallState(this);
        fallLoopState = new PlayerFallLoopState(this);
        platformerUpState = new PlayerPlatformerUpState(this);
        landState= new PlayerLandState(this);
    }
    public override void ChangeState(IState targetState)
    {
        base.ChangeState(targetState);
        player.ReusableData.currentState.Value = targetState.GetType().Name;
    }
}

Player : CharacterBase

功能模块CharacterBase 的职责Player 的职责
✅ 基础组件初始化 AnimatorCharacterController初始化 AnimancerComponent
✅ 重力与地面检测提供重力逻辑、地面检测、下落速度控制直接继承使用
✅ 空中运动控制控制空中水平速度、落地惯性清除提供接口如 AddHorizontalVelocityInAir() 等,供子类调用
✅ RootMotion处理控制是否使用动画自带位移、方向投影斜坡等添加 OnAnimationUpdate() 给状态同步动画信息
✅ 角色移动使用 controller.Move() 执行物理移动使用动画和状态控制移动逻辑
✅ 状态管理❌ 不涉及管理 PlayerStateMachine,控制角色各种行为(Idle、Jump等)
✅ 动画播放❌ 使用原生 Animator✅ 使用 Animancer 动态播放动画剪辑
✅ 输入/服务接入初始化 InputServiceTimerService
✅ 数据与逻辑封装使用 ReusableData 储存运行数据,ReusableLogic 封装通用逻辑

PlayerReusableData

它是一个“数据容器类”,非 MonoBehaviour 组件Unity 游戏角色控制器中用于缓存和复用运行时变量的数据结构,帮助管理一些易变的(动态)信息,例如动画混合参数、状态切换数据、角色速度等。

所有字段在同一角色状态机中的多个状态之间共享数据,比如移动、跳跃、攀爬、锁敌等避免重复创建和销毁,提高效率与一致性。

PlayerSO

public class PlayerSO : ScriptableObject
{
    [field:SerializeField]  public PlayerMovementData playerMovementData { get; private set; }
    [field :SerializeField] public PlayerParameterData playerParameterData { get; private set; }
}

PlayerParameterData

public class PlayerParameterData
{
    [field: SerializeField] public StringAsset standValueParameter { get; set; }
    [field: SerializeField] public StringAsset rotationValueParameter { get; set; }
    [field: SerializeField] public StringAsset speedValueParameter{ get; set; }
    [field: SerializeField] public StringAsset LockValueParameter { get; set; }
    [field: SerializeField] public StringAsset Lock_X_ValueParameter { get; set; }
    [field: SerializeField] public StringAsset Lock_Y_ValueParameter { get; set; }
    [field: SerializeField] public StringAsset moveInterruptEvent { get; set; }
    [field: SerializeField] public StringAsset cancelClimbEvent { get; set; }
 }

PlayerMovementData

集中管理玩家各种动作状态下的配置数据

public class PlayerMovementData
{
    [field: SerializeField] public PlayerIdleData PlayerIdleData { get;private set; }
    [field: SerializeField] public PlayerMoveStartData PlayerMoveStartData { get; private set; }
    [field: SerializeField] public PlayerMoveLoopData PlayerMoveLoopData { get; private set; }
    [field: SerializeField] public PlayerMoveEndData PlayerMoveEndData { get; private set; }
    [field :SerializeField] public PlayerClimbData  PlayerClimbData { get; private set; }
    [field:SerializeField] public PlayerHangWallData PlayerHangWallData { get; set; }
    [field: SerializeField] public PlayerJumpFallAndLandData PlayerJumpFallAndLandData { get;private set; }

}
学习笔记如有侵权,请提醒我,我会马上删除
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇