Related Posts Plugin for WordPress, Blogger...

2-25 Introducing Scriptable Objects

今天要來介紹使用Scriptable Objects,在我們遊戲中使用Scriptable Objects來更換武器。在我的理解中,可以把Scriptable Object當作進階的enum型別,又或者是物件導向概念中的MVC框架的Model。既可以用來儲存多種類型的資料跟方法,又可以在Unity Inspector中很方便的使用,撰寫出來以後讓遊戲設計師、關卡設計師用作調整跟測試是非常方便的。

首先,先引用Untiy官方的介紹:
https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects

Scriptable Objects are amazing data containers. They don't need to be attached to a GameObject in a scene. They can be saved as assets in our project. Most often, they are used as assets which are only meant to store data, but can also be used to help serialize objects and can be instantiated in our scenes. Tutor: Adam Buckner

接下來,我們將原本放在Player底下的武器刪除,因為之後要透過Scriptable Object把武器綁定上去。如下圖我的武器放在Armature/Hips/Spine/Shoulder_R/Uppder_Arm_R/Lower_Arm_R/Hand_R/Dirt Old Hoe。

然後呢,先將要測試用的武器放到場景中。

新增一個名為Weapon的Script。

Weapon.cs裡面要繼承ScriptableObject,我們在裡面宣告用來放武器的weaponPrefab,跟用來放攻擊動作的attackAnimation,再來是呼叫Instantiate時指定父物件的gripTransform。程式碼如下:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName="RPG/Weapon")]
public class Weapon : ScriptableObject {

 public Transform gripTransform;

 [SerializeField] GameObject weaponPrefab;
 [SerializeField] AnimationClip attackAnimation;

 public GameObject GetWeaponPrefab(){
  return weaponPrefab;
 }
}


然後,因為第五行的[CreateAssetMenu(menuName="RPG/Weapon")],我們便可以在Project視窗增加右鍵的選項,如下圖,選擇RPG/Weapon後便會新增一個ScriptableObject。

新增以後,取名為Dirty Old Hoe。

然後再新增一個Prefab,名稱為DirtyOldHoeGripPosition,此Prefab將用來記錄武器的Position跟Rotation。


接下來,我們就可以把武器物件的Prefab、指定位置的DirtyOldHoeGripPosition拉到名為Dirty Old Hoe的ScriptableObject。到這個步驟後,大家應該就能了解ScriptableObject就像是加強版的enum型別了吧!可以儲存多種物件(不局限於基本型別),並可透過Inspector視窗隨意新增跟指定參考。

再來要修改Player.cs,讓我們可以在Inspector上放置Weapon的ScriptableObject。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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;
 [SerializeField] GameObject weaponSocket;

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

 void PutWeaponInHand(){
  GameObject weaponPrefab = weaponInUse.GetWeaponPrefab ();
  // 生成武器時,將會放置到指定的GameObject之下,此處為WeaponSocket之下
  // 故可將WeaponSocket設置成玩家的右手或左手
  GameObject weapon = Instantiate (weaponPrefab, weaponSocket.transform);
  // 將武器放置到正確的位置,並有正確的方向
  weapon.transform.localPosition = weaponInUse.gripTransform.localPosition;
  weapon.transform.localRotation = weaponInUse.gripTransform.localRotation;
 }

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


然後請如下圖放置,Weapon In Use放置Dirty Old Hoe,然後Weapon Socket放在Hand_R。

執行遊戲以後,就能發現我們的Hoe放到手上了,但位置還不是我們要的,這是因為GripTransform的預設Position跟Rotation都是0, 0, 0。

讓我們在遊戲中實際調整位置跟角度,調整完後直接在Inspector中複製Transform。

並將複製好的Transform貼上到DirtyOldHoeGripPosition。

如此一來,我們就可以快速設定我們想要的武器,還包含座標跟方向了!

然後我再接著新增新的武器,哈哈!武器的位置不對,都貫穿身體了。

依照上述的方式調整以後,就可以正常使用這把大劍了!以後換武器,只要透過Weapon的ScriptableObject更換即可,就算負責遊戲設計跟關卡設計的人不懂程式,也可以輕鬆達成。

留言