Related Posts Plugin for WordPress, Blogger...

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不用寫任何程式碼,他只是一個標誌。

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);
 }
}


執行遊戲後,確定可以使用。


留言