在研究了Unity3D Mecanim动画系统的重定向特性后,今天我们继续来探索Mecanim动画系统更多的特性吧。今天狗刨网想和大家分享的是ACT游戏中的三连击效果的实现,由于Unity3D目前存在Animation和Animator两种类型的动画组件,因此本文将分别讲这两种类型的动画组件的三连击效果的实现,其中Animation组件是Unity3.5以下版本所使用的动画组件,Animator组件是目前Unity3D的Mecanim动画系统所使用的动画组件。
我们首先来了解三连击效果的具体流程,假定角色当前处于Idle状态,此时玩家如果按下攻击键则进入Attack1状态,如果在规定的时间内,玩家继续按下攻击键则进入Attack2状态,否则返回到Idle状态;同理,如果角色处于Attack2状态,如果此时玩家按下攻击键则进入Attack3状态,否则返回Idle状态;当Attack3状态结束后,将返回到Idle状态,等待玩家触发下一次攻击。由此我们可以归纳出三连击的状态变化:
(1)Idle->Attack1->Idle
(2)Idle->Attack1->Attack2->Idle
(3)Idle->Attack1->Attack->Attack3->Idle
通过状态变化情况我们可以考虑使用两种思路来实现三连击效果。第一种思路是,各状态动画相互独立,通过状态切换来实现整体的动画效果。第二种思路是,美工人员将各状态动画以序列形式,程序设计人员根据时间来控制动画效果。今天我们主要采用第一种方法,目的是领会游戏设计中的有限状态机思想,将其更好的应用到游戏开发中。好了,下面我们正式开始今天的内容吧!
一、Animator组件篇
Animator组件是Unity3D的Mecanim动画系统所使用的动画组件。这个组件通过Animator Controller来实现对动画的控制。如图是我们今天项目的主角,一个左手持盾,右手持剑的女战士。
我们首先来创建一个Animator Controller并将其命名为SwordGirlController,双击打开Animator窗口,按照我们对状态变化情况的讨论,我们可以很容易地设计出下面的状态模型:
在这里我们定义一个整型变量ActionID,其默认值为0。当ActionID的值为1时角色由Idle切换到Attack1,当ActionID的值为2时角色由Attack1切换到Attack2,当ActionID为3时角色由Attack2切换到Attack3。所有的指向Idle的连线的切换条件是ActionID的值为0。这样我们就建立了一个动画切换的状态模型。好了,下面我们来编写脚本实现对动画的控制:
[csharp] view plaincopyprint?
1. //基于Mecanim动画系统的三连击效果,目前最大的问题就是玩家在攻击后无法
2. //自动恢复到Idle状态,需要执行一次攻击才可以回到Idle状态
3.
4. using UnityEngine;
5. using System.Collections;
6.
7. public class SwordGirlScript : MonoBehaviour {
8.
9. //Mecanim动画组件
10. private Animator mAnimator=null;
11. //动画状态信息
12. private AnimatorStateInfo mStateInfo;
13. //定义状态常量值,前面不要带层名啊,否则无法判断动画状态
14. private const string IdleState="Idle";
15. private const string Attack1State="Attack1";
16. private const string Attack2State="Attack2";
17. private const string Attack3State="Attack3";
18.
19. //定义玩家连击次数
20. private int mHitCount=0;
21.
22. void Start ()
23. {
24. //获取动画组件
25. mAnimator=GetComponent<Animator>();
26. //获取状态信息
27. mStateInfo=mAnimator.GetCurrentAnimatorStateInfo(0);
28. }
29.
30. void Update ()
31. {
32. //如果玩家处于攻击状态,且攻击已经完成,则返回到Idle状态
33. if(!mStateInfo.IsName(IdleState) && mStateInfo.normalizedTime>1.0F)
34. {
35. mAnimator.SetInteger("ActionID",0);
36. mHitCount=0;
37. }
38. //如果按下鼠标左键,则开始攻击
39. if(Input.GetMouseButton(0))
40. {
41. Attack();
42. }
43. }
44.
45. void Attack()
46. {
47. //获取状态信息
48. mStateInfo=mAnimator.GetCurrentAnimatorStateInfo(0);
49. //如果玩家处于Idle状态且攻击次数为0,则玩家按照攻击招式1攻击,否则按照攻击招式2攻击,否则按照攻击招式3攻击
50. if(mStateInfo.IsName(IdleState) && mHitCount==0 && mStateInfo.normalizedTime>0.50F)
51. {
52. mAnimator.SetInteger("ActionID",1);
53. mHitCount=1;
54. }else if(mStateInfo.IsName(Attack1State) && mHitCount==1 && mStateInfo.normalizedTime>0.65F)
55. {
56. mAnimator.SetInteger("ActionID",2);
57. mHitCount=2;
58. }else if(mStateInfo.IsName(Attack2State)&& mHitCount==2 && mStateInfo.normalizedTime>0.70F)
59. {
60. mAnimator.SetInteger("ActionID",3);
61. mHitCount=3;
62. }
63. }
64. }
这样我们就可以实现ACT游戏中的三连击效果了,我们一起来看看最终的效果吧!
狗刨网在实际测试的过程中发现角色在执行一次攻击后无法自动恢复到Idle状态,除非玩家继续按下攻击键,狗刨网目前并没有找到解决的办法,如果各位朋友知道具体原因的话,一定要告诉狗刨网啊,可以留言哦。
二、Animation组件篇
我们知道Mecanim动画系统是通过状态机来实现对动画的控制的,而在3.5版本以前的Unity3D所采用的动画系统呢,按照狗刨网的理解,其实就是一种无状态的动画,我们只能通过动画片段的名称来决定播放某一个动画片段或者在一个确定的时间内进行动画的切换。因此,如果我们希望使用Animation组件来实现三连击效果的话,就必须在该组件的基础上实现一个状态机的结构。根据开始讨论的结果,我们知道整个三连击过程中有四个状态Attack1、Attack2、Attack3、Idle,由此我们可以定义一个动画状态的枚举类型ActionState。接下来我们就可以根据这个状态值来切换攻击动画,实现三连击的效果,我们一起来看脚本:
[csharp] view plaincopyprint?
1. using UnityEngine;
2. using System.Collections;
3.
4. public class AttackScripts : MonoBehaviour {
5.
6. //当前攻击动画;
7. AnimationClip currentClip;
8. //动画组件;
9. Animation mAnimation;
10.
11. //动画状态枚举
12. public enum ActionState
13. {
14. Attack1,
15. Attack2,
16. Attack3,
17. None
18. }
19.
20. //当前动画状态;
21. private ActionState mState = ActionState.None;
22.
23. //攻击触发
24. void AttackTrigger(){
25. if (Input.GetMouseButton(0)){
26. if (mState != ActionState.Attack1 && mState != ActionState.Attack2 && mState != ActionState.Attack3 ) {
27. mState = ActionState.Attack1;
28. }else if (mState == ActionState.Attack1 && mState != ActionState.Attack2 && mState != ActionState.Attack3
29. && mAnimation[currentClip.name].time > 1.0F){
30. mState = ActionState.Attack2;
31. }else if (mState == ActionState.Attack2 && mState != ActionState.Attack1 && mState != ActionState.Attack3
32. && mAnimation[currentClip.name].time > 1.0F){
33. mState = ActionState.Attack2;
34. }
35. }
36. }
37.
38. //攻击套路
39. void Attacks (){
40. float delayTime =0.0F;
41. switch (mState){
42. case ActionState.Attack1:
43. delayTime = -0.1F;
44. mAnimation.CrossFade("Attack1", 0.15F);
45. currentClip = mAnimation["Attack1"].clip;
46. break;
47.
48. case ActionState.Attack2:
49. delayTime = -0.1f;
50. mAnimation.CrossFade("Attack2", 0.15F);
51. currentClip = mAnimation["Attack2"].clip;
52. break;
53.
54. case ActionState.Attack3:
55. delayTime = -0.1f;
56. mAnimation.CrossFade("Attack3", 0.15F);
57. currentClip = mAnimation["Attack3"].clip;
58. break;
59.
60. case ActionState.None:
61. break;
62. }
63.
64. //如果攻击动画播放完就切换到Idle状态
65. if (mAnimation[currentClip.name].time > (mAnimation[currentClip.name].length +delayTime)){
66. mState = ActionState.None;
67. currentClip = mAnimation["Idle"].clip;
68. }
69. }
70.
71. void Awake(){
72. //获取动画组件;
73. mAnimation = GetComponent<Animation>();
74. }
75.
76. void Start () {
77. if (mAnimation.clip) {
78. currentClip = mAnimation.clip;
79. } else {
80. currentClip = mAnimation["Idle"].clip;
81. }
82. }
83.
84. void Update () {
85. if (currentClip != null){
86. AttackTrigger();
87. Attacks();
88. }
89. }
90. }
这里的连击效果并没有设置打断,即所有的动画一次性播放完毕。如果我们在Mecanim动画系统中仅仅保留第三种情形,那么它实现的效果是一样的,这一点希望大家注意。