Unity动画系统实现人物基本动画、跳远、下蹲
一、素材准备
首先图集切割,sprite mode为Multiple;设置每个格子的像素数量为16(图片下载的像素);选择Point模式不添加任何过滤。

点击Sprite Editor,切割方式选择按行列切割,再将锚点设置为人物脚底中心。点击apply保存切割后的图片

二、创建人物基本动画
基本动画的创建与使用
创建animator动画控制器 --> 创建clip帧动画
人物Player上添加Animator组件,并创建Animator Controller。
创建Idle状态,添加对应的idle动画片段。素材图拖入时轴,设置采样率为10,预览动画效果。跑步和走路动画同理。

状态机的转换如图。idle到walk的转换条件为变量velocityX大于0.1,walk到run的转换条件是变量velocityX大于2.5。在设置中取消勾选“Has Exit Time”,Transition Duration为0实现立即切换。


混合树实现跳跃动画
创建跳跃动画的五个阶段:起跳、空中飞行、最高点、下落和着陆。通过使用animate中的混合树(Blend Tree)工具,将跳跃动画分为不同的阶段进行管理。
1.在animator窗口中创建新的混合树Blend Tree,命名为jump。
2.选择flow类型,Parameter使用velocity y作为参数。
3.添加动画片段到混合树中,设置切换条件。可以打印出跳跃高度,更清楚的看到各阶段用velocity.y显示的跳跃高度
4.PlayerAnimation脚本中将velocity.y值传递给动画播放器实现动画切换

跳跃状态的进入与退出
1.创建is ground变量,用于检测玩家是否在地面上。
2.设置跳跃状态的进入条件为is ground=false,退出条件为is ground=true。

使用Sub State Machine次级状态机实现人物下蹲动画
1.在Animator窗口空白处右键,创建Sub StateMachine。
2.命名为Crouch,用于管理下蹲动画的状态转换。
3.双击打开子状态机,可以看到与Base Layer相似的结构。
- 配置下蹲动画转换条件
- 从Idle状态连接到Crouch状态,设置条件为isCrunch=true。
- 下蹲起始动画播放完成后自动过渡到下蹲保持状态
- 设置退出条件为isCrouch=false,确保动画完整播放后进行状态转换。可通过Exit节点返回上级状态机,或直接连接到特定动画状态
-
次级状态机如图
![e9bb4bdd-94f2-4066-bd46-240b437bd1be]()
4.完整的状态机如图

5.编写代码控制下蹲状态
- 在PlayerController脚本,添加isCrunch布尔值变量。检测条件:inputDirection.y < -0.5f && physicsCheck.isGround。即在Move函数中添加下蹲状态判断逻辑。检测输入方向键的Y轴值,小于一定数值时判断为下蹲。结合地面检测,确保人物在地上时才能下蹲。
- 在PlayerAnimation脚本中,获取PlayerController组件。传递isCrunch布尔值到Animator,控制下蹲动画的播
6.由于下蹲时碰撞器的位置并不会刷新,故需要代码控制。在PlayerController脚本move函数中
- 获取CapsuleCollider2D组件,调整其size和offset。
- 记录原始size和offset值,以便在退出下蹲状态时恢复。
- 设置下蹲时的size和offset值,确保碰撞体与动画匹配。
三、动画切换代码编写
1、PlayerController完整代码
using UnityEngine; using UnityEngine.InputSystem; public class PlayerController : MonoBehaviour { private Rigidbody2D rb; private PhysicsCheck physicsCheck; private CapsuleCollider2D collider; public PlayerInputControl inputControl; public Vector2 inputDirection; [Header("基本参数")] public float speed; public float runSpeed; public float walkSpeed; public float jumpForce; public bool isCrounch; private Vector2 originOffest; private Vector2 originSize; private void Awake() { rb = GetComponent<Rigidbody2D>(); inputControl = new PlayerInputControl(); physicsCheck = GetComponent<PhysicsCheck>(); collider = GetComponent<CapsuleCollider2D>(); originSize = collider.size; originOffest = collider.offset; //跳跃按钮事件注册 inputControl.GamePlay.Jump.started += JumpOnstarted; #region 强制走路 runSpeed = speed; walkSpeed = runSpeed/2; //持续按下时走路形态,取消时跑步 inputControl.GamePlay.WalkButton.performed += ctx => { if (physicsCheck.isGround) speed = walkSpeed; }; inputControl.GamePlay.WalkButton.canceled += ctx => { if (physicsCheck.isGround) speed = runSpeed; }; #endregion } private void OnEnable() { inputControl.Enable(); } private void OnDisable() { inputControl.Disable(); } private void Update() { inputDirection = inputControl.GamePlay.Move.ReadValue<Vector2>(); } private void FixedUpdate() { Move(); } public void Move() { if(!isCrounch) //人物移动 rb.velocity = new Vector2(inputDirection.x * speed * Time.deltaTime, rb.velocity.y); //由于手柄操作inputDirection的值不一定只有-1和1,所以需要处理 int faceDir = (int) transform.localScale.x; if (inputDirection.x > 0) faceDir = 1; if (inputDirection.x < 0) faceDir = -1; //人物翻转 transform.localScale = new Vector3(faceDir,1,1); //人物下蹲 isCrounch = inputDirection.y < -0.5f && physicsCheck.isGround; if (isCrounch) { //修改碰撞体参数 collider.offset = new Vector2(collider.offset.x,0.85f); collider.size = new Vector2(collider.size.x,1.7f) ; } else { //还原 collider.offset = originOffest; collider.size = originSize; } } private void JumpOnstarted(InputAction.CallbackContext obj) { if (physicsCheck.isGround) rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } }
2、、PlayerAnimation脚本专门管理动画逻辑
using System;
using UnityEngine;
public class PLayerAnimation : MonoBehaviour
{
private Animator animator;
private Rigidbody2D rb;
private PhysicsCheck physicsCheck;
private PlayerController player;
private void Awake()
{
animator = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
physicsCheck = GetComponent<PhysicsCheck>();
player = GetComponent<PlayerController>();
}
private void Update()
{
SetAnimation();
}
public void SetAnimation()
{
animator.SetFloat("velocityX",Math.Abs(rb.velocity.x));
animator.SetFloat("velocityY",rb.velocity.y);
animator.SetBool("isGround",physicsCheck.isGround);
animator.SetBool("isCrounch",player.isCrounch);
}
}

浙公网安备 33010602011771号