#scriptable objects: on value changed?

1 messages · Page 1 of 1 (latest)

spring basin
#

how could I achieve an event OnValueChanged for a scriptable object field?
for example I would like to have a scriptableobject with a WalkingSpeed value, and changing it at runtime on the object would also change it on my movement script.
I load the object using resources.load because I don't know which scriptable object to load until runtime.

I tried just regular on value changed but it seems like unity creates a copy of the scriptableobject in memory so I cannot change the object from the editor.
in the scriptable object:

 public float WalkingSpeed = 5f;
public float walkingSpeed
    {
        get => WalkingSpeed;
        set
        {
            if (Mathf.Approximately(WalkingSpeed, value)) return;
            WalkingSpeed = value;
            OnWalkingSpeedChanged?.Invoke(value);
        }
    }

from movement script:

LocalData = player.GetData("Walking");
LocalData.OnWalkingSpeedChanged += newWalkingSpeed => Debug.Log(newWalkingSpeed);
supple isle
#

What does player.GetData do exactly?

wheat iron
#

would also change it on my movement script
couldn't you have the movement script pull from the SO instead of keeping a separate variable

spring basin
# supple isle Can you explain what issue you had with this?

sorry I wrote this post too fast :)
my movement is a state machine, so there are many different types of player movements, (underwater, third person, first person fps, hoverboard, etc.)

  • player holds a reference to a scriptable object (lets call it LocalData)
  • all movement scripts use the same variables (walking speed for example is used by all states)

I would like in each state (each movement script) to be able to get the values from the scriptable object, and change it from the inspector during runtime (either change the values on the scriptableobject itself, or change the reference on the player) and have the current running state get the updated value.

spring basin
# supple isle What does `player.GetData` do exactly?

player.GetData -> returns a reference to the scriptable object that is saved on the player. (all movement states would take the data from there)

my issue: changing the values on the scriptable object does not update the reference on the player class in runtime.

#

I also tried: using NaughtyAttributes.ExpandableAttribute to show the scriptable object in the inspector, but it seems to only update the values on the project and not on the actual reference:
[Expandable] [SerializeField] private PlayerStateData playerStateData;

In the image: the scriptable object during play mode,
changing the values requires loading the scriptable object again to see the updated values.

spring basin
#

nevermind, I will go with a different approach and keep these for read only loading from disk.
I hoped to find a way to edit the reference that is stored on the monobehaviour script, I'll look for other ways to achieve that
Thanks

supple isle
#

If you want that to work you can write a custom editor or a custom property drawer that does invoke the property setter (and therefore the event)

wheat iron
#

you wouldn't have to change the usage sites either, presumably you already have a reference to the SO, you would just have a property that gets the value from the SO

spring basin
#

I think I am going to redesign this a bit though

wheat iron
spring basin
wheat iron
#

i'm not sure i understand the constraints here

#

a movement state script could change the walk speed dynamically?

#

then wouldn't you want the SO to be the initial/default speed, rather than the current speed? if you want to directly change the speed, you'd do so for that specific object, no?

spring basin
#

yes, my approach was wrong from the beginning.

if you want to directly change the speed, you'd do so for that specific object
I would like so if my walking speed changes on one state during runtime, after loading the initial from the SO, if it changes, it will also change in the next movement state.

So when I switch movement states, I don't need to reload the data from the SO each time, I load it once on game start and then it can change.

wheat iron
#

after loading the initial from the SO, if it changes, it will also change in the next movement state.
what are you referring to with "if it changes" here? the current walking speed or the SO's walking speed

wheat iron
#

why do you need to be notified of the SO value changing?

supple isle
#

Yeah - if you're just always reading the value from the SO, you don't need a notification

#

The specifics here will depend on exactly what GetLocalData is doing

wheat iron
#

it sounds like, once you load the initial value from the SO, you don't want to rely on it at all?

supple isle
#

is it copying data? or just giving you a reference to the SO

wheat iron
#

if you're doing editor testing/tweaking, you'd be changing the value of the object instead of the SO

spring basin
#

guys it's all good

#

I changed my approach 😅

young tide
#

Regarding the original question : You could setup a mini reactive framework to use and subscribe on.

spring basin
young tide
#

Imo you just shouldn't be changing SO data at all

spring basin
#

yes exactly, SOs shouldn't be used for that

young tide
#

But you could simplify on your use case by using mySO = MyScriptableObject.Instantiate(mySO) if need be

spring basin
#

awesome, thanks!
I switched to only using the so to load data but this is a great tip

sterile shell
#

it's a great footgun

#

a possible alternative is to use them as keys to look up a value

#

kind of like an enum, really