2-26 Problems With Prefabs
本章要來討論Prefab跟Instance之間的問題,我們撰寫程式時應減少『Prefab link to Instance』,意思是說Script程式碼中要減少直接放入GameObject,Instance是指C#寫成的Script,Prefab是指GameObject。
繼上一章我們使用ScriptableObject製作切換武器的功能中,就讓Player.cs使用WeaponSocket儲存Hand_R的Prefab。
這會稍稍有一個小問題,我們在選擇GameObject時,在Scene中可以找得到Hand_R。
但是在Assets中找不到。
目前我們的解法是,就讓instance連結到instance。在Hand_R底下新增一個DominantHand的Script。
DominantHand不用寫任何程式碼,他只是一個標誌。
然後,將Player.cs修改如下,註解也在程式碼中:
執行遊戲後,確定可以使用。
繼上一章我們使用ScriptableObject製作切換武器的功能中,就讓Player.cs使用WeaponSocket儲存Hand_R的Prefab。
這會稍稍有一個小問題,我們在選擇GameObject時,在Scene中可以找得到Hand_R。
但是在Assets中找不到。
目前我們的解法是,就讓instance連結到instance。在Hand_R底下新增一個DominantHand的Script。
DominantHand不用寫任何程式碼,他只是一個標誌。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DominantHand : MonoBehaviour {
// Add to dominant hand on character hand
}
然後,將Player.cs修改如下,註解也在程式碼中:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class Player : MonoBehaviour, IDamageable {
[SerializeField] float maxHealthPoints = 100f;
[SerializeField] float damagePerHit = 50f;
[SerializeField] float minTimeBetweenHits = 0.5f;
[SerializeField] float maxAttackRange = 5f;
[SerializeField] Weapon weaponInUse;
GameObject currentTarget;
float currentHealthPoint;
float lastHitTime;
CameraRaycaster cameraRaycaster;
public float healthAsPercentage{
get{ return currentHealthPoint / maxHealthPoints; }
}
void Start(){
// 初始化血量
currentHealthPoint = maxHealthPoints;
cameraRaycaster = FindObjectOfType ();
cameraRaycaster.notifyMouseClickObservers += OnMouseClick ;
//初始化武器,放置到手中
PutWeaponInHand ();
}
private void PutWeaponInHand(){
GameObject weaponPrefab = weaponInUse.GetWeaponPrefab ();
// 生成武器時,將會放置到指定的GameObject之下,此處為WeaponSocket之下
// 故可將WeaponSocket設置成玩家的右手或左手
GameObject weaponSocket = RequestDominantHand();
GameObject weapon = Instantiate (weaponPrefab, weaponSocket.transform);
// 將武器放置到正確的位置,並有正確的方向
weapon.transform.localPosition = weaponInUse.gripTransform.localPosition;
weapon.transform.localRotation = weaponInUse.gripTransform.localRotation;
}
private GameObject RequestDominantHand(){
// 從Children中尋找包含DominantHand的物件
DominantHand[] dominantHand = GetComponentsInChildren ();
// 計算取得有DominantHand的物件的數量
int numberOfDominantHands = dominantHand.Length;
// 確保該數量不為0
Assert.AreNotEqual (numberOfDominantHands, 0, "No DominantHand found on player, please add one");
// 確保該數量不要大於1
Assert.IsFalse (numberOfDominantHands > 1, "Multiple Dominant script on Player, please remove one");
// 傳回屬於該DominantHand的GameObject
return dominantHand [0].gameObject;
}
void OnMouseClick(RaycastHit raycastHit, int layerHit){
const int enemyLayer = 9;
if (layerHit == enemyLayer) {
GameObject enemy = raycastHit.collider.gameObject;
// 確認敵人是否在範圍技的攻擊範圍內
if ((enemy.transform.position - transform.position).magnitude > maxAttackRange) {
return; // 超過攻擊範圍就跳出
}
// 控制攻擊頻率,若本次攻擊時間離上一次攻擊時間大於minTimeBetweenHits,才允許攻擊
// 相當於範圍技的CD時間,不能一直發動技能!
if (Time.time - lastHitTime > minTimeBetweenHits) {
currentTarget = enemy;
IDamageable enemyDamageable = enemy.GetComponent (typeof(IDamageable)) as IDamageable;
// 讓敵人損血
enemyDamageable.TakeDamage (damagePerHit);
// 紀錄目前攻擊的時間
lastHitTime = Time.time;
}
}
}
public void TakeDamage(float damage){
// Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值
currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints);
}
}
執行遊戲後,確定可以使用。





留言
張貼留言