How can I get the derived primitive type from a generic interface?

Been struggling with this for a bit so wanted to see if someone here might know the solution. I have a set of interfaces (and an implementation):

public interface IInputValue
{
}

public interface IInputValue<T> : IInputValue where T : struct
{
    T Value { get; set; }
}

public class ButtonInputValue : IInputValue<bool>
{
    private bool m_Value;
    public bool Value
    {
        get => m_Value;
        set => m_Value = value;
    }
}

And then I have a scriptable object class that inherits and uses implementations of IInputValue<T>.

public abstract class InputTrigger : ScriptableObject
{
}

public abstract class InputTrigger<T> : InputTrigger where T : IInputValue
{
    public abstract T InputValue { get; }
}

T for example, could be ButtonInputValue. I want to serialize this scriptable object in another class (as InputTrigger) and simply be able to call .InputValue on it to get the correct derived type of IInputValue or just get the primitive value directly through IInputValue.Value. What’s the best way to go about doing this? Any help is much appreciated.

What is the point of IInputValue vs IInputValue? An interface without any members is usually rare (I can think of some edge use cases, but you seldom actually need it).

As is there’s no direct way to get the derived type without doing some reflection or just testing the type since your InputTrigger only requires it to be an IInputValue. As a result… that’s all you’ll know, that it’s an IInputValue. Of course reflecting more information can allow you to go deeper, but that implies your design isn’t doing what you want it to.

…

So with that said… what are you attempting to accomplish here? Not from a code syntax perspective. What is your actual use case goal?

Is there a reason you can’t do this?

    public interface IInputValue<T> where T : struct
    {
        T Value { get; set; }
    }

    public class ButtonInputValue : IInputValue<bool>
    {
        private bool m_Value;
        public bool Value
        {
            get { return m_Value; }
            set { m_Value = value; }
        }
    }

    public abstract class InputTrigger<TInput, TValue> where TInput : IInputValue<TValue> where TValue : struct
    {

        public abstract TInput Input { get; }

    }

    public class ButtonInputTrigger : InputTrigger<IInputValue<bool>, bool>
    {

        private ButtonInputValue _input = new ButtonInputValue();

        public override IInputValue<bool> Input { get { return _input; } }

    }

I still wonder what you need this for… this seems needlessly over-complicated.

3 Likes

In InputTrigger, just because T is an IInputValue doesn’t mean that it will be an IInputValue. You could declare the following class and stick it into your InputTrigger.InputValue

public class Duck : IInputValue
{
  public void Quack() { }
}

Obviously it doesn’t make any sense to talk about Duck.InputValue.

I’m almost certain you’re over thinking the problem. @lordofduct provided a decent simplification of your code, but you should notice how complicated this declaration still is:

“InputTrigger of IInputValue of bool, and bool”

Thanks for the reply @lordofduct ! Your solution makes total sense. The whole idea of IInputValue was to be able to use it in some sort of collection and just be able to call .Value on it and it originally had Value in the interface that returned an object, and then was “overridden” in the generic version.

So the way I had my code setup was because the whole idea is to serialize InputTriggers into other classes so they can be used and that class doesn’t care about the implementation, all it knows is it can get values from it (ie a bool in the case of ButtonInputTrigger). With your solution, how would I be able to use a ScriptableObject? Could I wrap InputTrigger in a class that extends ScriptableObject?

Thank you for your help!

@lordofduct but my last question is how can InputTrigger become a ScriptableObject? I don’t want to serialize ButtonInputTrigger directly.

Just make it inherit from ScriptableObject.

    public abstract class InputTrigger<TInput, TValue> : ScriptableObject where TInput : IInputValue<TValue> where TValue : struct
    {

        public abstract TInput Input { get; }

    }

Right but I don’t want to serialize implementation, I’d like to serialize some sort of BaseInputTrigger. That by itself is not serializable.

Wat?

I’m not following what you’re saying.

@lordofduct What I mean is in another class I would like to serialize InputTrigger (as a ScriptableObject), and that class is able to query InputTrigger.Input or InputTrigger.Input.Value without caring about the implementation. Because i also have other triggers like AxisInputValue for example. Does that help clear things up? Thank you!

I would assume an AxisInputValue would return a float/Vector (depending dimensions), and not a bool. So you can’t easily treat an AxisInputvalue and a ButtonInputValue polymorphically.

Also, that implementation doesn’t stop you from allowing a script to reference the InputTrigger. It’d only limit you on what specific type you’re polymorphically referencing it as… in this case it’d have to be as ScriptableObject directly and you’d have to cast it (since you can’t have generics as a serializable field… unless unity changed something in more recent versions I’m unaware of).

That is unless you inserted an interrum class. Like a ‘BoolInputTrigger’ that inherited from the InputTrigger, and then ButtonInputTrigger inherited from. But again, this doesn’t resolve your polymorphic issue of Axis(float) vs Button(bool). There just isn’t a way around that with out explicit casting, in which case just referencing it as ScriptableObject would be fine.

…

What is your end goal here?

Not code wise… but actual like intent/purpose for this design you’re attempting?

Are you trying to make it where you can define input types as assets (ScriptableObject) that you can drag onto scripts?

This way you have assets in your project like “JumpButton” “ShotButton” “WalkAxis” etc… and you drag them onto a script like “WalkScript” so that you’re telling it which input to use for its behaviour?

Yup, you’re right, and basically defining the problem exactly. I am already referencing InputTrigger in another script but want to use the generic version essentially. Yeah, I had the thought of making a BaseInputTrigger somehow that delegated the generics to the subclasses but don’t think that will work.

