2-16 InvokeRepeating() vs StartCoroutine()
本章要來介紹控制敵人發射Projectile的方法,繼上一章我們完成了讓敵人發射Projectile的方法(每個Frame都發射),這一章要介紹如何控制發射的速度,以及當玩家離開敵人的攻擊範圍時,敵人就必須停止發射的方法。
首先,我們這次會用到Untiy的InvokeRepeating及StartCoroutine方法,如不清楚的話請參閱Untiy官方提供的教學:
MonoBehaviour.InvokeRepeating
https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
MonoBehaviour.StartCoroutine
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
兩者最大的差異,在於InvokeRepeating呼叫方法時是string reference,這對於未來進行方法的重構時非常糟糕,不但找錯誤不方便,編輯器也無法自動幫你修改名稱,所以再三建議大家盡量不要用string reference的方法。再者,InvokeRepeating只能單一支援延遲執行跟每次執行的頻率,若在方法內部想要針對特殊情況進行暫停、修改執行頻率等等都不太可能。
相對StartCoroutine就能辦到,所以StartCoroutine是非常強大又好用的方法。
以下兩種寫法都會介紹。首先我設置了一個bool型別的變數isAttacking,然後使用InvokeRepeating呼叫SpawnProjectile。
這時進入敵人的攻擊範圍後,敵人就會開始發射Projectile,並且不會再向之前那樣連續發射。
然後以下是用StartCoroutine的寫法:
完成!執行遊戲以後,玩家進入攻擊範圍敵人便開始攻擊,離開範圍後便會停止攻擊。
首先,我們這次會用到Untiy的InvokeRepeating及StartCoroutine方法,如不清楚的話請參閱Untiy官方提供的教學:
MonoBehaviour.InvokeRepeating
https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
MonoBehaviour.StartCoroutine
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
兩者最大的差異,在於InvokeRepeating呼叫方法時是string reference,這對於未來進行方法的重構時非常糟糕,不但找錯誤不方便,編輯器也無法自動幫你修改名稱,所以再三建議大家盡量不要用string reference的方法。再者,InvokeRepeating只能單一支援延遲執行跟每次執行的頻率,若在方法內部想要針對特殊情況進行暫停、修改執行頻率等等都不太可能。
相對StartCoroutine就能辦到,所以StartCoroutine是非常強大又好用的方法。
以下兩種寫法都會介紹。首先我設置了一個bool型別的變數isAttacking,然後使用InvokeRepeating呼叫SpawnProjectile。
這時進入敵人的攻擊範圍後,敵人就會開始發射Projectile,並且不會再向之前那樣連續發射。
然後以下是用StartCoroutine的寫法:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using UnityStandardAssets.Characters.ThirdPerson; public class Enemy : MonoBehaviour, IDamageable { [SerializeField] float maxHealthPoints = 100f; [SerializeField] float attackRadius = 3.0f; [SerializeField] float chaseRadius = 10.0f; [SerializeField] float damagePerShot = 9f; [SerializeField] float secondsBetweenShots = 0.5f; [SerializeField] GameObject projectileToUse; [SerializeField] GameObject projectileSocket; bool isAttacking = false; float currentHealthPoint = 100f; AICharacterControl aiCharacterControl = null; GameObject player; IEnumerator coroutine; public float healthAsPercentage{ get{ return currentHealthPoint / maxHealthPoints; } } void Start(){ aiCharacterControl = GetComponent(); player = GameObject.FindGameObjectWithTag ("Player"); } void Update(){ // 計算Player跟Enemy的距離 float distanceToPlayer = Vector3.Distance (player.transform.position, transform.position); // 若彼此之間的距離小於attackRadius,就讓Enemy開始攻擊Player if (distanceToPlayer <= attackRadius && !isAttacking) { isAttacking = true; coroutine = SpawnProjectile (); StartCoroutine (coroutine); } if (distanceToPlayer > attackRadius && isAttacking) { isAttacking = false; StopCoroutine (coroutine); } // 若彼此之間的距離小於chaseRadius,就讓Enemy開始追蹤Player if (distanceToPlayer <= chaseRadius) { aiCharacterControl.SetTarget (player.transform); } else { aiCharacterControl.SetTarget (transform); } } IEnumerator SpawnProjectile(){ while (true) { yield return new WaitForSeconds (secondsBetweenShots); // Instantiate可以生成GameObject,Quaternion.identity為方向不進行任何轉向 GameObject newProjectile = Instantiate(projectileToUse, projectileSocket.transform.position, Quaternion.identity); // 取得Projectile Projectile projectileComponent = newProjectile.GetComponent (); // 設定Projectile的攻擊威力 projectileComponent.SetDamage(damagePerShot); // 計算發射Projectile到Player之間的單位向量 Vector3 unitVectorToPlayer = (player.transform.position - projectileSocket.transform.position).normalized; float projectileSpeed = projectileComponent.projectileSpeed; // 將單位向量乘以發射速度,透過velocity將Projectile發射出去吧! newProjectile.GetComponent ().velocity = unitVectorToPlayer * projectileSpeed; } } public void TakeDamage(float damage){ // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值 currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints); } void OnDrawGizmos(){ //繪製攻擊範圍 Gizmos.color = new Color(255f, 0f, 0f, 0.5f); Gizmos.DrawWireSphere (transform.position, attackRadius); //繪製移動範圍 Gizmos.color = new Color(0f, 0f, 255f, 0.5f); Gizmos.DrawWireSphere (transform.position, chaseRadius); } }
完成!執行遊戲以後,玩家進入攻擊範圍敵人便開始攻擊,離開範圍後便會停止攻擊。
留言
張貼留言