Hello, I am trying to make a kind of turn-based game, so I made a characters using Scriptable Objects.
It works nicely and characters act as I want to when placed into script but I have a problem.
I want to change the character(ScriptableObject) at runtime in the Inspector. If I put them in manually, it works but I want to choose character when playing so the ScriptableObject would have to change for another one.
So is there a way to change ScriptableObject reference in the script at runtime?
If you wanna set character to another scriptable object at your assets, you should have it in a resources folder path. If your scriptable object “Kassadin” its at Assets/Resources/Characters you should use:
character = Resources.Load<Character>("Characters/Kassadin");
If you wanna create a new scriptableObject from scratch with default values, use:
character = ScriptableObject.CreateInstance<Character>();
Sure, just make the variable that holds the reference a public member like anything else. It will appear in the Inspector and can be changed at run-time. (Note that it will change back to whatever it was before you entered play mode, after you exit play mode.)
ScriptableObjects are intended for read-only predefined data. If you change them at runtime in the editor, they will actually change in the project, even after your stop and restart the editor. This is by design and how Unity conflates runtime code with editor time code when in the editor.
If you change them at runtime in the built game, I am not sure what the specified behavior is. In fact, it probably isn’t specified, but when the game restarts the changes will be lost unless you persist them yourself to some other place, and you actually re-inject the persisted data at the next launch. This is not the intended use of ScriptableObjects.
If you want to use ScriptableObjects as a starting template, you can always Instantiate fresh copies of them at runtime, and use those as the “dirtied and modified” versions during your game. At the end of the game, discard them and start afresh from the asset on disk.
If you have such an Instantiated runtime-modified ScriptableObject, the Unity Editor does not presently (EDIT: Unity DOES let you see these, either with right-click properties or else simply double-clicking on the field, which injects it into the inspector window) let you change or see them during runtime. But if you use an asset such as Advanced Inspector or Odin Inspector, I believe then you can view/edit the instances at runtime.
I use them for a lot of things that are not read-only. A pattern I use a lot (due to Ryan Hipple) is this:
public int initValue = 3;
public int currentValue;
void OnEnable()
{
currentValue = initValue;
}
This lets me edit the initial value in the Inspector, modify the current value at run-time, make the changed current value available to anything with a reference to this SO, and never have to worry about the changes persisting between entering/leaving play mode.
One use for this pattern is tracking health points. Using C#'s built-in event system, observers can register to be called whenever the value changes. I also like to add code to the SO that lets the SO change its own values. So, if I am using the above pattern to track health, I might have a TakeHit method, an AddHealth method, and a TakeBigHit method, all defined in the SO, callable by anything with a reference to it. This centralizes those operations, and decouples them from whatever might call them.
The changes persist for the duration of the game session. They are only lost (because the values are reset to their default state) when the game is closed.
so if you have a player health SO with a starting value of “10” and during the game they lose 8 health points their in-game health will show as 2. However, quitting the game will reset their health back to 10 so as you mention below this data needs to be persisted in some other place (like a binary file).
This is true.
I think Kurt is right in regards to using an instance of an SO that was created at runtime.
CAUTION: I just checked this and the behavior has radically changed between Unity versions: until fairly recently, the OnEnable/OnDisable lifecycle was NOT honored for ScriptableObjects in the way that it is on MonoBehaviors.
It appears to be effectively honored now, testing informally in 2018 LTS, 2019 LTS and 2020.1.5 (just what I had handy), but I know that it did not used to be this way. In earlier versions of Unity, ScriptableObjects are not torn down and stood up each time you pressed PLAY in the editor, resulting in all kinds of weird persisting value problems, even on non-public members.
Yes, I seem to remember having to cope with this, circa Unity 2017. IIRC (and I may not), you could get them to behave properly, but it was pretty confusing, with OnEnable and OnDisable being called in the proper sequence, but often at times you would not expect (like OnDisable then OnEnable called when you entered play mode, but no OnDisable when you left it). Also, Awake was called when the SO was first actually constructed (as opposed to all subsequent instances which are deserialized from the SO asset), but not every time the SO was deserialized after that (as opposed to a MonoB which has Awake called every time a Scene first starts). Haven’t tried it in the current version. Maybe it works now too.
Been teaching my students the power of using SOs as injectable code holders for the strategy pattern, as well as for decoupled communications between GameObjects. Most of them seem to love it. I sure do .
Well you seem to use ScriptableObject assets. The assets are already alive in the editor and stay alive when you exit playmode. OnDisable is actually called when the object is destroyed. This happens at an assembly reload which happens when you enter playmode. This does not happen when you exit playmode. The behaviour should be similar to MonoBehaviours which have the ExecuteInEditMode attribute.
Awake is a callback when the scriptable object is created / loaded on the native side, OnEnable is a callback that tells you when it’s loaded on the managed side. During an assembly reload the native part should stay alive but the managed part is unloaded and reloaded, hence you get a OnDisable and OnEnable call. Prefabs would behave similar, though prefabs do not receive any callbacks from Unity (besides ISerializationCallbackReceiver callbacks if applied).
Keep in mind when you want to have a modifiable / non-persisted instance at runtime you can always use Instantiate to clone a scriptable object asset. In fact Instantiate can clone any UnityEngine.Object (that includes Textures, Meshes, Materials, GameObjects(along its components) and ScriptableObjects)
I doesn’t know who write Unity documentation, but it’s a very weird guy or it doesn’t know English nor Unity :):(
The current description (2020.3) for ScriptableObject.Awake
“This function is called when the ScriptableObject script is started.(???!!!)
Awake is called as the ScriptableObject script starts. This happens as the game is launched and is similar to MonoBehavior.Awake.”
Obvious lowball aside, if you trash other people’s work, please at least take the time to reason about it. What would you like to see instead? You could use the suggestion facilities at the bottom of the docs page too.
I did it often (not only Unity site and on Microsoft documentation site also )
What I want to see ? Something from that post , after Unity Senior Developers to check that and correct that, if it needs
P.S>
When you make some suggestion on Microsoft documentation site, you are received some issue number and can be notice about response to your issue. This is motivate to did it more frequently and describe a more detail issue. On Unity documentation site when you leave some comments, you are feeling it will read nobody that.
Microsoft has about 200,000 employees. Unity, about 3,400. I have complaints about the Unity documentation too, but adding a tracking system for suggestions is probably more than we can reasonably expect.
IMHO I don’t completely agree with this, because if the Unity team had done this (simple portal + a few paid moderators), they could have harnessed the greater power of the Unity forum users to make the Unity documentation to be cleaner and additionally decrease the “entry level for beginners”. ROI will be positive to company.
“simple portal” - can be a separate structured part of the Unity Forum.
Well, that sounds easy enough. But after nearly fifty years of programming computers, I’ve humbly come to admit that everything sounds easy when someone else has to do it.