Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)


文章目录

        • 13.1 皮皮猫打字机游戏
        • 13.2 场景制作
          • 13.2.1 入口场景
          • 13.2.2 游戏场景
          • 13.2.3 场景切换
        • 13.3 游戏管理器
          • 13.3.1 数据定义
          • 13.3.2 生成字母盘
          • 13.3.3 按键判断
          • 13.3.4 连击定时器
        • 13.4 动画控制器Animator
          • 13.4.1 添加Animator
          • 13.4.2 Animator状态机
            • 13.4.2.1 Any State状态
            • 13.4.2.2 Entry状态
            • 13.4.2.3 Exit状态
          • 13.4.3 动画状态的属性
          • 13.4.4 状态间的过渡关系(Transitions)
          • 13.4.5 添加状态控制参数
          • 13.4.6 编辑切换状态的条件
        • 13.5 猫娘动画状态机
          • 13.5.1 模型资源下载
          • 13.5.2 动画循环设置
          • 13.5.3 状态过渡设置
        • 13.6 角色动画控制器
          • 13.6.1 数据定义
          • 13.6.2 直接播放动画接口
          • 13.6.3 设置变量值
          • 13.6.4 根据状态队列设置状态
        • 13.7 入口场景脚本
        • 13.8 游戏场景脚本
        • 13.9 文字特效动画

简介:我是一名Unity游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity游戏开发。
于是,发生了一系列有趣的故事。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.1 皮皮猫打字机游戏 皮皮:“铲屎官,你为什么打字速度这么快?”
我:“一个字,练。”
皮皮:“教教我,怎么连打字速度。”
我:“来,我给你做一个打字练习游戏吧。”
我:“当你可以一分钟连击180次的时候,你就可以出山了。”
皮皮:“作为猫族,不能被速度打败。”
游戏画面如下:

模块设计如下
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

本工程使用的Unity版本为2020.1.14f1c1 (64-bit),工程已上传到GitHub,感兴趣的同学可以下载下来学习。
GitHub地址:https://github.com/linxinfa/Unity-TypeWriting-Game
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.2 场景制作 13.2.1 入口场景 EntryScene.unity
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

一个标题文本(Text组件),一个开始按钮(Button组件),一个难度选择勾选(ToggleGroupToggle组件)。
背景图使用SpriteRender组件在3D摄像机中渲染。

难度等级的勾选使用了ToggleGroup组件,用来给Toggle分组。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

子节点中的Toggle需要指明相同的ToggleGroup
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

实现单选的效果。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.2.2 游戏场景 GameScene.unity
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

一个血条(Slider组件),一个得分(Text组件),一个字母盘(GridLayoutGroupText组件)、一个连击(Text组件),一个角色(SpriteRendererAnimator组件)、一个背景图(SpriteRenderer组件)。
其中字母盘只做一个字母,游戏中进行动态克隆。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

再做游戏结束面板,提供一个返回和重来的按钮。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.2.3 场景切换 点击菜单File - Build Settings...
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

将场景添加到Scenes In Build中。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

代码中,通过SceneManager.LoadScene切换场景,如下
// 进入GameScene场景 UnityEngine.SceneManagement.SceneManager.LoadScene(1);

13.3 游戏管理器 游戏管理器GameMgr,它需要包括数据和逻辑,如下。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.3.1 数据定义
/// /// 难度等级 /// public int hardLevel { get; set; } /// /// 得分 /// public int score { get; set; } /// /// 最大血量 /// private const int MAX_BLOOD = 1500; /// /// 血量 /// public int blood { get { return m_blood; } set { m_blood = value; if (m_blood <= 0) { gameOver = true; // TODO 抛出事件 } } } private int m_blood = 0; /// /// 连击数量 /// public int comboCnt { get; set; } /// /// 连击定时器 /// public float comboTimer { get; set; } /// /// 游戏结束 /// public bool gameOver { get; private set; } /// /// 按键列表 /// public List keyList { get { return m_keyList; } } private List m_keyList = new List();

13.3.2 生成字母盘 生成字母盘(16个字母),要求每个字母都不重复,生成的字母存到m_keyList中。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

/// /// 生成字母盘 /// private void GenKeys() { for (int i = 0; i < 16; ++i) { m_keyList.Add(GenOneKey()); } }/// /// 生成一个字母 /// /// private KeyCode GenOneKey() { var key = (KeyCode)UnityEngine.Random.Range((int)KeyCode.A, (int)KeyCode.Z); for(int i=0,cnt=m_keyList.Count; i

