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);
}
}
完成!執行遊戲以後,玩家進入攻擊範圍敵人便開始攻擊,離開範圍後便會停止攻擊。



留言
張貼留言