unity-atoms / unity-atoms

⚛️ Tiny modular pieces utilizing the power of Scriptable Objects

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[FEATURE] Allow listening to AtomReference even with Value or Const usage

lgarczyn opened this issue · comments

Is your feature request related to a problem? Please describe.

The choice between using an AtomReference and AtomEventReference is always a bit difficult.

Usually it's something like

Reference EventReference
Reading Has a garanteed value Doesn't have a garanteed value (but can replay previous one)
Reacting Can be listened to through GetEvent/GetOrCreateEvent (except on Value/Const usage) Made to be listened to

I've started to think of Variables simply as Events with base values. No data can guarantee that it will only has either the "reading a value when you need it" use-case or the "waiting for a value to change" use-case.

It can simply state "do I have a default value or not".

But right now if I want an reference that guarantees a default value AND can be listened to, I have a problem.

(current example, I want to set fmod parameter using an AtomReference. I need to update the parameter when it changes, but I also want to read that value when a new audio event starts)

Describe the solution you'd like

If this approach makes sense to you, then you should be able to get an event for any type of variable.

In the case of a "Value" usage, then it should do two things:

  • replay its value when subscribed to
  • if the user modified the value from the inspector, emit a value

The API would be harmonized, simply implementing IGetOrCreateEvent for AtomReference

This would cover the "gap" where you need to both read a variable and react to it.

Describe alternatives you've considered

    static E GetFakeEvent<T, E>(T replayValue)
      where E : AtomEvent<T>
    {
      E output = ScriptableObject.CreateInstance<E>();
      // Add replay value to buffer
      output.Raise(replayValue);
      return output;
    }
    
    public static E1 GetOrCreateEvent<T, P, C, TV, E1, E2, F, TVi>(this AtomReference<T, P, C, TV, E1, E2, F, TVi> reference)
      where P : struct, IPair<T>
      where C : AtomBaseVariable<T>
      where TV : AtomVariable<T, P, E1, E2, F>
      where E1 : AtomEvent<T>
      where E2 : AtomEvent<P>
      where F : AtomFunction<T, T>
      where TVi : AtomVariableInstancer<TV, P, T, E1, E2, F>
    {
      switch (reference.Usage)
      {
        case AtomReferenceUsage.VALUE :
        case AtomReferenceUsage.CONSTANT : return GetFakeEvent<T, E1>(reference.Value);
        default: return reference.GetEvent<E1>();
      }
    }

This does everything except the inspector stuff, and actually implementing the IGetOrCreateEvent interface.

This could only be done by extending or modifying AtomBaseReference (for example with an OnValidate function).

Additional context

Big problem with this idea, is if people expect that directly setting the value will trigger those events.

I'm not exactly sure if it shouldn't trigger them. It could be a lightweight alternative to instancers, but I don't feel comfortable with it.

We could have warning logs if someone modifies those values at runtime after fake events have been created, but that's obviously simply a band-aid.