0

I am trying to create a tool for creating game items in Unity. I started by creating a base class from which all game elements in the game are inherited:

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

Then, based on the base class, several classes were created for a specific type of item (such as a weapon class):

using UnityEngine;
using System;

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

To save data about this item, you need to use a ScriptableObject, and here I created a variable of type Object to store all the specific classes about the game item in it:

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

But this solution is not correct for EditorGUILayout.PropertyField (), since it will draw exactly the type Object and will not allow me to add the class I need. Then I tried, depending on the enum ItemType, to set the needed class into Object using the following method:

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

And call it like this:

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

But at the moment varsArray[i].SetValue(instance, null) Unity throws an error MissingMethodException: Default constructor not found for type WeaponModel.

I can refuse the idea with inherited object classes, but in this case, you lost the specificity of the created game item.

In that case, how do I organize the base item classes so that I can create specific items like Weapons or Armor and save them as ScriptableObject and implement it all in a separate window as EditorGUILayout in Unity?

Alex.D
  • 1
  • 2
  • I would probably make the `ItemModel : ScriptableObject` and in your `ItemData` have `public ItemModel Item;` – derHugo Feb 03 '21 at 15:23
  • I am learning unity right now and I think what derHugo is getting at, to put it in different terms, is that unity works better when you forgo inheritence and, instead, [use composition.](https://en.wikipedia.org/wiki/Composition_over_inheritance) I really struggled with that myself as I am very used to inheritance based OOP – Marie Feb 03 '21 at 15:42
  • @derHugo By your recommendation I've used `ItemModel` to keep basics information. For addition and specific information I've written a list with a class that keeps the `string Name` and `float Value`. But this solution looks raw... – Alex.D Feb 03 '21 at 16:13
  • I agree .. the question is how is the stored data supposed to be used later? And: The advantage of the `ScriptableObject` is that it can also implement behavior => You can e.g. add a `public abstract void Use();` and every inherited type can define its own behavior .. – derHugo Feb 03 '21 at 16:20
  • @derHugo Some kind of Script attached to GameObject in Scene will keep this ScriptableObject as information about itself. This will help to interact with Objects and the Inventory system. About implementation behavior in ScriptableObject... I'm trying to separate logic and information, so ScriptableObject should keep methods that ONLY represent info about the object there they are implemented.. – Alex.D Feb 03 '21 at 16:53

2 Answers2

0

Soo, i've managed to convert nested data to byte array and roll it back by accessors.

[Serializable]
[CreateAssetMenu(fileName = "New Room", menuName = "Game/Room")]
public class RoomData : ScriptableObject
{
    //Smart desition to save anything in bytearies and then convert it back ;)
    public BlockModel.BlockType[,] RoomGrid => RoomSettingsEditor.FromByteArray<BlockModel.BlockType[,]>(roomGridBinary);
    public Vector2Int RoomSize => roomSize;
    public List<Character.Basic.Character> Enemies => enemies; 
            

    [SerializeField] 
    private List<Character.Basic.Character> enemies = newList<Character.Basic.Character>();
    
    [SerializeField] 
    private Vector2Int roomSize = new Vector2Int(20,20);
    
    [SerializeField]
    [HideInInspector] 
    private byte[] roomGridBinary; //Smartest programmer in the world

  
    /// <summary>
    /// For Editor Only!!!
    /// </summary>
    public void SetNewRoomBinary(byte[] newRoomData) => roomGridBinary = newRoomData; 
    //TODO: Think about encapsulating this wonder
}

Further working with this scriptable I've realized that this instanse creates as it shold but it doesn't saves any data from inspector.

After surfing the internet I've came across 'EditorUtility.SetDirty()': https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html

[CustomEditor(typeof(RoomData))]
public class RoomSettingsEditor : Editor
{
    private RoomData _roomData;
    private BlockModel.BlockType[,] _roomGridTemp;

    //some work..bla..

    private void OnDisable()
    {
        //when closing the Editor window, save the array from RAM (well, so as not to eat the processor while creating the room)
        _roomData.SetNewRoomBinary(ToByteArray(_roomGridTemp)); 
        EditorUtility.SetDirty(_roomData);
    }

    //some work..bla..
}

As always unity documentation is complicated but in general this EditorUtility method allows you to save an object such this.

glhf

Alex.D
  • 1
  • 2
-1

Check out this page https://docs.unity3d.com/Manual/ManagedCodeStripping.html.

Try to set Managed Stripping Level to "Disabled" in Project Settings. Your script will work like a charm but will bring you another problem. You will need to ask users change Player Settings for making your tool work correctly.

You may consider using Objects in slightly different way. You can put every kind of data in Object:

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

And then deserialise it in any way you want, as described in this threads:

Get properties and values from unknown object

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

You can just write some simple serialisable class which allows user to add key-value based data of common types, it will be easier to parse.

  • How is the code stripping related to OP's question? The main issue is that Unity doesn't serialize `System.Object` at all so it won't really allow the developer to configure anything within the Unity Inspector which is the goal – derHugo Feb 03 '21 at 20:40