Unity 2D 从零精通:第三阶段 - 2D的灵魂,艺术与动画
引言:从功能到体验的飞跃
在前两篇文章中,我们构建了游戏的核心骨架:移动、物理、碰撞、交互。我们的飞船可以飞行、射击,行星可以被摧毁,分数会增加。从功能上讲,它已经是一个完整的“游戏”了。
然而,它缺乏“灵魂”。飞船的移动生硬得像滑动一个图标,爆炸只是简单的消失。真正的游戏通过动画(Animation) 来传递感觉、重量和反馈,它是让玩家沉浸其中的关键。一个简单的摇晃可以表达冲击力,一个粒子效果可以传达爆炸的震撼,角色流畅的跑动跳跃能极大地提升操作手感。
在这一篇,我们将为我们冰冷的机械世界注入生命。我们将学习Unity强大且灵活的动画系统,让我们的飞船不仅能动,而且能“活灵活现”地动。我们将从处理静态图片开始,直到创造出由代码控制的复杂状态动画。
第一章:艺术的基石——深入Sprite Editor
在制作动画前,我们必须确保我们的原始素材(Sprites)被正确设置。Unity的Sprite Editor就是为此而生的瑞士军刀。
1.1 切片(Slicing)精灵图集(Sprite Atlas)
艺术家们通常会在一张大图上绘制所有相关的帧,这种图被称为精灵图集或雪碧图(Sprite Sheet)。我们需要将它切割成单个的Sprite才能使用。
-
准备图集:假设我们有一张图片(
ship_sheet.png),它包含了飞船 idle(待机)和 thrust(推进)两个动画,每个动画有4帧,水平排列。 -
导入设置:在Project窗口中选中这张图片,在Inspector中进行以下关键设置:
- Texture Type:必须设置为
Sprite (2D and UI)。 - Sprite Mode:因为我们有多个帧,所以必须设置为
Multiple(如果是单个Sprite,则用Single)。 - 点击
Apply。
- Texture Type:必须设置为
-
打开Sprite Editor:点击
Sprite Editor按钮。 -
自动切片:
- 在打开的窗口中,点击顶部工具栏的
Slice按钮。 - 在弹窗中,
Type选择Grid By Cell Count(按单元格数量)或Grid By Cell Size(按单元格大小)。根据你的图集结构选择。 - 例如,如果你的图集是2行4列(2个动画,每个4帧),你可以选择
Grid By Cell Count,并设置C(列)为4,R(行)为2。 - 点击
Slice,Unity会自动根据网格切割图片。你可以手动拖拽绿色边框微调每个Sprite的范围。
- 在打开的窗口中,点击顶部工具栏的
-
应用并命名:点击
Apply,关闭Sprite Editor。现在在你的图片资源下,可以看到所有被切好的单个Sprites。建议为它们重命名,例如ship_idle_1,ship_idle_2,ship_thrust_1,ship_thrust_2等。良好的命名是高效动画制作的第一步。
1.2 枢轴点(Pivot)的重要性
枢轴点是Sprite旋转和缩放所围绕的点。一个错误的枢轴点会导致动画看起来极其怪异。
- 如何设置:在Sprite Editor中,点击任意一个切好的Sprite,你可以在右侧设置它的
Pivot。常用的预设是:Center:对于行星、子弹等对称物体。Bottom:对于站在地面上的角色。Custom:可以手动精确定位。对于飞船,枢轴点通常设置在它的中心或尾部(如果它要围绕尾部旋转)。
第二章:创造生命——Animation窗口与逐帧动画
Unity提供了两种主要的2D动画制作方式:逐帧动画(Frame-by-Frame) 和 骨骼动画(Cutout/骨骼动画)。我们先从最直观的逐帧动画开始。
目标:为我们的飞船创建一个“推进器”动画,当玩家移动时播放。
2.1 创建Animation Clip(动画片段)
- 选中对象:在Hierarchy中,选中你要添加动画的GameObject——我们的
PlayerShip。 - 打开动画窗口:
Window -> Animation -> Animation(快捷键Ctrl+6)。将窗口停靠在方便的位置(通常与Game视图并排)。 - 创建新动画:在Animation窗口中,点击
Create按钮。Unity会提示你保存一个新的.anim文件。在项目Assets中创建一个Animations文件夹,并将文件命名为ShipThrust.anim。这创建了一个新的Animation Clip(动画片段)。
2.2 制作逐帧动画
- 认识时间轴:Animation窗口打开后,会显示一个时间轴。水平轴是时间(帧),垂直轴是属性(如Sprite Renderer.sprite)。
- 录制动画:
- 确保红色的录制按钮是按下状态。
- 将时间轴播放头(那条红色的线)移动到
0:00的位置。 - 在Project窗口中,找到
ship_thrust_1这张Sprite,拖拽到Animation窗口的时间轴上。Unity会自动为Sprite Renderer组件的Sprite属性创建一个关键帧。 - 将播放头移动到第
0:05(表示第5帧,Unity默认每秒60帧,所以这是约0.083秒)。 - 将
ship_thrust_2拖拽到时间轴上,创建第二个关键帧。 - 重复这个过程,将
ship_thrust_3,ship_thrust_4也依次放入,间隔相同。
- 预览动画:点击Animation窗口上的播放按钮,你可以在Scene视图或Game视图中看到飞船的推进器开始闪烁动画!
- 调整速度:如果你的动画播放得太快或太慢,你可以缩放整个时间轴。选中所有关键帧(在时间轴上拖拽框选),然后将它们整体向右拖拽,增加间隔,动画就会变慢。
2.3 设置默认状态(Idle)
现在,只要你运行游戏,飞船就会一直播放推进动画。我们需要一个默认的、不推进时的“待机”(Idle)状态。
- 创建Idle动画:再次点击Animation窗口中的
Create按钮(它现在可能显示为ShipThrust),创建一个新的动画片段,命名为ShipIdle.anim。用同样的方法,将ship_idle_1,ship_idle_2等Sprite拖入,制作一个缓慢循环的待机动画(比如两帧切换)。 - 了解Animator Controller:当你创建第一个动画时,Unity自动做了一件重要的事:它为
PlayerShip对象添加了一个Animator组件,并创建了一个Animator Controller资源(通常和第一个动画在同一目录,名为PlayerShip)。Animator组件:是播放动画的“播放器”。Animator Controller:是控制播放哪个动画的“大脑”或“状态机”。
第三章:控制大脑——Animator Controller与状态机
现在我们有了两个动画片段(Idle和Thrust),我们需要一个逻辑来告诉Unity何时播放哪一个。这就是Animator Controller和状态机(State Machine) 的工作。
3.1 理解状态机
双击Project窗口中的Animator Controller资源(如PlayerShip),会打开Animator窗口(Window -> Animation -> Animator)。
你会看到一个流程图,这就是状态机。它包含了:
- 状态(States):橙色的
Any State,黄色的Entry和Exit是系统状态。蓝色的方块(如ShipIdle)是你创建的动画状态。其中一个状态是橙色的,这是默认状态(通常是Entry指向的那个)。 - 转换(Transitions):状态之间的箭头。它定义了从一个状态切换到另一个状态的条件。
3.2 创建转换与参数(Parameters)
我们的逻辑是:当玩家按下移动键时,播放Thrust动画;不按的时候,播放Idle动画。
- 创建参数:在Animator窗口的左上方,有一个
Parameters标签页。点击+号,选择Bool(布尔值),创建一个新参数,命名为IsThrusting。这个参数将由我们的代码控制。 - 创建转换:
- 右键点击
ShipIdle状态 ->Make Transition。然后将出现的箭头拖到ShipThrust状态上。这创建了一个“从Idle到Thrust”的转换。 - 同样地,右键点击
ShipThrust状态 ->Make Transition,创建一条指向ShipIdle的箭头。这是“从Thrust回到Idle”的转换。
- 右键点击
- 设置转换条件:
- 点击从Idle到Thrust的箭头。在Inspector中,你会看到
Conditions列表。点击+,设置条件为:IsThrustingTrue。 - 点击从Thrust到Idle的箭头。在Inspector中,设置条件为:
IsThrustingFalse。
- 点击从Idle到Thrust的箭头。在Inspector中,你会看到
- 取消勾选:在两条转换线的Inspector中,找到
Has Exit Time并取消勾选。Exit Time会等待当前动画播放完才转换,我们希望输入能立即得到响应,所以必须取消它。
现在,你的状态机逻辑已经清晰:IsThrusting为false时,播放Idle动画;为true时,立即切换到Thrust动画。
3.3 用代码控制动画状态
最后一步,我们需要修改PlayerMovement脚本,根据玩家的输入来设置Animator中的IsThrusting参数。
- 获取Animator组件引用:打开
PlayerMovement脚本,我们需要在代码中访问Animator组件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed = 5f;
public float horizontalLimit = 8f;
public GameObject bulletPrefab;
public Transform firePoint;
public float fireRate = 0.5f;
private float nextFireTime = 0f;
// 新增:声明一个变量来引用Animator组件
private Animator shipAnimator;
// Start在第一次帧更新之前调用,用于初始化
void Start()
{
// 新增:在游戏开始时,获取挂载在同一GameObject上的Animator组件
shipAnimator = GetComponent<Animator>();
// 安全检查:确保获取到了组件
if (shipAnimator == null)
{
Debug.LogError("Animator component not found on the player ship!");
}
}
void Update()
{
HandleMovement();
HandleShooting();
}
void HandleMovement()
{
float horizontalInput = Input.GetAxis("Horizontal");
Vector3 movement = new Vector3(horizontalInput, 0f, 0f);
transform.Translate(movement * moveSpeed * Time.deltaTime);
Vector3 currentPosition = transform.position;
currentPosition.x = Mathf.Clamp(currentPosition.x, -horizontalLimit, horizontalLimit);
transform.position = currentPosition;
// 新增:根据是否有水平输入,来设置Animator的参数
// 即使输入很小(> 0.1 或 < -0.1),我们也认为玩家在尝试移动
bool isMoving = Mathf.Abs(horizontalInput) > 0.1f;
shipAnimator.SetBool("IsThrusting", isMoving);
}
// ... HandleShooting和Shoot方法保持不变 ...
}
- 保存脚本并测试:返回Unity,播放游戏。现在,当你按下A或D键移动飞船时,你应该能看到飞船的Sprite切换为推进动画!松开按键,它又回到待机状态。
恭喜!你已经成功创建了一个由玩家输入驱动的状态动画!
第四章:进阶技术——2D骨骼动画简介
对于复杂的角色(如人类、动物),逐帧动画工作量大且不易修改。Unity提供了更强大的2D骨骼动画系统,它像操控木偶一样,通过移动“骨骼”来驱动“皮肤”(Sprite),从而产生流畅的动画。
4.1 准备工作:PSB与骨骼工具
- 资源要求:骨骼动画需要角色各个部分(身体、手臂、腿等)是分层的PSD或PSB文件(Photoshop格式)。Unity对PSB支持更好。确保在导入PSB文件时,
Texture Type设置为Sprite (2D and UI),Sprite Mode设置为Multiple,然后使用Sprite Editor正确切片。 - 打开窗口:
Window -> Animation -> Animation(如果之前是Animation窗口,请切换到Animator旁边的Animation选项卡)Window -> 2D -> PSD Importer。
4.2 快速工作流
- 生成骨骼:在Project中选中分好层的PSB文件,在Inspector中点击
Sprite Editor。在Sprite Editor中,选择Skinning Editor(蒙皮编辑器)。这里你可以使用自动骨骼生成工具或手动创建骨骼。 - 绑定蒙皮:创建骨骼后,你需要将不同的Sprite(皮肤)绑定(Bind) 到影响它们的骨骼上。这可以通过自动权重或手动绘制权重来完成。
- 制作动画:绑定完成后,你就可以像在3D中一样,在Animation窗口中通过在不同时间点旋转、移动骨骼来制作动画了,而不是切换整张Sprite。这使得制作“转身”、“挥手”等动画变得异常简单和高效。
虽然本篇不会深入每一步,但了解这个强大工具的存在至关重要。对于复杂的2D角色,骨骼动画是行业标准。
第五章:总结与展望
你在本篇掌握的动画之力:
- 精通了Sprite Editor:学会了如何切片精灵图集和正确设置枢轴点。
- 掌握了逐帧动画:使用Animation窗口创建了基于Sprite切换的动画片段(Animation Clip)。
- 理解了核心概念:学会了使用Animator Controller和状态机(State Machine) 来逻辑性地管理多个动画。
- 实现了代码控制:通过C#脚本
Animator.SetBool()动态控制动画状态的切换,使游戏逻辑与视觉反馈完美结合。 - 窥见了未来:了解了更高级的2D骨骼动画工作流。
你的飞船不再是一个简单的移动图片,而是一个能够响应你操作、拥有不同状态的活生生的实体。这种视觉反馈极大地提升了游戏的质感和玩家的沉浸感。
下一篇预告:《第四阶段:构建你的世界——关卡设计与Tilemap》
在下一篇文章中,我们将离开单个对象,放眼整个游戏世界。我们将学习:
- Unity强大的Tilemap系统,用于快速、高效地绘制大型游戏关卡。
- 规则瓦片(Rule Tiles) 和随机瓦片(Random Tiles) 等高级技巧,让关卡设计变得有趣且多样。
- 如何解决图层排序(Sorting Layers和Order in Layer) 问题,确保物体正确遮挡。
- 使用CinemaMachine实现平滑的摄像机跟随。
- 最终,我们将构建一个完整的平台跳跃游戏关卡!
练习与思考:
- 为行星被子弹击中创建一个简单的“爆炸”动画(可以是一个缩放消失或几帧闪烁的动画),并把它做进状态机里。(提示:你需要修改Bullet脚本,触发行星播放动画而不是直接Destroy它)。
- 尝试为你飞船的idle状态添加更多帧,让它看起来像是在太空中轻微浮动,而不仅仅是两张图切换。
- 探索Animation窗口中的曲线(Curves) 功能,尝试让Sprite的透明度(Alpha)随时间变化,制作一个淡入淡出的效果。
世界正在你的手中变得鲜活。准备好画笔,我们下次将绘制更广阔的天地!
Unity 2D动画核心技术详解

被折叠的 条评论
为什么被折叠?



