2-28 Projectiles Discriminate
今天要來改進Enemy發射Projectile時也同樣會攻擊到Enemy的問題。解決方法是在Projectile中增加條件式,判斷發射Projectile的GameObject所處的Layer。比方說,敵人的Layer是Enemy,那由Enemy Layer的GameObject發射的Projectile,就不應該攻擊到同樣在Enemy Layer的Projectile。
所以首先我們先替玩家角色新增Layer,我們之前一直沒有新增Player。
所以首先我們先替玩家角色新增Layer,我們之前一直沒有新增Player。
然後修改Projectile.cs,我們新增一個GameObject名為Shooter,並判斷Collision GameObject的Layer是否不同於Shooter的Layer:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Projectile : MonoBehaviour {
[SerializeField] float projectileSpeed;
[SerializeField] GameObject shooter;
float damageCaused;
public void SetShooter(GameObject shooter){
this.shooter = shooter;
}
public void SetDamage(float damage){
damageCaused = damage;
}
public float GetDefaultLaunchSpeed(){
return projectileSpeed;
}
void OnCollisionEnter(Collision collision){
int layerCollidedWith = collision.gameObject.layer;
// 若被碰撞的物體所處的Layer跟shooter的Layer不同,才會觸發攻擊機制
// 如敵人就不會攻擊到敵人
if (layerCollidedWith != shooter.layer) {
DamageIfDamageable (collision);
}
}
private void DamageIfDamageable(Collision collision){
// 取得Component為IDamageable
Component damageableComponent = collision.gameObject.GetComponent (typeof(IDamageable));
// 若IDamageable存在
if (damageableComponent) {
// 呼叫damageableComponent的TakeDamage方法
(damageableComponent as IDamageable).TakeDamage (damageCaused);
}
// 自我消滅
Destroy(gameObject);
}
}
接著,因為Enemy發射Projectile,所以修改Enemy.cs,呼叫Projectile的SetShooter方法:
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;
[SerializeField] Vector3 aimOffset = new Vector3(0, 1f, 0);
bool isAttacking = false;
float currentHealthPoint;
AICharacterControl aiCharacterControl = null;
GameObject player;
IEnumerator coroutine;
public float healthAsPercentage{
get{ return currentHealthPoint / maxHealthPoints; }
}
void Start(){
currentHealthPoint = maxHealthPoints;
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的Shooter為自己
projectileComponent.SetShooter (gameObject);
// 計算發射Projectile到Player之間的單位向量
Vector3 unitVectorToPlayer = (player.transform.position + aimOffset - projectileSocket.transform.position).normalized;
float projectileSpeed = projectileComponent.GetDefaultLaunchSpeed();
// 將單位向量乘以發射速度,透過velocity將Projectile發射出去吧!
newProjectile.GetComponent ().velocity = unitVectorToPlayer * projectileSpeed;
}
}
public void TakeDamage(float damage){
// Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值
currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints);
// 敵人血量低於0就會消失
if (currentHealthPoint <= 0) {
Destroy (gameObject);
}
}
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);
}
}
最後,測試遊戲看看Enemy的Projectile是否會攻擊到Enemy。


留言
張貼留言