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經過那顆球球時,就會因為碰觸到而損血。
假如引誘敵人去碰觸那顆球球,也會跟著損血了。





留言
張貼留言