嗨,各位游戲開發人員,我正在開發一個 Unity 專案,該專案允許關卡設計師編輯場景元素的指令,說明他們應該如何對事件采取行動。
unity inspector 中的命令編輯器截圖
我已經設法用一個通用的抽象基類來表達所有可執行的指令單元——運算式、陳述句、控制塊Command。它是這樣的:
[Serializable]
abstract class Command {
public abstract object Execute();
public abstract void Inspect(/* ... */);
}
class CommandCarrier : MonoBehaviour {
public Command command;
}
/*
There are several carrier classes in the real project,
this one is only for illustrating the problem.
Command.Inspect() would be called by a CustomEditor of CommandCarrier.
*/
whereExecute()是在運行時執行命令,并且Inspect()是繪制檢查器 GUI。
每個物體型別的命令都是 的派生類Command,例如 if-else 塊如下:
[Serializable]
class Conditional : Command {
public Command condition, trueBranch, falseBranch;
public override object Execute() {
if((bool)condition.Execute()) trueBranch.Execute();
else falseBranch.Execute();
return null;
}
public override void Inspect(/* ... */) { /* ... */ }
}
常量運算式不包含子命令:
[Serializable]
class Constant<T> : Command {
public T value = default(T);
public override object Execute() => value;
public override void Inspect(/* ... */) { /* ... */ }
}
Here comes the problem: all the commands I've written in the inspector panel would be lost as long as a reserialization is triggered (like when the code changed and therefore is recompiled). This is probably because Unity failed to serialize a subclass instance stored in a field of base class; all the type information and the contained data are lost during reserialization. What's worse is that these polymorphical instances are even nested.
I've tried to solve the case and failed: given a field of base class, it's apparently impossible to "upgrade" an instance to a subclass by calling whatever methods belonging to that instance; it must be done externally by assigning the field with a subclass instance created elsewhere. But again, every subclasses have their own fields, and these data I haven't figure out where to recover from.
Could anybody help?
uj5u.com熱心網友回復:
現在您在這里更正了您的代碼,我將向您指出腳本序列化,特別是該部分
不支持多型性
如果你有 a
public Animal[] animals并且你放入了 aDog、 aCat和 a的一個實體Giraffe,那么在序列化之后,你就有了三個Animal.解決此限制的一種方法是意識到它僅適用于自定義類,這些類是行內序列化的。對其他
UnityEngine.Objects的參考被序列化為實際參考,對于那些,多型確實有效。您將創建一個ScriptableObject派生類或另一個MonoBehaviour派生類,并參考它。這樣做的缺點是您需要將該Monobehaviour物件或可撰寫腳本的物件存盤在某處,并且您無法有效地對其進行行內序列化。這些限制的原因是序列化系統的核心基礎之一是提前知道物件的資料流布局。它取決于類欄位的型別,而不是欄位記憶體儲的內容。
所以在你的情況下,我會簡單地使用ScriptableObject并做
abstract class Command : ScriptableObject
{
public abstract object Execute();
public abstract void Inspect(/* ... */);
}
和
[CreateAssetMenu]
public class Conditional : Command
{
public Command condition, trueBranch, falseBranch;
public override object Execute() {
if((bool)condition.Execute()) trueBranch.Execute();
else falseBranch.Execute();
return null;
}
public override void Inspect(/* ... */) { /* ... */ }
}
和
public abstract class Constant<T> : Command
{
public T value = default(T);
public override object Execute() => value;
public override void Inspect(/* ... */) { /* ... */ }
}
例如
[CreateAssetMenu]
public class IntConstant : Constant<int>
{
}
each in their own script files with matching name (that part is very important for the serializer).
And then you would create instance of these via the Assets -> right click -> Create -> "Conditional" for example and reference it into the according slots.
Also note that these are now re-usable and you can simply reference the same item in various places, something that wasn't possible if you use a normal serializable class due to
When might the serializer behave unexpectedly?
Custom classes behave like structs
With custom classes that are not derived from UnityEngine.Object Unity serializes them inline by value, similar to the way it serializes structs. If you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data.
When you need to serialize a complex object graph with references, do not let Unity automatically serialize the objects. Instead, use
ISerializationCallbackReceiverto serialize them manually. This prevents Unity from creating multiple objects from object references. For more information, see documentation onISerializationCallbackReceiver.This is only true for custom classes. Unity serializes custom classes “inline” because their data becomes part of the complete serialization data for the
MonoBehaviourorScriptableObjectthey are used in. When fields reference something that is aUnityEngine.Object-derived class, such aspublic Camera myCamera, Unity serializes an actual reference to the cameraUnityEngine.Object. The same occurs in instances of scripts if they are derived fromMonoBehaviourorScriptableObject, which are both derived fromUnityEngine.Object.
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/425057.html
標籤:c# unity3d serialization polymorphism unity3d-editor
