Unity 2D序列帧动画全流程指南:从素材导入到脚本控制
1. 项目概述:为什么是Unity 2020.1与2D序列帧动画?
如果你刚接触Unity,想做一个2D小游戏,角色动画绝对是绕不开的第一道坎。网上教程很多,但要么版本老旧界面不符,要么讲得太快跳过了关键细节,比如精灵(Sprite)导入后为什么是糊的?动画片段(Animation Clip)怎么就和角色控制器(Animator Controller)连不上?最后只能对着一个不会动的纸片人干瞪眼。这个教程就是来解决这些“最后一公里”问题的。我选择Unity 2020.1这个长期支持(LTS)版本作为基准,因为它足够稳定,生态完善,教程中的方法在后续多个版本中依然通用,避免了因版本迭代过快导致的操作失效。我们将从零开始,手把手带你完成一个2D角色序列帧动画的全流程,从素材准备、导入设置,到动画创建、状态机配置,最后通过代码控制,让你彻底掌握这套工作流。所有涉及的工程文件和源码都会放在GitHub上,你可以随时对照、克隆和修改。
序列帧动画,说白了就是“翻页动画”,把角色奔跑、跳跃、攻击等动作的每一帧画面准备好,然后让Unity快速连续播放这些画面,形成动态效果。这听起来简单,但要让动画在游戏中流畅、可控且易于管理,就需要理解Unity的2D动画系统、精灵渲染器(Sprite Renderer)和动画状态机(Animator)是如何协同工作的。本教程的目标,就是让你不仅“做出来”,更能“弄明白”,以后遇到任何2D动画需求都能举一反三。
2. 工程准备与素材规范:为成功打下基础
在动手写第一行代码或拖拽第一个组件之前,充分的准备工作能避免后续80%的麻烦。这一节,我们来搭建一个干净的工程环境,并处理好你的动画素材。
2.1 创建与配置Unity 2D项目
打开Unity Hub,点击“新建项目”。在项目模板中,务必选择“2D”核心模板。不要小看这个选择,它和选择3D模板创建的项目有本质区别:2D模板会默认将场景的摄像机(Camera)设置为正交投影(Orthographic),这消除了Z轴上的透视变形,让我们的2D精灵能保持正确的像素比例;同时,它也会预设一些适合2D的包管理和渲染管线设置。
给项目起个名字,比如“2DCharacterAnimationTutorial”,选择一个合适的存储路径。我强烈建议路径中不要包含中文或特殊字符,这能避免一些潜在的引擎或版本控制系统的诡异问题。点击创建后,Unity会初始化项目。
项目创建好后,第一件事是规划文件夹结构。一个清晰的结构是项目可维护性的基石。在Project窗口的Assets文件夹下,右键创建以下文件夹:
Sprites/Characters/Player: 存放玩家角色的所有精灵图片。Sprites/Environment: 存放背景、平台等环境元素。Animations/Player: 存放玩家角色的动画控制器(Animator Controller)和动画片段(Animation Clip)。Prefabs: 存放预制体(Prefab),方便重复使用。Scripts: 存放所有C#脚本。Scenes: 存放游戏场景。Settings: 存放如标签(Tags)、图层(Layers)、输入管理器(Input Manager)设置等。
注意:Unity对路径和文件名大小写敏感(尤其在跨平台时)。保持命名一致性,建议全部使用英文和驼峰命名法(CamelCase)或下划线连接。
2.2 获取与处理序列帧素材
动画的灵魂在于素材。你可以自己绘制,也可以从一些免费的资源网站(如Kenney.nl、OpenGameArt.org)获取。对于本教程,我们假设你已经拥有一个角色的奔跑动画序列帧,通常是像player_run_01.png,player_run_02.png...这样命名的一系列PNG图片。
素材的黄金规范:
- 格式:优先使用PNG格式,它支持透明通道(Alpha Channel),这对于2D角色和特效至关重要。
- 尺寸与比例:确保序列中所有图片的尺寸(像素宽高)完全一致。如果第一帧是32x64像素,那么所有帧都必须是32x64。不一致的尺寸会导致Unity在生成精灵时出现错位和拉伸。
- 命名规范:使用有规律的命名,如“角色名_动作名_帧序号.扩展名”。这不仅能让你自己一目了然,一些自动化工具或脚本也能更好地识别和处理它们。
- 艺术风格统一:确保所有帧的光源方向、色彩风格、线条粗细保持一致。否则,播放动画时会感到明显的“跳跃感”。
将你的序列帧图片文件直接拖入Unity Project窗口中对应的Sprites/Characters/Player文件夹。此时,Unity会开始导入这些纹理(Texture)。
2.3 精灵导入设置详解:告别模糊与锯齿
素材拖进来只是第一步,正确的导入设置(Import Settings)才是让画面清晰锐利的关键。在Project窗口中点击任意一张导入的精灵图片,Inspector窗口会显示其导入设置。
- 纹理类型(Texture Type):必须设置为“Sprite (2D and UI)”。这告诉Unity这张图片将作为2D精灵使用,而不是3D模型的贴图。
- 精灵模式(Sprite Mode):
- 如果你的角色动画所有帧都在一张大图上(精灵图集,Sprite Atlas),选择
Multiple,然后需要点击Sprite Editor进行切片。 - 对于我们这种每帧一个独立文件的情况,选择
Single即可。稍后我们会通过另一种方式将它们组合成动画。
- 如果你的角色动画所有帧都在一张大图上(精灵图集,Sprite Atlas),选择
- 像素每单位(Pixels Per Unit, PPU):这是最重要的参数之一。它定义了游戏世界中一个Unity单位对应多少像素。假设你的角色精灵高度是64像素,你希望它在游戏里高度约为1个单位,那么PPU就设为64。保持项目中所有精灵的PPU一致(例如全部设为32, 64, 或100),可以确保角色、环境元素的比例协调,避免大小不一。这里我们先设为64。
- 过滤模式(Filter Mode):对于像素风或需要锐利边缘的2D游戏,务必选择
Point (no filter)。如果选择Bilinear或Trilinear,Unity会对纹理进行平滑插值,导致像素风格变模糊。对于高清或柔和的2D艺术,可以选择Bilinear。 - 压缩(Compression):为了节省包体大小,可以选择压缩。但对于像素艺术,压缩可能会引入色差或模糊。一个折中的方案是选择
None以获得最佳质量,或者在项目后期再考虑使用Low Quality的ETC2或ASTC格式(针对移动平台)。在开发阶段,为了清晰度,可以先设为None。
设置好一张后,可以选中所有同类型的精灵图片,在Inspector底部点击“Apply”按钮,将这些设置一键应用到所有选中资源。
实操心得:我经常遇到新手问“为什么我的游戏画面这么糊?”。十有八九是
Filter Mode没设为Point,或者PPU设置得太大/太小,导致精灵被过度拉伸压缩。花几分钟配置好导入设置,画面质感立竿见影。
3. 构建动画:从静态精灵到活灵活现的角色
现在,我们有了清晰规范的精灵素材,是时候让它们动起来了。Unity的动画系统核心是动画窗口(Animation Window)和动画控制器(Animator Controller)。
3.1 创建动画片段(Animation Clip)
首先,我们需要在场景中创建一个游戏对象(GameObject)来承载我们的角色。在Hierarchy窗口右键 ->2D Object->Sprite,创建一个空的精灵对象,重命名为“Player”。
- 将
Sprites/Characters/Player文件夹下的第一帧精灵(例如player_idle.png)拖拽到Hierarchy中Player对象的Sprite Renderer组件的Sprite属性栏上。这样角色就显示在场景中了。 - 确保Player对象被选中,然后打开
Window->Animation->Animation(快捷键Ctrl+6)。这个窗口是用来制作动画片段的。 - 动画窗口顶部会显示“Create”按钮。点击它,Unity会提示你保存一个新的动画片段。导航到
Assets/Animations/Player文件夹,命名为Player_Idle.anim并保存。同时,Unity会自动为Player对象添加两个组件:Animator和Animation(旧版系统)。在2020.1中,它主要添加的是Animator组件,并自动创建一个同名的Animator Controller(如Player.controller)赋值给它。
现在动画窗口的时间轴打开了。我们要制作一个奔跑动画。
- 在时间轴的0:00位置,确保
Sprite Renderer组件的Sprite属性是关键帧记录状态(属性名旁边有个红点)。 - 将Project窗口中的
player_run_01.png拖到动画窗口的时间轴区域,或者在0:00帧处,直接在Inspector中手动将Sprite Renderer的Sprite换成player_run_01.png。Unity会自动在此刻记录一个关键帧。 - 将时间轴上的红色播放头拖动到下一个时间点,比如0:05秒(这取决于你想要的动画速度,帧率通常是12, 24或30 FPS。假设我们按24FPS,0.05秒大约是一帧)。
- 将
player_run_02.png拖到时间轴,或手动更换Sprite属性。如此反复,按顺序将所有奔跑序列帧(01, 02, 03...)添加到时间轴上。 - 添加完所有帧后,点击动画窗口的播放按钮预览,你应该能看到角色在场景视口中循环奔跑起来了。
关键参数调整:
- 采样率(Samples):在动画窗口左上角,可以设置动画的采样率,默认60。这代表每秒评估60次动画曲线。对于序列帧动画,这个值不影响帧率,但保持默认即可。
- 动画长度与速度:时间轴的总长度决定了动画播放一遍的时间。你可以拖动最后一帧的关键帧来拉长或缩短总时间,从而改变动画播放的快慢。更直接的方法是,在Project窗口中选中创建好的
Player_Run.anim文件,在Inspector中可以看到其Speed(速度)乘数,调整它也能全局改变播放速率。
3.2 配置动画控制器(Animator Controller)与状态机
动画片段(Animation Clip)是“动作”,而动画控制器(Animator Controller)是“大脑”,它决定什么时候播放哪个动作。双击Project窗口中Animations/Player文件夹下自动生成的Player.controller,打开Animator窗口(Window -> Animation -> Animator)。
你会看到一个初始状态机,里面可能已经有一个状态(State)指向你刚才创建的Player_Idle动画。我们需要构建一个简单的角色状态机。
- 创建状态(States):在Animator窗口空白处右键 ->
Create State->From New Blend Tree(先创建空状态,再替换更简单)。我们先创建几个空状态,然后分别重命名并赋值:Idle: 将Motion字段设置为Player_Idle.anim。Run: 将Motion字段设置为Player_Run.anim。Jump: 创建一个新的动画片段Player_Jump.anim(方法同上),并赋值给Jump状态。Fall: 同上,创建Player_Fall.anim。
- 设置状态转换(Transitions):状态之间需要用箭头连接,并定义转换条件。例如,从
Idle到Run。- 右键
Idle状态 ->Make Transition,然后点击Run状态,创建一条箭头。 - 点击这条箭头,在Inspector窗口中可以看到转换设置。
- 条件(Conditions):这是控制转换的逻辑。我们需要参数(Parameters)来驱动。在Animator窗口左上角的Parameters选项卡,点击“+”号,添加以下浮点型(Float)或布尔型(Bool)参数:
Speed(Float): 控制移动速度。IsGrounded(Bool): 角色是否在地面。VerticalVelocity(Float): 垂直方向速度,用于区分跳跃上升和下落。
- 右键
- 配置转换条件:选中
Idle->Run的转换箭头,在Inspector的Conditions区域,点击“+”添加条件,选择参数Speed,条件设为Greater(大于),值设为0.1(一个很小的阈值,表示开始移动)。这意味着当Speed参数大于0.1时,状态从Idle切换到Run。 同样,创建Run->Idle的转换,条件设为SpeedLess(小于)0.1。 对于跳跃:Any State(或Idle/Run) ->Jump,条件需要IsGrounded为True,并且某个跳跃输入触发(这通常由另一个Bool参数如JumpTrigger控制,或者直接在脚本中调用Animator.Play(“Jump”))。Jump->Fall:条件可以是VerticalVelocityLess0(垂直速度小于0,表示开始下落)。Fall->Idle:条件是IsGrounded为True。
注意事项:避免设置过多的“Any State”到具体状态的转换,这可能导致状态机逻辑混乱。尽量让状态转换形成清晰的路径,如Idle <-> Run -> Jump -> Fall -> (Idle/Run)。同时,合理使用转换的退出时间(Exit Time)和固定持续时间(Fixed Duration)。对于序列帧动画,转换通常希望立即发生(Exit Time不勾选),这样响应更及时。
4. 脚本控制与逻辑集成:赋予角色生命
动画状态机搭好了,但它需要来自游戏逻辑的输入才能运转。这就是C#脚本的用武之地。我们将编写一个简单的角色控制器脚本,来驱动Animator中的参数。
4.1 编写角色动画控制器脚本
在Assets/Scripts文件夹下,右键创建一个新的C#脚本,命名为PlayerController。双击在编辑器中打开。
using UnityEngine; public class PlayerController : MonoBehaviour { // 移动速度 public float moveSpeed = 5f; // 跳跃力 public float jumpForce = 10f; // 检测地面的距离 public float groundCheckDistance = 0.2f; // 地面图层掩码 public LayerMask groundLayer; // 组件引用 private Rigidbody2D rb; private Animator animator; private SpriteRenderer spriteRenderer; // 状态变量 private bool isGrounded; private float horizontalInput; void Start() { // 获取组件引用 rb = GetComponent<Rigidbody2D>(); animator = GetComponent<Animator>(); spriteRenderer = GetComponent<SpriteRenderer>(); // 如果没有Animator组件,则添加一个(通常自动添加了) if (animator == null) { animator = gameObject.AddComponent<Animator>(); } } void Update() { // 1. 获取输入 horizontalInput = Input.GetAxisRaw("Horizontal"); // 返回 -1, 0, 1 // 2. 翻转角色朝向 if (horizontalInput > 0.01f) { spriteRenderer.flipX = false; } else if (horizontalInput < -0.01f) { spriteRenderer.flipX = true; } // 3. 检测地面 CheckGrounded(); // 4. 跳跃输入检测 if (Input.GetButtonDown("Jump") && isGrounded) { Jump(); } // 5. 更新Animator参数 UpdateAnimatorParameters(); } void FixedUpdate() { // 物理移动放在FixedUpdate中 Move(); } void Move() { // 计算水平速度,保持垂直速度不变 Vector2 velocity = new Vector2(horizontalInput * moveSpeed, rb.velocity.y); rb.velocity = velocity; } void Jump() { rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 可以在这里触发一个跳跃动画的触发器参数 // animator.SetTrigger("Jump"); } void CheckGrounded() { // 从角色底部中心点向下发射一条短射线检测地面 Vector2 origin = (Vector2)transform.position + Vector2.down * 0.5f; // 假设角色中心点为原点,Collider向下偏移 RaycastHit2D hit = Physics2D.Raycast(origin, Vector2.down, groundCheckDistance, groundLayer); isGrounded = hit.collider != null; // 可视化调试射线(仅在编辑器中显示) Debug.DrawRay(origin, Vector2.down * groundCheckDistance, isGrounded ? Color.green : Color.red); } void UpdateAnimatorParameters() { // 将水平速度的绝对值传递给Animator的Speed参数,用于Idle/Run转换 float speed = Mathf.Abs(horizontalInput); animator.SetFloat("Speed", speed); // 传递是否在地面 animator.SetBool("IsGrounded", isGrounded); // 传递垂直速度,用于Jump/Fall状态判断 animator.SetFloat("VerticalVelocity", rb.velocity.y); } }4.2 组件装配与场景设置
- 将
PlayerController脚本拖拽到Hierarchy中的Player对象上。 - 确保Player对象上有以下组件:
Sprite Renderer,Animator,Rigidbody 2D,Capsule Collider 2D(或Box Collider 2D)。Rigidbody 2D:用于物理模拟(重力、跳跃)。设置Body Type为Dynamic,Gravity Scale根据需要调整(如3)。Collider 2D:用于碰撞检测。调整其大小和位置,大致匹配角色精灵的轮廓。
- 在
PlayerController脚本的Inspector中,需要设置Ground Layer。- 在Unity顶部菜单
Edit->Project Settings->Tags and Layers中,定义一个名为“Ground”的图层(Layer)。 - 在场景中创建一个地面对象(如一个Sprite,缩放成一个长条),在其Inspector顶部,将其图层从“Default”改为“Ground”。
- 回到Player对象的
PlayerController组件,将Ground Layer设置为“Ground”。
- 在Unity顶部菜单
- 配置输入管理器(Input Manager):Unity默认的“Horizontal”、“Vertical”、“Jump”轴通常已配置好。你可以在
Edit->Project Settings->Input Manager中检查。Jump键默认是空格键。
4.3 动画事件(Animation Events)的运用
有时,你需要在动画播放到特定帧时触发游戏逻辑,比如在攻击动画的挥刀瞬间产生伤害判定,或在脚触地时播放音效。这就要用到动画事件。
- 在Animation窗口中,选中你的动画片段(如
Player_Attack.anim)。 - 将播放头拖动到你想要触发事件的帧(比如刀挥到最高点)。
- 在动画窗口左上角,点击那个小小的“添加事件”按钮(看起来像一个小旗帜),或者直接在时间轴刻度上右键。
- 一个白色标记会出现在时间轴上。选中这个标记,在Inspector窗口可以看到“Function”下拉菜单。这里会列出挂载在该游戏对象上所有脚本的公有方法(无参数或有一个参数的方法)。
- 例如,你可以在
PlayerController脚本中添加一个公有方法void DealDamage(),然后在这里选择它。 - 这样,当动画播放到这一帧时,
DealDamage()方法就会被自动调用。
实操心得:动画事件非常强大,但要注意事件函数的执行是跟动画帧率绑定的。如果游戏卡顿导致动画跳帧,事件可能会被错过。对于关键逻辑(如伤害判定),有时在
Update中根据动画状态和归一化时间(animator.GetCurrentAnimatorStateInfo)进行判断会更可靠。
5. 性能优化与常见问题排查
当你的角色动画能跑能跳后,我们还需要关注性能和解决一些常见“坑点”,确保项目健康、高效。
5.1 性能优化要点
- 精灵图集(Sprite Atlas):如果你的游戏有很多小精灵(比如UI图标、道具、特效帧),为每个精灵单独绘制调用(Draw Call)会严重影响性能。Unity的Sprite Atlas功能可以将多个精灵打包成一张大图,从而合并绘制调用。在
Window->2D->Sprite Atlas中创建图集,将相关精灵拖入,然后在Sprite Renderer中使用,Unity运行时会自动引用图集。注意:对于角色序列帧动画,如果所有帧已经在同一张图集里(Sprite Mode为Multiple),那本身就是优化的。如果是多个文件,可以考虑将它们打包进一个图集,但要注意动画播放时纹理采样可能带来的性能影响,通常单个角色的序列帧影响不大。 - 动画器控制器优化:
- 禁用未使用的层和参数:在Animator Controller中,如果有多余的层(Layers)或参数(Parameters),将其删除或禁用。
- 简化状态机:避免过于复杂的状态机和大量的Any State转换。复杂的逻辑可以考虑用脚本辅助控制。
- 使用子状态机(Sub-State Machines):将相关的一组状态(如所有攻击变体)放入一个子状态机中,可以使主状态机更清晰,且子状态机内的转换不影响外部,有助于性能管理和逻辑组织。
- 更新模式(Update Mode):Animator组件有一个
Update Mode选项,默认是Normal(每帧更新)。如果你的游戏是物理驱动的,可以考虑设为Animate Physics,使动画更新与物理系统同步。对于UI动画,可能使用Unscaled Time以忽略Time.timeScale的影响。 - 剔除(Culling):Animator组件还有
Culling Mode。对于永远在屏幕内的主角,用Always Animate。对于远处或屏幕外的敌人、NPC,可以使用Cull Update Transforms或Cull Completely,当它们不可见时停止更新动画和变换,节省CPU开销。
5.2 常见问题与解决方案速查表
下面这个表格整理了新手在制作2D序列帧动画时最常遇到的问题及其解决方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 精灵模糊、有锯齿 | 1. 纹理导入设置的Filter Mode不是Point。2. Pixels Per Unit设置不当,导致缩放失真。3. 摄像机正交大小(Orthographic Size)与屏幕分辨率不匹配。 | 1. 检查并修改纹理的Filter Mode为Point。2. 统一并合理设置PPU(如32, 64, 100)。 3. 调整2D摄像机的 Orthographic Size,使游戏单位与像素比例协调。可以写一个脚本根据屏幕分辨率动态调整。 |
| 动画播放卡顿、不流畅 | 1. 动画片段本身帧率不足或关键帧间隔不均匀。 2. 游戏运行时帧率(FPS)过低。 3. 使用了性能消耗大的实时阴影、后处理等。 | 1. 检查动画时间轴,确保关键帧分布均匀。在Animation Clip的Inspector中调整Sample Rate(通常保持默认)。2. 使用Profiler(Window -> Analysis -> Profiler)分析性能瓶颈,优化代码和资源。 3. 对于2D游戏,谨慎使用复杂的光照和阴影。 |
| 角色动画状态不切换 | 1. Animator Controller中的转换条件(Conditions)设置错误。 2. 脚本中没有正确更新Animator的参数。 3. 状态机有多个冲突的转换条件。 | 1. 双击打开Animator Controller,仔细检查箭头上的转换条件,确保参数名和比较逻辑正确。 2. 在脚本中打印或调试 animator.GetFloat(“Speed”)等参数值,看是否按预期变化。3. 检查状态优先级,确保没有两个条件同时满足导致不确定行为。使用状态机的“排序层”(Layer)和“权重”(Weight)来管理复杂逻辑。 |
| 碰撞体与精灵图像不匹配 | Collider 2D的形状和大小没有根据精灵轮廓调整。 | 1. 在Sprite Editor中为精灵生成物理形状(Physics Shape),然后在Collider 2D组件中选择Sprite作为形状源。2. 手动调整Collider 2D的尺寸(Size)和偏移(Offset),使其紧密贴合精灵可视区域。 |
| 动画播放一次后停止 | 动画片段(Animation Clip)的循环设置被关闭。 | 在Project窗口中选中动画片段,在Inspector中勾选Loop Time选项。对于某些不希望循环的动画(如死亡动画),则不应勾选。 |
| 构建(Build)后动画丢失 | 可能某些动画片段或控制器没有被场景引用,导致未被自动打包。 | 1. 确保所有用到的动画资源都放在Resources文件夹下,或者被场景中的对象直接或间接引用(如通过预制体)。2. 在 Build Settings->Player Settings->Other Settings中,检查Scripting Backend和Api Compatibility Level是否合适,不合适的设置可能导致资源加载失败。 |
5.3 调试技巧与工具
- Animator窗口预览:在Play模式下,打开Animator窗口,你可以实时看到当前活跃的状态(橙色高亮),以及各个参数的值。这是调试状态机逻辑最直观的工具。
- Animation窗口预览:在非播放模式下,也可以选中对象和动画片段,在Animation窗口中逐帧查看,检查关键帧是否正确。
- Debug.DrawRay / Gizmos:如上文脚本中的
Debug.DrawRay,用于可视化调试射线检测,非常有用。 - Editor Console:关注警告(Warnings)和错误(Errors)。一些设置不当(如丢失引用、参数未使用)会在这里提示。
6. 扩展思路与GitHub源码解析
掌握了基础流程后,你可以尝试更高级的应用,让角色动画更加丰富和智能。
6.1 混合树(Blend Trees)实现平滑移动
上面的例子中,Idle和Run是硬切换。对于拥有多个方向奔跑动画(八方向)或者由速度线性混合的动画(如从走到跑),可以使用混合树。
- 在Animator中,右键 ->
Create State->From New Blend Tree。 - 双击进入混合树,在Inspector中设置混合类型,如
2D Simple Directional(用于八方向),然后将不同方向的奔跑动画片段拖入,并设置对应的阈值(Threshold),如(0,1)对应向前跑,(1,0)对应向右跑等。 - 在脚本中,你需要计算一个二维向量(如
inputVector)传递给混合树的两个浮点参数(如BlendX,BlendY)。
6.2 动画层(Layers)与遮罩(Avatar Masks)
如果你想让人物上半身播放攻击动画的同时,下半身继续播放奔跑动画,就需要用到动画层和遮罩。
- 在Animator窗口中,点击“Layers”旁边的“+”号添加一个新层,命名为“UpperBody”。
- 为该层创建一个Avatar Mask。在Project窗口右键 ->
Create->Avatar Mask。在Inspector中,你可以选择遮罩身体的哪些部位(对于2D,通常是选择对应的骨骼或变换节点,如果是骨骼动画)。这里我们假设你使用了2D骨骼(Unity的2D Animation包),可以勾选上半身的骨骼。 - 将创建的Avatar Mask赋给“UpperBody”层的
Mask属性。 - 在“UpperBody”层中创建你的攻击动画状态机。设置该层的
Weight为1,Blending为Override(覆盖)。 - 这样,当“UpperBody”层播放攻击动画时,它会覆盖基础层(Base Layer)上半身的动画,而下半身则继续播放基础层的奔跑或站立动画。
6.3 GitHub源码结构说明
为了方便大家学习和参考,我将本教程的完整Unity工程上传到了GitHub。仓库的结构清晰地反映了我们的学习路径:
2DCharacterAnimationTutorial/ ├── Assets/ │ ├── Animations/ │ │ └── Player/ │ │ ├── Player.controller # 动画状态机控制器 │ │ ├── Player_Idle.anim # 闲置动画片段 │ │ ├── Player_Run.anim # 奔跑动画片段 │ │ ├── Player_Jump.anim # 跳跃动画片段 │ │ └── Player_Fall.anim # 下落动画片段 │ ├── Scripts/ │ │ └── PlayerController.cs # 角色控制与动画参数驱动脚本 │ ├── Sprites/ │ │ └── Characters/ │ │ └── Player/ # 存放所有角色精灵图片 │ └── ... (其他标准文件夹) ├── Packages/ # 项目依赖的包清单 ├── ProjectSettings/ # Unity项目设置 └── README.md # 项目说明与教程链接在PlayerController.cs脚本中,你不仅能看到基础的移动和动画参数更新,我还添加了更多注释和可配置的公共变量,方便你根据自己的需求调整移动手感、跳跃力度等。Animator Controller中也已经搭建好了Idle、Run、Jump、Fall四个状态及其转换逻辑,你可以直接导入工程,运行Main场景查看效果,并以此为模板进行修改。
最后的小技巧:在制作动画时,我习惯为每个角色创建一个空的“模板”预制体,上面挂好Animator、Rigidbody2D、Collider2D和控制器脚本,并配置好Animator Controller。当需要制作新角色动画时,只需复制这个预制体,替换精灵和动画片段,微调碰撞体和参数即可,能极大提升工作效率。动画制作是一个需要耐心和细心的过程,从简单的循环动画开始,逐步增加交互性动画(如受击、交互),你的2D游戏世界就会变得越来越生动。
