摘要:本文从零开始,在 Unity 中实现一个基于 Animator 参数与代码驱动的四方向移动角色。文章先介绍环境准备与素材导入,再通过混合树搭建待机与奔跑动画状态,最后编写 C# 脚本控制角色移动,并实现停止移动时自动切换回待机动画、待机时保持最后一次面朝方向的效果。

目录
1. 引言
在 2D 游戏开发中,角色的四方向移动(上、下、左、右)是最基础也最核心的操作之一。而一个能让角色在待机与移动状态之间自动切换的动画系统,则是提升游戏手感与画面表现力的关键。
本文将带你从零开始,在 Unity 中实现一个基于 Animator 参数 + 代码驱动的四方向移动角色,并让它在停止移动时自动播放待机动画,移动时自动切换到对应方向的行走动画,在待机状态下角色依然保持最后一次移动的方向。
2. 准备工作
在开始之前,请确保你的环境满足以下条件:
- Unity 版本:2021.3 LTS 或更高版本(本文示例使用 2022.3.62f3c1)
- 开发语言:C#
- 素材准备:一组四方向(上、下、左、右)的行走与待机 Sprite 序列帧或图片,或使用 Unity 商店中的免费 2D 角色素材
本教程使用的素材取自Free - Pixel Art Asset Pack - Topdown Tileset - 16x16 Sprites by Anokolisa的免费像素风俯视角 RPG 素材包,包含 16x16 的角色行走与待机序列帧,非常适合本教程的演示。
提示:如果没有现成素材,可以在 Unity Asset Store 搜索 “2D Character” 获取免费资源,或使用简单的色块代替 Sprite 进行测试。若使用上述素材包,下载后解压,将 Sprites 文件夹直接拖入 Unity 的 Project 窗口即可完成导入。
项目搭建
第一步:新建项目。打开 Unity Hub,点击「新建项目」,在模板列表中选择 2D 项目模板,然后为项目命名并选择保存路径,点击「创建」即可进入项目。

进入项目后,找到顶部菜单栏中的 Window-Package Manager(包管理器)。
第二步:打开包管理器。进入项目后,找到顶部菜单栏中的 Window → Package Manager(包管理器),点击打开。

第三步:切换包来源。在包管理器左上角的下拉选项中,找到并选择 Unity Registry,这样就能看到 Unity 官方提供的所有扩展包。

第四步:安装 2D Sprite 包。在搜索框中输入 2D Sprite,找到对应包后点击「Install」安装(这里我已经安装成功)。这个包用于支持 Sprite 序列帧动画的导入与播放。

第五步:导入角色序列图,接下来找到下载好的玩家动画序列图(如果你的玩家图并没有像我这样的小箭头没有裁剪好图,是一整块图片,则需要你提前裁好,如果是单张图片,直接拖入即可)
选择一张你喜欢的、或最适合待机动作的序列图,将其拖入主窗口,并改名为 Player。这样角色素材就导入完成了。


第六步:添加刚体组件。选中场景中的 Player,在 Inspector 面板点击「Add Component」,搜索并添加 Rigidbody2D(用于物理移动)。

第七步:关闭重力。在 Rigidbody2D 组件中,将 Gravity Scale(重力)设置为 0,这是为了防止玩家运行时直接无限下坠掉下去。

第八步:打开 Animation 窗口。再次找到顶部菜单栏,点击 Window,选择 Animation 组件,将其拖到合适位置。

第九步:新建动画。在 Animation 窗口中点击 Create 新建动画(确保已经选中玩家 Player)。
第十步:命名待机动画。在弹出的保存窗口中,新建一个 Animations 文件夹,将动画命名为 Player_Idle_Down,点击保存。

新建一个 Animations 文件夹,将动画命名为 Player_Idle_Down。
> 说明:如果素材只有左右方向,可以通过 Sprite 的 `FlipX` 属性实现左右翻转,减少动画剪辑数量,或者编辑图片编辑素材将素材左右翻转
将裁剪好的动画序列图依次拖入 Animation 组件(注意顺序)。