13.3.3 按键判断 我们需要先判断按键类型,封装一个接口GetKeyDownCode
/// /// 获取按键类型 /// /// public KeyCode GetKeyDownCode() { if (Input.anyKeyDown) { foreach (KeyCode keyCode in Enum.GetValues(typeof(KeyCode))) { if (Input.GetKeyDown(keyCode)) { return keyCode; } } } return KeyCode.None; }

然后判断按下的按键是否在字母盘中,返回对应的索引,如果不在字母盘中,则返回-1。
/// /// 判断按键是否在字母盘中 /// /// 按键 /// private int IsKeyBingo(KeyCode key) { for (int i = 0, cnt = m_keyList.Count; i < cnt; ++i) { if (m_keyList[i] == key) return i; } return -1; }

按键正确的时候,执行连击计算,加血加分,生成新的字母,抛事件更新ui
/// /// 按键正确 /// private void OnKeyBingo(int bingoIndex) { // 加连击 ++comboCnt; if (comboCnt >= 3) { // 加血加分,连击加持 blood += 150; if (blood > MAX_BLOOD) blood = MAX_BLOOD; score += 20; } else { // 加血加分 blood += 50; score += 10; } // 生成新的字母 var oldKey = m_keyList[bingoIndex]; var newKey = GenOneKey(); m_keyList[bingoIndex] = newKey; // TODO 抛事件,更新ui }

按键错误的时候,连击中断,扣血,抛事件更新ui
/// /// 按键错误 /// private void OnKeyError() { // 连击中断 comboCnt = 0; // 扣血 blood -= 30; // TODO 抛事件,更新ui }

13.3.4 连击定时器 如下,其中Time.deltaTime是一帧的间隔时间。每帧调用UpdateComboTimer,对comboTimer进行帧间隔时间递减,通过comboTimer判断是否超过时间限制,超过则中断连击。
/// /// 连击定时器 /// public void UpdateComboTimer() { if (comboTimer > 0) { comboTimer -= Time.deltaTime; // 超过时间限制,连击断开 if (comboTimer <= 0) { comboCnt = 0; // TODO 抛事件更新连击ui } } }

13.4 动画控制器Animator Unity可以用两种方式控制动画
1 Animation,这种方式简单,直接 Play(“Idle”)或者CorssFade(“Idle”)就可以播放动画;
2 AnimatorUnity5.x之后推荐使用这种方式,因为里面可以加上混合动画,让动画切换更加平滑。
13.4.1 添加Animator 点击菜单Window - Animation - Animation,可以打开Animation窗口,快捷键是Ctrl+6
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

选中某个物体后,可以为该物体添加或编辑动画,比如选中一个空物体,由于没有动画,会出现一个Create按钮。Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

点击Create按钮,会弹出窗口设置文件保存路劲。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

创建成功后,物体上会出现一个Animator组件。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

并且我们可以在目录中看到生成了两个文件。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

.controller文件是一个动画状态机,在Unity中双击它会打开Animator窗口,即可看到里面的内容,我们可以在这个窗口中组织各个动画文件。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

.anim是动画文件,在Unity中双击它会打开Animation窗口,我们可以在这个窗口中制作动画。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.4.2 Animator状态机 每个Animator Controller都会自带三个状态:Any State, EntryExit
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.4.2.1 Any State状态 表示任意状态的特殊状态。例如我们如果希望角色在任何状态下都有可能切换到死亡状态,那么Any State就可以帮我们做到。当你发现某个状态可以从任何状态以相同的条件跳转到时,那么你就可以用Any State来简化过渡关系。
13.4.2.2 Entry状态 表示状态机的入口状态。当我们为某个GameObject添加上Animator组件时,这个组件就会开始发挥它的作用。
如果Animator Controller控制多个Animation的播放,那么默认情况下Animator组件会播放哪个动画呢? 由Entry来决定的。
但是Entry本身并不包含动画,而是指向某个带有动画的状态,并设置其为默认状态。被设置为默认状态的状态会显示为 橘黄色。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

当然,你可以随时在任意一个状态上通过 鼠标右键->Set as Layer Default State更改默认状态。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

记住, EntryAnimator组件被激活后 无条件 跳转到默认状态,并且每个Layer有且仅有一个默认状态。
13.4.2.3 Exit状态 表示状态机的出口状态,以红色标识。如果你的动画控制器只有一层,那么这个状态可能并没有什么卵用。但是当你需要从子状态机中返回到上一层(Layer)时,把状态指向Exit就可以了。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.4.3 动画状态的属性 我们可以选中某个自定义状态,并在Inspector窗口下观察它具有的属性
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

