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); } }
執行遊戲後,確定可以使用。
留言
張貼留言