Related Posts Plugin for WordPress, Blogger...

3-14 Player Death Animation

製作好玩家死亡時播放的音效後,接著要來製作玩家死亡後要播放特定的動畫。首先,在Player的Animator中新增State。


將State取名為Death,Motion選擇Armed-Death1。如不知道這個Motion是去哪邊下載的,請大家先從以下文章連結進行下載:
2-27 Import Mechanim Animation Pack


接著在Grounded中按右鍵,選擇Make Transition,將其連結到Death。

然後在Attack中也建立連結,連到Death。

新增一個名為Death的Trigger。

然後點擊Grounded到Death之間的連結線,從Inspector視窗中增加Conditions,選擇Death。

同理在Attack到Death的連結中,Conditions也選擇Death。

另外,我將Has Exit Time關掉,不然玩家死掉之後還要等快1秒的時間,人物才會倒下去。大家可以自由調整Exit Time,我喜歡讓玩家直接死掉,所以就關掉了。


底下提供Player.cs的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.SceneManagement;
using RPG.CameraUI; // TODO consider re-wiring
using RPG.Core;
using RPG.Weapons;
 
namespace RPG.Character{
 public class Player : MonoBehaviour, IDamageable {
 
  [SerializeField] float maxHealthPoints = 100f;
  [SerializeField] float baseDamage = 50f;
  [SerializeField] AnimatorOverrideController animatorOverrideController;
  [SerializeField] Weapon weaponInUse;
  [SerializeField] SpecialAbilityConfig[] abilities;
  [SerializeField] AudioClip[] damageSounds;
  [SerializeField] AudioClip[] deathSounds;
 
  // 定義Trigger的字串常數
  const string DEATH_TRIGGER = "Death";
  const string ATTACK_TRIGGER = "Attack";
 
  float lastHitTime;
  float currentHealthPoint;
  CameraRaycaster cameraRaycaster;
  ISpecialAbility[] specialAbility;
  AudioSource audioSource;
  Animator animator;
 
 
  public float healthAsPercentage{
   get{ return currentHealthPoint / maxHealthPoints; }
  }
 
  void Start(){
   // 初始化血量
   currentHealthPoint = maxHealthPoints;
   cameraRaycaster = FindObjectOfType<cameraraycaster> ();
   // 註冊滑鼠碰到敵人的事件
   cameraRaycaster.onMouseOverEnemy += OnMouseOverEnemy;
   // 初始化武器,放置到手中
   PutWeaponInHand ();
   // 覆寫人物角色中的Animation
   OverrideAnimatorController ();
   // 替Player增加技能的Behaviour,並回傳ISpecialAbility物件
   AttachSpecialAbility();
   audioSource = GetComponent<audiosource> ();
  }
 
  private void AttachSpecialAbility(){
   specialAbility = new ISpecialAbility[abilities.Length];
   for (int i = 0; i < abilities.Length; i++) {
    // 儲存ISpecialAbility物件
    specialAbility[i] = abilities [i].AddComponent (gameObject);
   }
  }
 
  private void OverrideAnimatorController(){
   // 取得Animator物件
   animator = GetComponent<animator> ();
   // 將人物的Animator取代為我們剛剛新增的Player Animator Override
   animator.runtimeAnimatorController = animatorOverrideController;
   // Player Animator Override中有一預設的DEFAULT ATTACK,將其取代成Weapon物件中的Animation
   // 亦即人物的Animation將會依據不同的Weapon物件,而使用有不同的動畫
   animatorOverrideController ["DEFAULT ATTACK"] = weaponInUse.GetAnimClip ();
  }
 
  private void PutWeaponInHand(){
   GameObject weaponPrefab = weaponInUse.GetWeaponPrefab ();
   // 生成武器時,將會放置到指定的GameObject之下,此處為WeaponSocket之下
   // 故可將WeaponSocket設置成玩家的右手或左手
   GameObject weaponSocket = RequestDominantHand();
   GameObject weapon = Instantiate (weaponPrefab, weaponSocket.transform);
   // 將武器放置到正確的位置,並有正確的方向
   weapon.transform.localPosition = weaponInUse.gripTransform.localPosition;
   weapon.transform.localRotation = weaponInUse.gripTransform.localRotation;
  }
 
  private GameObject RequestDominantHand(){
   // 從Children中尋找包含DominantHand的物件
   DominantHand[] dominantHand = GetComponentsInChildren<dominanthand> ();
   // 計算取得有DominantHand的物件的數量
   int numberOfDominantHands = dominantHand.Length;
   // 確保該數量不為0
   Assert.AreNotEqual (numberOfDominantHands, 0, "No DominantHand found on player, please add one");
   // 確保該數量不要大於1
   Assert.IsFalse (numberOfDominantHands > 1, "Multiple Dominant script on Player, please remove one");
   // 傳回屬於該DominantHand的GameObject
   return dominantHand [0].gameObject;
  }
 
  void OnMouseOverEnemy(Enemy enemy){
   if (Input.GetMouseButtonDown (0) && IsTargetInRange (enemy)) {
    AttackTarget (enemy);
   }else if (Input.GetMouseButtonDown (1)) {
    // 0為使用第一個技能
    AttemptSpecialAbility(0, enemy);
   }
  }
 
  private void AttemptSpecialAbility(int abilityIndex, Enemy enemy){
   Energy energyComponent = GetComponent<energy> ();
   // 取得技能需要的能量消耗量
   float energyCost = abilities [abilityIndex].GetEnergyCost ();
 
   if (energyComponent.IsEnergyAvailable (energyCost)) {
    energyComponent.ConsumeEnergy (energyCost);
    // 發動技能,並傳入Player的baseDamage
    AbilityParams abilityParams = new AbilityParams (enemy, baseDamage);
    specialAbility [abilityIndex].Use(abilityParams);
   }
 
  }
 
  private void AttackTarget(Enemy enemy){
   // 確認本次攻擊時間離上一次攻擊時間須大於minTimeBetweenHits,相當於技能冷卻時間
   if ( (Time.time - lastHitTime > weaponInUse.GetMinTimeBetweenHits())) {
    // 呼叫Trigger啟動攻擊動畫
    animator.SetTrigger(ATTACK_TRIGGER);
    // 呼叫攻擊Target的方法
    enemy.TakeDamage(baseDamage);
    // 紀錄目前攻擊的時間
    lastHitTime = Time.time;
   }
  }
 
  // 確認敵人是否在範圍技的攻擊範圍內
  private bool IsTargetInRange(Enemy enemy){
   float distanceToTarget = (enemy.transform.position - transform.position).magnitude;
   return distanceToTarget <= weaponInUse.GetMaxAttackRange();
  }
 
  public void TakeDamage(float damage){
   // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值
   currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints);
   // 隨機播放受傷的音效
   audioSource.clip = damageSounds [Random.Range (0, damageSounds.Length)];
   audioSource.Play ();
   bool playerDies = currentHealthPoint <= 0;
   if (playerDies) {
    StartCoroutine (KillPlayer ());
   }
  }
 
  IEnumerator KillPlayer(){
   // 播放死亡音效
   audioSource.clip = deathSounds [Random.Range (0, deathSounds.Length)];
   audioSource.Play ();
   // 播放死亡動畫
   animator.SetTrigger(DEATH_TRIGGER);
   // 等待一段時間(依音效長度而定)
   yield return new WaitForSecondsRealtime(audioSource.clip.length);
   // 重載關卡
   SceneManager.LoadScene(0);
  }
 }
}
</energy></dominanthand></animator></audiosource></cameraraycaster>


最後,執行遊戲看看,發現血量沒有了以後,角色就倒下啦!


留言