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:

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
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方法:

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
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<aicharactercontrol> ();
  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> ();
   // 設定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<rigidbody> ().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);
 }
}
 
</rigidbody></projectile></aicharactercontrol>

最後,測試遊戲看看Enemy的Projectile是否會攻擊到Enemy。


留言