依次命名添加动画
动画名称 | 用途 |
| -------- | ---- |
|Player_Idle_Down | 待机(朝下) |
|Player _Idle_Up | 待机(朝上) |
|Player_Idle_Left | 待机(朝左) |
|Player _Idle_Right | 待机(朝右) |
|Player_Run_Down | 向下行走 |
|Player_Run_Up | 向上行走 |
|Player_Run_Left | 向左行走 |
|Player_Run_Right | 向右行走 |
在主视图窗口点击左上角的 Animator 组件,将 New Animation,以及我们刚创建好的动画 Player_Idle_Down 删除,我们不需要这些。

在空白处右键新建两个混合树Create State——From New Blend Tree
将其命名为Idle空闲以及Run奔跑(命名方式可以依据个人情况更改)
在左侧菜单新建三个浮点型变量分别命名为(Horizontal-水平轴)(Vertical-垂直轴)(Speed-速度)(命名方式可以依据个人情况更改)

双击进入混合树Idle,再次点击混合树,在右侧将Blend Type 更改为2D Simple Directional

将Parameters设置为我们刚设置的变量,水平轴与垂直轴
在右侧动画列表新建四个动画片段
在posX和posY轴代表方位,在各自的位置上填好你之前设置好的动画
Run混合树也是同理,同样设置成这样
>注意,在这个动画列表会优先运行最上面的动画,所以要将默认朝向的动画放置在最上边,如果想要其他方向的动画作为默认动画,就将其仿制在最上边
设置好后回到Base Layer界面,我们开始连线,右键点击Idle混合树,会弹出Make Transition选项,将其连接到Run树上,然后在Run树上再连一条线连回来到Idle树上

将退出时间以及固定时长关掉,同时将过渡时长调为0,也就是无过渡(两条线都设置成这样)

在链接到Run树的线上,将下方过度条件Conditions设置为当Speed大于(Greater)0.01,这代表着,当我们移动时就会产生速度,说明我们正在移动,虽然现在很难理解,当我们一会写代码实现时你就会懂了
同时链接回Idle树的线上,将过度条件设置为当Speed小于(Less)0.01,这意味着我们停下了,所以回到Idle待机模式上
代码部分
我们新建一个C #脚本,将其命名为PlayerMove挂载到我们的玩家身上

打开脚本,我们先设置三个变量,放置我们刚体组件的变量,以及动画组件的变量,然后设置一个控制玩家速度的变量
>注意不要将Animator 写成 Animation

然后我们需要获取我们玩家身上的刚体和动画组件,方便我们引用

然后,我们需要新建两个新变量,方便我们获取键盘输入虚拟轴的输入值

然后将获取的值赋给我们自己设置的水平轴和垂直轴,.GetAxisRaw没有像GetAxis那样的数字平滑慢慢0.1~1过渡的增长,它只会返回三个值,向右1,向左-1,停下不动返回0,

每次都调用两个值太麻烦,我们直接新建一个二维变量,将他们存储起来


我们开始让他能够动起来,我们添加一个专门用来控制物理运动的FixedUpdate,它以固定时间间隔执行,不受帧率影响
Unity 默认FixedUpdate每 0.02 秒跑一次(也就是 50 次 / 秒)。 哪怕游戏掉帧、卡顿,它执行的时间间隔始终稳定,更适合物理相关代码
引用刚体组件的速度,让他来控制我们玩家的移动,将它乘上我们的速度,我们就可以自主控制它的速度和移动了

当我们退出去移动时,我们发现,玩家虽然会移动了,但是完全没有动画
我们开始编写更新动画的代码,让它能够根据我们的方向播放动画,同时进行待机和移动的自动切换,我们首先得知道它是否在移动,还是在待机,所以我们需要一个if语句

