【问题标题】:Keeping a specific classes in ScriptableObject在 ScriptableObject 中保留特定类
【发布时间】:2021-02-03 14:44:18
【问题描述】:

我正在尝试创建一个用于在 Unity 中创建游戏项目的工具。我首先创建了一个基类,游戏中的所有游戏元素都从该基类继承:

[System.Serializable]
public class ItemModel
{
    public int ID;
    public string Name;
    public string Description;
    public UnityEngine.Sprite Picture;
    public int Cost;
    public float Weight;
}

然后,基于基类,为特定类型的物品(例如武器类)创建了几个类:

using UnityEngine;
using System;

[Serializable]
public class WeaponModel : ItemModel
{
    [Range(0, 10000)] int Damage;
}

要保存关于这个项目的数据,你需要使用一个ScriptableObject,这里我创建了一个Object类型的变量来存储关于游戏项目的所有特定类:

using System;
using UnityEngine;

[System.Serializable]
public enum ItemType
{
    Default,
    Weapon,
    Armory,
    Potion,
    Food,
    Readable
}


[Serializable]
public class ItemData : ScriptableObject
{
    public ItemType Type;
    public System.Object Item;
    public GameObject Prefab;
}  

但是这个解决方案对于 EditorGUILayout.PropertyField() 是不正确的,因为它会准确地绘制 Object 类型并且不允许我添加我需要的类。然后我尝试根据枚举 ItemType 使用以下方法将所需的类设置为 Object:

protected void DrawNoSerializedField<T, T2>(System.Object obj)
{
    Type objType = typeof(T);
    FieldInfo[] varsArray = objType.GetFields();
    for (int i = 0; i < varsArray.Length; i++)
    {
        if(varsArray[i].FieldType == typeof( System.Object))
        {
            Type Ttype = typeof(T2); 
            System.Object instance = Activator.CreateInstance(Ttype);
            varsArray[i].SetValue(instance, null);
        }   
    }
}

然后这样称呼它:

ItemData data = _window.serializedObject.targetObject as ItemData;
DrawNoSerializedField<ItemData, WeaponModel>(data);

但此刻 varsArray[i].SetValue(instance, null) Unity 抛出错误 MissingMethodException: Default constructor not found for type WeaponModel.

我可以拒绝继承对象类的想法,但在这种情况下,您失去了所创建游戏项目的特异性。

在这种情况下,我如何组织基础项目类,以便我可以创建特定项目(如武器或盔甲)并将它们保存为 ScriptableObject 并在单独的窗口中作为 Unity 中的 EditorGUILayout 实现它们?

【问题讨论】:

  • 我可能会让ItemModel : ScriptableObject 和你的ItemDatapublic ItemModel Item;
  • 我现在正在学习统一,我认为 derHugo 的意思是,换句话说,当你放弃继承时,统一会更好地工作,相反,use composition. 我真的很挣扎我本人非常习惯基于继承的 OOP
  • @derHugo 根据您的建议,我使用ItemModel 来保存基本信息。对于补充和具体信息,我编写了一个列表,其中包含一个保留 string Namefloat Value 的类。但是这个解决方案看起来很原始......
  • 我同意..问题是存储的数据以后应该如何使用?并且:ScriptableObject 的优点是它还可以实现行为 => 你可以例如添加一个public abstract void Use();,每个继承的类型都可以定义自己的行为..
  • @derHugo 在场景中附加到 GameObject 的某种脚本会将此 ScriptableObject 保留为关于其自身的信息。这将有助于与对象和库存系统进行交互。关于 ScriptableObject 中的实现行为...我正在尝试分离逻辑和信息,因此 ScriptableObject 应该保留仅表示有关它们被实现的对象的信息的方法..

标签: c# unity3d game-development


【解决方案1】:

查看此页面https://docs.unity3d.com/Manual/ManagedCodeStripping.html

尝试在项目设置中将托管剥离级别设置为“禁用”。 你的脚本会像魅力一样工作,但会给你带来另一个问题。您需要要求用户更改播放器设置以使您的工具正常工作。

您可以考虑以稍微不同的方式使用对象。您可以将各种数据放在 Object 中:

Object[] obj = new Object[][]
    {
        new Object[] {1f, 2f, 5f}
        , new Object[] { new Vector3(10f, 2f, 3f)}
        , "Hello, world!"
    };

然后以任何你想要的方式反序列化它,如这个线程中所述:

Get properties and values from unknown object

How to get keys of object (key-value) in C#.

你可以写一些简单的可序列化类,允许用户添加常见类型的基于键值的数据,这样会更容易解析。

【讨论】:

  • 代码剥离与OP的问题有什么关系?主要问题是 Unity 根本不序列化 System.Object,因此它不会真正允许开发人员在 Unity Inspector 中配置任何东西,这是目标
猜你喜欢
  • 2019-01-23
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 2021-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多