2-14 Using Interfaces In C#
本章要來介紹Interface在C#的妙用,除去大家可能習以為常的設計模式之類的,今天要談的是運用Interface在Unity中,製作一個可以進行攻擊的物件,並使Player跟Enemy損血。
首先,在Project視窗中找到一個RollerBall物件,這東西放在Standard Asset中,若沒有就請大家自己匯入吧。
上述設定方式,是為確保Trigger可以跟任何物件進行反應,大家可以從Untiy官網有關Collider教學中看到表格中有相關介紹。
https://docs.unity3d.com/Manual/CollidersOverview.html
讓我們來執行遊戲吧,當Player經過那顆球球時,就會因為碰觸到而損血。
假如引誘敵人去碰觸那顆球球,也會跟著損血了。
首先,在Project視窗中找到一個RollerBall物件,這東西放在Standard Asset中,若沒有就請大家自己匯入吧。
接著替RollerBall更名為Projectile,並且新增Rigidbody、Sphere Collider,Rigidbody的Is Kinematic打勾,Sphere Collider的Is Trigger打勾。簡言之依照下圖進行設定。
上述設定方式,是為確保Trigger可以跟任何物件進行反應,大家可以從Untiy官網有關Collider教學中看到表格中有相關介紹。
https://docs.unity3d.com/Manual/CollidersOverview.html
Trigger messages are sent upon collision | ||||||
---|---|---|---|---|---|---|
Static Collider | Rigidbody Collider | Kinematic Rigidbody Collider | Static Trigger Collider | Rigidbody Trigger Collider | Kinematic Rigidbody Trigger Collider | |
Static Collider | Y | Y | ||||
Rigidbody Collider | Y | Y | Y | |||
Kinematic Rigidbody Collider | Y | Y | Y | |||
Static Trigger Collider | Y | Y | Y | Y | ||
Rigidbody Trigger Collider | Y | Y | Y | Y | Y | Y |
Kinematic Rigidbody Trigger Collider | Y | Y | Y | Y | Y | Y |
接下來,新增一個名為IDamageable的Interface吧。
IDamageable的寫法如下:
public interface IDamageable{ void TakeDamage(float damage); }
然後,讓Player.cs跟Enemy.cs實作IDamageable的TakeDamage方法,亦即當有其他物件碰觸到Player跟Enemy時,都會呼叫TakeDamage方法。
Player.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour, IDamageable { [SerializeField] float maxHealthPoints = 100f; float currentHealthPoint = 100f; public float healthAsPercentage{ get{ return currentHealthPoint / maxHealthPoints; } } public void TakeDamage(float damage){ // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值 currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints); } }
Enemy.cs:
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 = 5.0f; float currentHealthPoint = 100f; AICharacterControl aiCharacterControl = null; GameObject player; 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) { aiCharacterControl.SetTarget (player.transform); } else { aiCharacterControl.SetTarget (transform); } } public void TakeDamage(float damage){ // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值 currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints); } }
最後,我們要替那顆球Projectile新增Projectile.cs。此時,就能夠看出Interface的好處了,不管球球碰到的是Player還是Enemy,我們呼叫的Function都是來自IDamageable的TakeDamage。若是不透過Interface的寫法,我們就必須要判斷碰到的是Player還是Enemy然後再呼叫個別的TakeDamage。顯而易見,後者的做法就無法達到統一呼叫的作用了。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Projectile : MonoBehaviour { [SerializeField] float damageCaused = 10f; void OnTriggerEnter(Collider collider){ // 取得Component為IDamageable Component damageableComponent = collider.gameObject.GetComponent (typeof(IDamageable)); // 若IDamageable存在 if (damageableComponent) { // 呼叫damageableComponent的TakeDamage方法 (damageableComponent as IDamageable).TakeDamage (damageCaused); } } }
讓我們來執行遊戲吧,當Player經過那顆球球時,就會因為碰觸到而損血。
假如引誘敵人去碰觸那顆球球,也會跟著損血了。
留言
張貼留言