属性名 描述
Motion 状态对应的动画。每个状态的基本属性,直接选择已定义好的动画(Animation Clip)即可
Speed 动画播放的速度。默认值为1,表示速度为原动画的1.0倍。
Mutiplier 勾选右侧的Parameter后可用,即在计算Speed的时考虑 区域1 中定义的某个参数。若选择的参数为smooth, 则动画播放速度的计算公式为 smooth * speed * fps(animation clip中指定)
Mirror 仅适用于humanoid animation(人型机动画)
Cycle Offset 周期偏移,取值范围为0-1.0,用于控制动画起始的偏移量。把它和正弦函数的offset进行对比就能够理解了,只会影响起始动画的播放位置。
Foot IK 仅适用于humanoid animation(人型机动画)
Write Default 最好保持默认,感兴趣可以参考官方手册
Transitions 该状态向其他状态发起的过渡列表,包含了Solo和Mute两个参数,在预览状态机的效果时起作用
Add Behaviour 用于向状态添加“行为
13.4.4 状态间的过渡关系(Transitions) 状态间的过渡关系,直观上说它们就是连接不同状态的有向箭头。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

要创建一个从状态A状态B的过渡,直接在状态A上 鼠标右键 - Make Transition并把出现的箭头拖拽到状态B上点击鼠标左边即可。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.4.5 添加状态控制参数 参数有FloatIntBoolTrigger
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

FloatInt用来控制一个动画状态的参数,比如速度方向等可以用数值量化的东西,
Bool用来控制动画状态的转变,比如从走路转变到跑步,
Trigger本质上也是bool类型,但它默认为false,且当程序设置为true后,它会自动变回false
如下这里创建一个Int类型的参数AnimState
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.4.6 编辑切换状态的条件 点击连线,在Inspecter窗口中可以进行设置,在Conditions栏下可以添加条件,如下图表示当参数
AnimState0时会执行这个动画Any StateNew Animation2的过渡
必须在Parameters面板中添加了参数才可以在这里查看到,其次添加的条件为&& ”与” 关系,即必须同时满足。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.5 猫娘动画状态机 13.5.1 模型资源下载 从Assets Store上下载猫娘模型,资源地址:https://assetstore.unity.com/packages/2d/characters/fancydoll-c000-little-cat-girl-112776
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.5.2 动画循环设置 模型自带了一些动画
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

idle(站立)、walk(走路)、run(跑)需要循环播放,勾选Loop Time
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

get_hit(受击)、die(阵亡)不需要循环播放,不勾选Loop Time
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.5.3 状态过渡设置 【Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)】我们需要通过Animator将这些动画进行合理的组织,如下
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

添加变量Action,过渡条件根据Action的值进行判断。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

过渡条件如下
状态1 状态2 条件
Any State idle Action == 1
Any State get_hit Action == 4
get_hit idle
Any State die Action == 5
13.6 角色动画控制器 角色动画控制器CharacterAniCtrler,它需要包括数据和逻辑,如下。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

运行中的状态过渡
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

13.6.1 数据定义
/// /// 状态定义,默认为Idle状态。 /// public enum CharacterAniId { Idle = 1, Walk = 2, Run = 3, Hit = 4, Death = 5, } /// /// 角色动画Animator组件 /// private Animator m_animator; /// /// 动画队列 /// private Queue m_animQueue = new Queue();

13.6.2 直接播放动画接口
/// /// 立即播放某个动画 /// /// 动画名称 private void PlayAniImmediately(string name) { if (IsDeath) return; m_animator.CrossFade(name, 0.1f, 0); }

如立即播放走路动画
public void PlayWalk() { PlayAniImmediately("walk"); }

13.6.3 设置变量值
// 设置Action变量值为4 m_animator.SetInteger("Action", 4);

封装成接口
private const string STR_ACTION = "Action"; /// /// 播放不同动作ID /// /// /// public void PlayAnimation(int actionID) { if (IsDeath) return; if (m_animator == null) return; if (!m_animator.isInitialized || m_animator.IsInTransition(0)) { // 如果正在过渡,则先塞到队列中 m_animQueue.Enqueue(actionID); return; } m_animator.SetInteger(STR_ACTION, actionID); }

