Related Posts Plugin for WordPress, Blogger...

2-28 Projectiles Discriminate

今天要來改進Enemy發射Projectile時也同樣會攻擊到Enemy的問題。解決方法是在Projectile中增加條件式,判斷發射Projectile的GameObject所處的Layer。比方說,敵人的Layer是Enemy,那由Enemy Layer的GameObject發射的Projectile,就不應該攻擊到同樣在Enemy Layer的Projectile。

所以首先我們先替玩家角色新增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。


留言