最近在寫遊戲時遭遇一點困難:在繼承基礎類別時,需要將其附屬的資料類別一併繼承,而在建立繼承類別的實體後,遇到初始化基礎類別的資料類別的問題,原因只是因為兩者的欄位 (field) 同名。有問題的程式碼像這樣:
class Character : MonoBehaviour
{
protected CharacterData data;
...
}
class CharacterData : ScriptableObject
{
public float movingVelocity = 10.0f;
public float rotatingVelocity = 10.0f;
}
class Player : Character
{
public new PlayerData data;
private void Start()
{
base.data = data;
}
...
}
[CreateAssetMenu(fileName = "PlayerData",
menuName = "Scriptable Object/PlayerData", order = 1)]
class PlayerData : CharacterData
{
public float firingPeriod = 0.1f;
}
Unity 在編譯後會出現錯誤訊息,"The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(Player) data":即使可以忽略錯誤訊息將 PlayerData 的 scriptable object 設給 data 欄位:
但只要重新編譯後,該欄位還是會變回 None,即使為基礎類別的 data 加上 [HideInInspector] 的性質。因為 Unity 無法為一個物件同名的欄位初始化多次(一次是 Player 裡的 data,一次是繼承的 Character 裡的 data)。
其實解法很簡單,一個解法是直接命名成不同的名稱,在 Character 裡的叫做 characterData,在 Player 裡的 playerData。另一個解法是將 Character 裡的 data 設為 private,並提供對應的 setter 與 getter,因為屬性 (property) 不會被 Unity 序列化:
class Character : MonoBehaviour
{
private CharacterData _data;
public CharacterData data {
get => _data;
set => _data = value;
}
...
}
class Player : Character
{
public new PlayerData data;
private void Start()
{
base.data = data;
}
...
}
而我傾向前者的解法,也就是直接改名,因為修改成本最低,也可以保留 Character 作為獨立物件的能力,像是可以直接在 inspector 將 CharacterData 設給它的 characterData 欄位。如果採用後者的作法就不行了。 在這篇的例子中,我把 CharacterData 設為 private 只是希望存取權限限制在自己和繼承的類別,如果原本是 public 也是可以,只是在 inspector 中會出現兩個欄位,加上 [HideInInspector] 即可:
想寫這篇文章是因為我一開始把問題想的太複雜,還大興土木改了資料的架構,後來回頭檢視是不是繞了遠路,冷靜想想才發現問題其實很簡單。
沒有留言:
張貼留言