还记得我们的移动输入存储着我们输入的键盘虚拟轴的值吗,当我们按下移动时他会返回1或-1的值,当我们不动时则会返回0,.magnitude的作用是取模长,这有点难理解,但你只需要知道他会返回一个大于等于0的正整数,也就是说我们不需要在if判断里添加这个那个&&||,来判断我们到底是向左还是向右,我们只需要知道我们有没有在移动,只要按下了任意一个移动键,他就会返回1,说明我们在移动,反之,返回0
我们继续新建一个变量,用来确定我们的方向,以确保,不管我们按下哪个方向键都会朝对应方向移动,同时,将其.normalized归一化,斜向行走向量长度强制=1,防止斜走速度变快,它的方向就是我们的虚拟轴键盘方向的值


调用我们的动画Animatior组件,将我们之前设置好的水平轴以及垂直轴的值设为当前方向,这样当我们运行游戏并按下键盘时,Animator组件的值就会产生变化了,同时根据对应的值播放动画,不过我们发现当我停下时它依旧会播放对应的奔跑动画,而不是对应的待机动画

还记的我们的Animator组件的Speed吗,当我们的Speed大于0.01时,才会运行我们的Run树,当我们的Speed小于0.01时,才会回到我们的待机Idle树,所以,我们要将我们的Speed设置为我们上边IF的判断条件,判断我们在待机和奔跑中的切换条件

我们的奔跑条件完成,我们开始做我们的待机模式,我们暂时只有奔跑和待机两个状态,因此,只需要else即可,我们再次将保留了面朝向的二维变量,继续传给动画Animator组件的变量,之所以不在update里设置他是为了确保它不会归零,不会扭头回到正面

最后我们将Speed,设置为0,切换回待机模式

最后总结:
移动时:把归一化后的方向向量存入 lastMove,同时把 Horizontal/Vertical 设为该方向,Speed 设为输入向量的长度(>0),触发移动动画。
停下时:仍然把 Horizontal/Vertical 设为 lastMove(保留最后面朝方向),但 Speed 设为 0,触发待机动画。
附上代码
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMove : MonoBehaviour//玩家移动
{
private Rigidbody2D rb;//刚体组件引用
private Animator anim;
[Header("玩家属性")]
public float speed = 5;//玩家速度
[Header("水平轴")]
[SerializeField] private float horizontal;
[Header("垂直轴")]
[SerializeField] private float vertical;
[SerializeField] private Vector2 movementInput;//移动输入
[Header("最后一次移动")]
[SerializeField]private Vector2 lastMove;//最后一次移动
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
private void Update()//在Update中检测用户输入
{
horizontal = Input.GetAxisRaw("Horizontal");//用来获取某项输入-1,0,1
vertical = Input.GetAxisRaw("Vertical");
movementInput = new Vector2(horizontal, vertical);
}
private void FixedUpdate()//在FixedUpdate中做物理运动控制
{
rb.velocity = movementInput * speed;
if (movementInput.magnitude &gt; 0.01f)//如果按下方向键的任意键,就说明在移动,magnitude的作用是获取长度,当获取输入按下任意键,就表示它在移动中
{
lastMove = movementInput.normalized;//将最后一次移动,存储起来变成方向向量
anim.SetFloat("Horizontal", lastMove.x);
anim.SetFloat("Vertical", lastMove.y);
anim.SetFloat("Speed", movementInput.magnitude);//Speed更像是判断是否在移动,你可以将它理解为isMove,当移动时变为1,停下时为0
}
else//停下时进入待机状态,保留最后一次的面朝方向
{
anim.SetFloat("Horizontal", lastMove.x);
anim.SetFloat("Vertical", lastMove.y);
anim.SetFloat("Speed", 0f);
}
}
}
参考资料
- Unity 官方文档:Animator 组件
- Unity 官方文档:Rigidbody2D
- Unity 官方文档:Animation Clip
- 素材来源:Free - Pixel Art Asset Pack - Topdown Tileset - 16x16 Sprites by Anokolisa
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/m0_73876557/article/details/164454838




