- Home /
How do you store non-GameObject data?
There's data I want to store outside of any GameObject. For example, I'd like to store inheritable data for character races in a database style structure.
In this way, I can say:
All character races have a 'Strength' attribute
Goblins have a range of strength attribute values (they can override)
Goblin warriors have a specific range of strength values (they can override)
Joe the Goblin Warrior has an even more specific value (he can override)
I really don't want to create empty prefabs for generic character template data. It feels clumsy for searching and updating. I also don't believe prefabs support nested data the way I'm thinking about it.
So, I have all of this working using my own game-save/storage system. However I now have 2 different 'save' buttons in the editor:
Unity's normal 'save' to save the scene
My 'save' to save data external to the scene (i.e. inheritable attributes)
This feels cumbersome.
So, how do you store data that isn't part of a GameObject?
Is there a way to trigger my data save when the developer (me) saves the scene?
Answer by PAEvenson · Jun 23, 2014 at 01:07 PM
Sounds like you want ScriptableObject!
http://docs.unity3d.com/ScriptReference/ScriptableObject.html
Thanks PA. I did do some research and testing on the ScriptableObject, but that seemed to fall short (or maybe I missed something).
The ScriptableObjects seemed to be part of the scene data. So I couldn't create a prefab or share the data across multiple scenes.
In my example above, I wanted the goblin attribute data to be accessible no matter what scene was loaded. The alternative was to save the data once for each scene, but the DBA in me cringed. :)
ScriptableObjects do work across multiple scenes. They look just like any other asset inside the Project View. They are not scene specific. For example:
public class GoblinBadGuy: $$anonymous$$onoBehavior
{
public BudGuyStats GoblinStats;
}
//In different file
public class BudGuyStats : ScriptableObject
{
public int health = 100;
public int power = 100;
public int toughness = 100;
}
Inside of the Project View you would create a new BadGuyStats. I would then rename it to something like GoblinStats. This is now a file sitting inside the project, which you can change the stats inside the inspector. You could then create new files for each of your different types(storing differnt values ie "PowerfulGoblin") but all are of the same class.
When I use ScriptableObjects I like to create editor scripts that allow me to create them easily:
using UnityEngine;
using UnityEditor;
using System;
public class BadGuy$$anonymous$$enuItems
{
[$$anonymous$$enuItem("Assets/BadGuys/BasicBadGuy")]
public static void CreateBadGuyStats()
{
CustomScriptableObjectUtility.CreateAsset<BadGuyStats>("NewBadGuyStats");
}
}
Here is the UtilityFile(Im sure i found this somewhere in the Unity community) it super useful.
using System;
using UnityEngine;
using UnityEditor;
using System.IO;
public class CustomScriptableObjectUtility
{
public static void CreateAsset<T> () where T : ScriptableObject
{
CreateAsset<T>("New " + typeof(T).Name.ToString());
}
public static void CreateAsset<T>(string name) where T : ScriptableObject
{
T asset = ScriptableObject.CreateInstance<T>();
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (path == "")
{
path = "Assets";
}
else if (Path.GetExtension(path) != "")
{
path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
}
string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/" + name + ".asset");
AssetDatabase.CreateAsset(asset, assetPathAndName);
AssetDatabase.SaveAssets();
UnityEditor.EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
}
Thanks PA for all the details!
So that means I'd need a separate class/file for each instance:
BadGuyStats
BadGuyGoblinStats
BadGuyGoblinWarriorStats
BadGuyGoblinWarriorArcherStats
Joe the Goblin Warrior Archer would have an instance of GoblinWarriorArcherStats.
The difference being only value changes. They all have 'Health' just a different value.
So far, so good. Potentially lots of files, but I can inherit the classes and only override the property values I need to.
However, if in mid-game, all Goblin Warrior Archerers power should increase by 10, I'd have to find all the instances and update them individually...
I wouldn't be able to update the GoblinWarriorArcherStats once and have it flow because that data lives as a resource and I can't save it at run-time.
Hopefully this makes sense.
Note: my initial reaction to creating all the classes is that since it's just values that are changing, it makes more sense to store them in a database vs. separate classes. But, maybe I'm stuck in my way of doing things.
Sorry for dragging this out...I'm really trying to make sure I (and maybe others) understand.
You can certainly write an editor script that finds all of the objects of a specific type and adjust a value in them if you were to go the ScriptableObject route. You could also write another editor script to view all of the creatures in the project and display them in an editor window, and change stats right then and there.
As for the changing at runtime, I would personally add a modifier property. Something like this:
public int BaseHealth
{
get { return stats.Health}
}
public int $$anonymous$$odifiedHealth
{
get { return stats.Health + this.Health$$anonymous$$ods;}
}
public int Health$$anonymous$$ods
{
get
{
return 0;//Adds all the Health $$anonymous$$ods together here
}
}
It all depends how comfortable you feel with the technologies. If you are familiar with databases or want to learn more about them, then I would go the database route. If you want to learn more about ScriptableObjects and the Unity Editor, go that route. You could probably combine the database and UnityEditor, allow you to change stats inside of Unity rather than an outside application.
-Paul
Actually, if you have more than 30 or so different variations, I would probably use a database. Things could start getting a little out of hand if you have 30+ different scriptableObjects sitting in your project. :P
Answer by AyAMrau · Jun 23, 2014 at 01:17 PM
Have you considered using Scriptable Objects with a script like the one here. This will let you store data in objects that are saved in the Project. You can edit them just like prefabs and everything is saved automatically. You can also give them custom inspector if you need something fancy to happen.
Thanks AyA$$anonymous$$rau, I haven't seen this and will have to try it. If it does let me reuse the data across all scenes, that may work.
I have X$$anonymous$$L serialization working, but if this is the 'right' way to use data I'd rather work with Unity than against it.
Yes, this is saved in the Project (same as prefabs, scripts etc), therefore it's available in all scenes. You can either add references directly to game objects or you can place the scriptable object files in the Resources folder and use Resources.Load.
If you have quite a lot of scriptable objects that need loading, then you can also make an object that just stores a list of them, where it makes sense. For example one that stores all the enemies. Then you only need to load one thing and access the list as normal in code.
Thanks.
I just did some digging and testing. Everything looked good until I realized you can't save the data back into the resource at runtime.
Not saving parent data at runtime might be ok, but not being able to update inherited instances is an issue.
Quickly about your comment to the previous answer. If all your goblins have the same properties (i.e. health, strength), then you don't need a Scriptable Object derived class for every single one. You only need a separate copy of the actual Scriptable Object in your project, name them differently and set all the values to be what you want. You will probably have a hierarchy for your game objects when creating characters in game, but they can all refer to one type of Scriptable Object.
You shouldn't be modifying the scriptable object instances at runtime, because this is permanent (and shared for all in game characters). The way to do this, is to make a field on the game object to store the Scriptable Object with the data and then duplicate any fields that you will be modifying (like health). Treating the Scriptable Object as source of base values.
If the base values need to change during the game I can see two ways to do this.
If the new value is also a preset data: Store it in the Scriptable Object as a new field and then in the game object have some logic that let's the character know which value to use.
If the value is dynamic and decided at runtime: Have a stat modifier variable on the game object that will be applied after reading from the data object.
If the change is specific to one character then you will need to access it. But if you need all the enemies of given type to make change you can probably get away with using a static.
Thanks AyA$$anonymous$$rau.
Part of what I was trying to figure out was if there was a pre-built solution in Unity that I just didn't know about. Given the responses, I think the answer is 'no' and going with my database style approach is probably the best answer for me.
Thanks again for helping me beat through this! :)
Answer by Gizmoi · Jun 23, 2014 at 01:41 PM
I would suggest saving your data out to text and reading it in at run-time.
JSON is currently my format of choice.
SimpleJSON can be used with Unity.
Thanks Gizmoi. I'm using a compact version of X$$anonymous$$L now and my run-time save/load game is working great.
However, when in the editor I have to remember to save data changes in addition to saving the scene. That feels error prone and a bit weird to me and why I'm thinking I should have serialized it a different way...maybe with ScritableObjects ins$$anonymous$$d of JSON or X$$anonymous$$L.
Your answer
