如何将GameObject分配给child进行序列化
How to assign GameObject to child for serialization
我想为我创建的每个项目分配某种游戏对象,然后当我创建一个新的 object(称为项目)时,它将分配的游戏对象作为 child。
为此,我有一个 class 的可编写脚本的 object,其中包含一个名为“gameObj”的 public 游戏对象:
public abstract class ItemObject : ScriptableObject
{
public int id;
public GameObject gameObj;
}
然后,在另一个 class 我想要这样的东西:
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
this.gameObject.transform.GetChild(0) = item.gameObj; //WRONG CODE, NEED HELP HERE
}
}
目的是将给定 ItemObject.item 的 gameObj 设置为 GroundItem 的游戏对象。
最终目的是拥有大量各种可编写脚本的物品(如面包、剑、石头等),每个物品都会分配一个游戏对象,一旦我创建了一个新的 GroundItem 游戏 object 我将简单地分配可编写脚本的 object 项目,它的 child 将拥有游戏 object 本身(包括所有视觉效果、特殊脚本等)。
供参考,在下面的 link 中,此人从第 4 分钟到第 6 分钟都在这样做,但使用的是精灵而不是游戏 object。
谁知道应该怎么写?有可能吗?
你可能是指
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
if(!item) return;
if(!item.gameObj) return;
// make according object a child of this object
item.gameObj.transform.parent = transform;
// make it the firs child
item.gameObj.transform.SetSiblingIndex(0);
}
}
但是,一般来说,ScriptableObject
是资产,和GameObject
是场景层次结构中的实例-> 您不能简单地引用资产中的场景对象,无论如何这样做可能会导致意外行为。
为什么必须在 ISerializationCallbackReceiver
中完成此操作是否有充分的理由?
我认为您实际上更想实现的是例如
public class GroundItem : MonoBehaviour
{
public ItemObject item;
#if UNITY_EDITOR
[HideInInspector]
[SerializeField]
private ItemObject _lastItem;
// This is called whenever something is changed in this objects Inspector
// like e.g. item ;)
private void OnValidate()
{
// Delay the call in order to not get warnings for SendMessage
UnityEditor.EditorApplication.delayCall += DelayedOnValidate;
}
private void DelayedOnValidate()
{
// remove the callback since we want to be sure it is always added only once
UnityEditor.EditorApplication.delayCall -= DelayedOnValidate;
// if item hasn't changed nothing to do
if (item == _lastItem) return;
// otherwise first destroy current child if any
if (_lastItem && transform.childCount > 0)
{
if (Application.isPlaying) Destroy(transform.GetChild(0).gameObject);
else DestroyImmediate(transform.GetChild(0).gameObject);
}
// is an item referenced and does it even have a gameObject ?
if (item && item.gameObj)
{
// instantiate the new one as child of this object
var obj = Instantiate(item.gameObj, transform);
// set as first child (if needed)
obj.transform.SetSiblingIndex(0);
if (!Application.isPlaying)
{
// if in edit mode mark this object as dirty so it needs to be saved
UnityEditor.EditorUtility.SetDirty(gameObject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
_lastItem = item;
}
#endif
}
现在看起来像
我想为我创建的每个项目分配某种游戏对象,然后当我创建一个新的 object(称为项目)时,它将分配的游戏对象作为 child。
为此,我有一个 class 的可编写脚本的 object,其中包含一个名为“gameObj”的 public 游戏对象:
public abstract class ItemObject : ScriptableObject
{
public int id;
public GameObject gameObj;
}
然后,在另一个 class 我想要这样的东西:
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
this.gameObject.transform.GetChild(0) = item.gameObj; //WRONG CODE, NEED HELP HERE
}
}
目的是将给定 ItemObject.item 的 gameObj 设置为 GroundItem 的游戏对象。
最终目的是拥有大量各种可编写脚本的物品(如面包、剑、石头等),每个物品都会分配一个游戏对象,一旦我创建了一个新的 GroundItem 游戏 object 我将简单地分配可编写脚本的 object 项目,它的 child 将拥有游戏 object 本身(包括所有视觉效果、特殊脚本等)。
供参考,在下面的 link 中,此人从第 4 分钟到第 6 分钟都在这样做,但使用的是精灵而不是游戏 object。
谁知道应该怎么写?有可能吗?
你可能是指
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
if(!item) return;
if(!item.gameObj) return;
// make according object a child of this object
item.gameObj.transform.parent = transform;
// make it the firs child
item.gameObj.transform.SetSiblingIndex(0);
}
}
但是,一般来说,ScriptableObject
是资产,和GameObject
是场景层次结构中的实例-> 您不能简单地引用资产中的场景对象,无论如何这样做可能会导致意外行为。
为什么必须在 ISerializationCallbackReceiver
中完成此操作是否有充分的理由?
我认为您实际上更想实现的是例如
public class GroundItem : MonoBehaviour
{
public ItemObject item;
#if UNITY_EDITOR
[HideInInspector]
[SerializeField]
private ItemObject _lastItem;
// This is called whenever something is changed in this objects Inspector
// like e.g. item ;)
private void OnValidate()
{
// Delay the call in order to not get warnings for SendMessage
UnityEditor.EditorApplication.delayCall += DelayedOnValidate;
}
private void DelayedOnValidate()
{
// remove the callback since we want to be sure it is always added only once
UnityEditor.EditorApplication.delayCall -= DelayedOnValidate;
// if item hasn't changed nothing to do
if (item == _lastItem) return;
// otherwise first destroy current child if any
if (_lastItem && transform.childCount > 0)
{
if (Application.isPlaying) Destroy(transform.GetChild(0).gameObject);
else DestroyImmediate(transform.GetChild(0).gameObject);
}
// is an item referenced and does it even have a gameObject ?
if (item && item.gameObj)
{
// instantiate the new one as child of this object
var obj = Instantiate(item.gameObj, transform);
// set as first child (if needed)
obj.transform.SetSiblingIndex(0);
if (!Application.isPlaying)
{
// if in edit mode mark this object as dirty so it needs to be saved
UnityEditor.EditorUtility.SetDirty(gameObject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
_lastItem = item;
}
#endif
}
现在看起来像