Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Scriptable Object with constant, Unique ID

Tags:

c#

unity3d

I use Scriptable Objects to create Items for my Unity Game. Let's say I create a Stick, a Sword and a Helmet. Now to use them, I need to drag them into a list and on game start, each item get's an ID assigned which is also the position in the list, as I simply loop through the list to assign ID's. Now I can access the item by using the index it has or, as shown in most of the tutorials about ScriptableObjects, we can then add the item and the id to a Dictionary to better access them.

Stick is 0, Sword is 1 and Helmet is 2. Now I decide to remove the sword with a patch so helmet gets the ID of 1. Now every savegame is broken as swords become helmets.

How can I handle assigning fixed ID's which wont change and wont be taken by another item until I really want to assign it again? I could of course hard-code the id for every ScriptableObject I create but as you can imagine, managing hard-coded IDs seems not like a good idea.

like image 745
Sonic1305 Avatar asked Nov 21 '19 22:11

Sonic1305


People also ask

How do you make an object scriptable?

In order to create a Scriptable Object, you'll need to first add to the Create menu you've used to create materials and scripts. To do this, you'll use the attribute CreateAssetMenu and specify the default filename and location within the menu. Also, you'll need to change what class CharStats is inheriting from.

Are scriptable objects persistent?

ScriptableObjects aren't persistent. Only in the Editor. When you make a build, run your game, change something in the ScriptableObject, exit the game and restart it, it won't retain the change. You still need a proper save system to achieve persistence.

Can you create scriptable objects at runtime?

You cannot save scriptable objects during runtime. Use an editor script instead. Otherwise, if you are loading a "spreadsheet" in runtime as part of the application/game, use JSONUtility instead to serialize and deserialize common [Serializable] classes and structs with [SerializeField] instead.


1 Answers

Here is my take on it :

  1. Create a custom attribute "ScriptableObjectIdAttribute", you can use it in front of whatever you need to be an id.
  2. Make a custom property drawer in order to initialize the id the first time you access the object in the inspector and also to make it non-editable by the inspector.
  3. (Optional) Make a base class with that Id property and extend all your ScriptableObjects which need a unique Id from it.

BaseScriptableObject.cs :

using System;
using UnityEditor;
using UnityEngine;

public class ScriptableObjectIdAttribute : PropertyAttribute { }

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(ScriptableObjectIdAttribute))]
public class ScriptableObjectIdDrawer : PropertyDrawer {
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
        GUI.enabled = false;
        if (string.IsNullOrEmpty(property.stringValue)) {
            property.stringValue = Guid.NewGuid().ToString();
        }
        EditorGUI.PropertyField(position, property, label, true);
        GUI.enabled = true;
    }
}
#endif

public class BaseScriptableObject : ScriptableObject {
    [ScriptableObjectId]
    public string Id;
}

Weapon.cs :

using System;
using UnityEngine;

[CreateAssetMenu(fileName = "weapon", menuName = "Items/Weapon")]
public class Weapon : BaseScriptableObject {
    public int Attack;
    public int Defence;
}

Result : enter image description here

like image 72
BlueBossa Avatar answered Oct 01 '22 22:10

BlueBossa