13.6.4 根据状态队列设置状态 提供一个LateUpdate接口每帧调用,设置了Action值需要在下一帧的时候重置为0,然后从队列中取下一个状态进行处理。
/// /// 每帧调用 /// public void LateUpdate() { if (m_animator == null) { return; } if (!m_animator.isInitialized || m_animator.IsInTransition(0)) { return; } if (null == mClips) mClips = m_animator.GetCurrentAnimatorClipInfo(0); if (null == mClips || mClips.Length == 0) return; int actionID = m_animator.GetInteger(STR_ACTION); if (actionID > 0) { //将Action复位 m_animator.SetInteger(STR_ACTION, 0); }//将剩余队列的动作重新拿出来播放 PlayRemainAction(); }/// /// 将剩余队列的动作重新拿出来播放 /// void PlayRemainAction() { if (m_animQueue.Count > 0) { PlayAnimation(m_animQueue.Dequeue()); } }

13.7 入口场景脚本 入口场景脚本EntryScene.cs挂在Canvas上,设置Start Game BtnTgl Group
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

代码如下
using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class EntryScene : MonoBehaviour { public Button startGameBtn; public ToggleGroup tglGroup; void Start() { startGameBtn.onClick.AddListener(() => { // 根据勾选,缓存难度等级 foreach (var item in tglGroup.ActiveToggles()) { GameMgr.Instance.hardLevel = int.Parse(item.name); break; }// 进入Game场景 SceneManager.LoadScene(1); }); } }

13.8 游戏场景脚本 游戏场景脚本GameScene.cs挂在Canvas上,设置公开的成员对象。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

主要根据各种事件更新ui
using System; using UnityEngine; using UnityEngine.UI; public class GameScene : MonoBehaviour {public Animator anitor; public Text comboText; public Text scoreText; public Slider bloodSlider; public Image bloodImage; public GameOverDlg gameOverDlg; public KeyGrid keyGrid; private CharacterAniCtrler m_aniCtrler; private void Awake() { // 注册事件 EventDispatcher.Instance.Regist(EventNameDef.EVENT_KEY_BINGO_INDEX, OnEventKeyBingoIndex); EventDispatcher.Instance.Regist(EventNameDef.EVENT_COMBO, OnEventCombo); EventDispatcher.Instance.Regist(EventNameDef.EVENT_PLAY_ANI, OnEventPlayAni); EventDispatcher.Instance.Regist(EventNameDef.EVENT_UPDATE_SCORE, OnEventUpdateScore); EventDispatcher.Instance.Regist(EventNameDef.EVENT_RESTART_GAME, OnEventRestartGame); EventDispatcher.Instance.Regist(EventNameDef.EVENT_GAMEOVER, OnEventGameOver); m_aniCtrler = new CharacterAniCtrler(); m_aniCtrler.Init(anitor); // 开始游戏 StartGame(); }private void OnDestroy() { // 注销事件 EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_KEY_BINGO_INDEX, OnEventKeyBingoIndex); EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_COMBO, OnEventCombo); EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_PLAY_ANI, OnEventPlayAni); EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_UPDATE_SCORE, OnEventUpdateScore); EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_RESTART_GAME, OnEventRestartGame); EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_GAMEOVER, OnEventGameOver); }/// /// 开始游戏 /// private void StartGame() { GameMgr.Instance.Init(); TextEffect.Init(); // 初始化血量 bloodSlider.maxValue = https://www.it610.com/article/GameMgr.Instance.blood; bloodSlider.value = GameMgr.Instance.blood; bloodImage.enabled = true; // 生成字母盘 keyGrid.CreateKeyList(GameMgr.Instance.keyList); comboText.gameObject.SetActive(false); scoreText.text ="0"; gameOverDlg.Hide(); }void Update() { if (GameMgr.Instance.gameOver) return; // 更新连击定时器 GameMgr.Instance.UpdateComboTimer(); // 更新血量ui bloodSlider.value = https://www.it610.com/article/GameMgr.Instance.blood; GameMgr.Instance.blood -= GameMgr.Instance.hardLevel; // 按键判断 var keyCode = GameMgr.Instance.GetKeyDownCode(); if (KeyCode.None == keyCode) return; GameMgr.Instance.OnKey(keyCode); }private void LateUpdate() { // 更新动画控制器 m_aniCtrler.LateUpdate(); }/// /// 按键正确事件 /// /// private void OnEventKeyBingoIndex(params object[] args) { int index = (int)args[0]; KeyCode oldKey = (KeyCode)args[1]; KeyCode newKey = (KeyCode)args[2]; keyGrid.UpdateKeyByIndex(index, oldKey, newKey); }/// /// 连击事件 /// /// private void OnEventCombo(params object[] args) { var combo = (int)args[0]; comboText.text = "连击" + combo; comboText.gameObject.SetActive(combo >= 3); }/// /// 播放动画事件 /// /// private void OnEventPlayAni(params object[] args) { var ani = (string)args[0]; switch (ani) { case "idle": m_aniCtrler.PlayAnimation((int)CharacterAniId.Idle); break; case "walk": GameMgr.Instance.comboTimer = 0.5f; m_aniCtrler.PlayWalk(); break; case "run": GameMgr.Instance.comboTimer = 0.5f; m_aniCtrler.PlayRun(); break; case "hit": m_aniCtrler.PlayAnimation((int)CharacterAniId.Hit); break; case "die": m_aniCtrler.PlayDieImmediately(); break; } }/// /// 更新得分事件 /// /// private void OnEventUpdateScore(params object[] args) { var score = (int)args[0]; scoreText.text = score.ToString(); }/// /// 游戏结束事件 /// /// private void OnEventGameOver(params object[] args) { bloodImage.enabled = false; gameOverDlg.Show(GameMgr.Instance.score); }/// /// 重新开始游戏事件 /// /// private void OnEventRestartGame(params object[] args) { m_aniCtrler.PlayReviveImmediately(); StartGame(); } }

其中事件定义如下
/// /// 事件定义 /// public class EventNameDef { /// /// 按键正确事件 /// public const string EVENT_KEY_BINGO_INDEX = "EVENT_KEY_BINGO_INDEX"; /// /// 连击事件 /// public const string EVENT_COMBO = "EVENT_COMBO"; /// /// 播放动画事件 /// public const string EVENT_PLAY_ANI = "EVENT_PLAY_ANI"; /// /// 游戏结束事件 /// public const string EVENT_GAMEOVER = "EVENT_GAMEOVER"; /// /// 更新得分事件 /// public const string EVENT_UPDATE_SCORE = "EVENT_UPDATE_SCORE"; /// /// 重新开始游戏事件 /// public const string EVENT_RESTART_GAME = "EVENT_RESTART_GAME"; }

13.9 文字特效动画 Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

制作一个TextEffect.prefab预设,添加动画如下。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

由于游戏中需要重复显示这个特效,所以采用对象池方式。
特效动画结束时回收到对象池中,这样可以反复利用。为了监听动画结束,在动画的最后一帧添加帧事件。
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

创建一个TextEffect.cs脚本,挂到预设上,提供一个OnAnimationEnd共有方法
public void OnAnimationEnd()

这样就可以设置帧事件的响应函数了
Unity3D|《学Unity的猫》——第十三章(Unity使用Animator控制动画播放,皮皮猫打字机游戏)
文章图片

TextEffect.cs脚本如下
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /// /// 文本特效 /// public class TextEffect : MonoBehaviour {/// /// 初始化 /// public static void Init() { if(null != s_root) { Destroy(s_root.gameObject); s_root = null; }s_objPool.Clear(); var canvas = GameObject.Find("Canvas"); if (null != canvas) { var rootObj = new GameObject("EffectRoot"); s_root = rootObj.transform; s_root.SetParent(canvas.transform, false); } }/// /// 显示特效 /// /// /// public static void Show(string text, Vector3 pos) { if (null == s_prefab) { s_prefab = Resources.Load("TextEffect"); }TextEffect bhv = null; if (s_objPool.Count > 0) { // 从对象池中取对象, bhv = s_objPool.Dequeue(); } else { var obj = Instantiate(s_prefab); obj.transform.SetParent(s_root, false); bhv = obj.GetComponent(); } bhv.gameObject.SetActive(true); bhv.transform.position = pos; bhv.keyText.text = text; }/// /// 动画结束事件的响应函数 /// public void OnAnimationEnd() { gameObject.SetActive(false); // 对象回收 s_objPool.Enqueue(this); }private static GameObject s_prefab; /// /// 对象池 /// private static Queue s_objPool = new Queue(); /// /// 根节点 /// private static Transform s_root; /// /// 文字组件 /// public Text keyText; }

完成。
如果有什么疑问,欢迎留言或私信。
《学Unity的猫》——第十四章:Unity实现文件上传下载,支持续传,猫后爪的秘密

    推荐阅读