Yes you nailed it, I want to be able to set triggers that can start (and subsequently stop) abilities in my game, but I want to be able to swap and set certain rules for which input triggers “trigger” these abilities. Are there any other solutions you can think of to solve this? Thanks again for your help @lordofduct

So what about this Axis vs Button thing?

How would you expect to polymorphically treat a Axis the same as a Button?

…

And you use this word ‘Trigger’… which implies an event. Like “On button pressed do thing” or “On axis tilted, do thing”. Is there more to it… is there “On axis tilted, do thing at the scaled value of the axis?” Which goes back to that problem of Axis vs Button.

I’m more looking to understand what problem you’re solving in relation to these so as to generalize it into a design. If you could give like 3 distinct/unique use cases, that’d be helpful.

Thanks for the response @lordofduct ! I don’t have to treat a button vs an axis polymorphically, I just want to be able to use serialized InputTriggers to retrieve values (either bool for buttons, float for an axis, and Vector2 for axes). So I suppose in this case when an Ability requests a value, it could cast to the appropriate class and get the value that way?

Yes, a trigger implies an input event in this case. So my idea was an Ability will have a “Ability start” input trigger which will run the ability, and optionally could have an “Ability end” input trigger which would be some state of input that would cause an ability to end. A good example is a move ability that starts when a player moves the joystick (start input trigger) and ends when the player stops moving the joystick (end input trigger). But maybe we want to make a button press end movement, I want it to be as simple as just swapping to a ButtonDown ScriptableObject.

Apologies if I wasn’t making sense initially, hope this helps!

So, if you aren’t going to treat them polymorphically… create 3 triggers…

BoolInputTrigger
FloatInputTrigger
Vector2InputTrigger

They all inherit from InputTrigger<IInputValue<***>, ***>. (where *** is bool, float, or Vector2).

Then when you need to reference them in any of your scripts, you do so by the one that script allows.

1 Like

With that said… if it’s more event based. Why not use something like UnityEvents or regular events in a ScriptableObject that the script listens for?

With all this said… if this was my goal. I’d decouple this all together and not have such things in my movement script, or my X script.

Instead I’d go with something like this approach… mind you this is pseudocode for demonstrative purposes.

public class ButtonInputDriver : MonoBehaviour
{
 
    public UnityEvent OnPress;
    public string InputName; //maybe create an editor that listed the available input names
 
    private void Update()
    {
        if(Input.GetButtonDown(this.InputName))
        {
            OnPress.Invoke();
        }
    }
 
}


public class JumpScript : MonoBehaviour
{
 
    public void DoJump()
    {
        //logic to do jump
    }
 
}

Then in the editor I’d attach these 2 scripts to GameObject/s and target the JumpScript with the ButtonInputDriver.

The reason I’d do it this way is that JumpScript (or really any script that performs a simple event) can be done so as a function. And well… I’d want to be able to drive that thing any way rather than restricting myself. I could drive it by my ‘Driver’ script… I could also drive it by calling it from another script that I wrote. I’m not limited in any way.

Furthermore, if my targeted script needs some like axis information… you just make it take that as an argument:

public class AxesInputDriver : MonoBehaviour
{
 
    public AxesUnityEvent OnAxis;
    public string XInputName;
    public string YInputName
    public bool UpdateAlways;
 
    private void Update()
    {
        var v = new Vector2(Input.GetAxis(this.XInputName), Input.GetAxis(this.YInputName));
        if(UpdateAlways || v.sqrMagnitude > 0f)
        {
            OnAxis.Invoke(v);
        }
    }


    [System.Serializable]
    public class AxesUnityEvent : UnityEvent<Vector2>
    {
     
    }
 
}


public class MovementScript : MonoBehaviour
{
 
    public void DoMove(Vector2 axes)
    {
        //logic to do movement
    }
 
}

Now we’re completely decoupled. MovementScript/JumpScript/etc can be called any way you decide is needed in the moment you need to do it.

For example… what if you want the character to jump if they press ‘A’… BUT you also want it to happen on some animation event, or some scene event, or whatever. Well… all those various ways can interact with the same JumptScript however.

Of course I don’t know your exact needs and maybe this inversion of control breaks your design in some manner. Just saying that’s how I’d likely go about it if I was building a system that was designed to react to events.

1 Like

Hey @lordofduct ! I really like the solution, I think my input triggers can still work in this case except they fire an event when triggered (like you said) so instead of abilities polling them, they just receive input data when it’s time to run. This is useful too because other data can be passed through this event, for example… a TeleportAbility will want to know when the teleport button is pressed but it will also need the current movement vector to know which direction to teleport to. I was thinking an intermediary class like an InputData that has the data specific to the ability that was triggered but also ancillary data like movement and aim vectors that the ability might need but does not necessarily trigger them. Any thoughts on that? This way, an Ability class could have an Ability.Run(InputData inputData) for example, and the ability subclass just gets what it needs from it?

My other concern is firing a UnityEvent every frame like you do for the movement axes. I’m wondering if there’s a way to keep using the movement vector without the need of an event every frame like that (this is for performance reasons by the way)? I would say we could cache this InputData and poll it but then I fear it’s going back to the previous design. Thanks for your help in advance :slight_smile:

I’m wondering if having InputData would lead to the same polymorphic issue with floats, bools and Vector2s. The trigger maybe could populate a dictionary of trigger names to values and the ability just knows about the trigger name. Still trying to think through this but so close. What do you think @lordofduct

Oh I also think by this approach that instead of scriptable objects each input trigger would just become a prefab that can be wired up