Is there a way to store functions in scripts that have no context for them?

I’m trying to make a modular attack system that lets me drag and drop attacks into my player. One might move the player forward, for instance, while another moves them back, while yet another makes them invisible.

The issue I’m currently trying to solve is that I can’t think of a way to store the functions in a modular way (i.e. within a scriptable object), because there are a lot of variables that it would need reference to from the player class (like the movement script, or the renderer to disappear, etc.). All of the variables would be available at the time the function is called, however, so I thought there might be a workaround.

Has anyone done something like this and have advice?

Can’t really ‘store methods’ on their own, not as simply as you might want. UnityEvent’s exist as a way to reference methods, but they work by referencing a Unity object, and serialising a string to the method in order to invoke it via reflection. I don’t think it’s useful in this case though.

SerializeReference is one option. It lets you serialise non-Unity Object reference types (ergo plain C# classes) via a base class or interface (polymorphism). It makes a good option for pluggable behaviour, and is my general approach. Only problem is that Unity doesn’t have inspector support for it out of the box, so you need third party tools to make it usable.

Another option is regular inheritance. A base class scriptable object type with derived types that override the relevant methods. Then you can just plug these into regular unity object reference fields.

As far as getting the relevant values from the player: just pass the player into your attack method.

2 Likes

My choice would be a base abstract Attack-MonoBehaviour (Component). That way your base-class defines a public abstract void Attack(Player player);, and you can add the actual attack-components to your Player-GameObject.
In my own Project I simply do a GetComponents() in Awake() then index that array when doing a specific attack.

1 Like

Thanks for the advice! I think inheritance is going to be the way to go. Otherwise, I might try calling functions via animation events, but that seems like a bit of a messy solution.

I just quickly created a generic SerializableInterface class which comes with a property drawer that allows you to create a serialized field of a certain interface type. With that you can do:

public SerializableInterface<IMyInterface> someVariableName;

That’s all. In order to access the serialized reference you can use the Instance property. So assuming IMyInterface looks like this:

public interface IMyInterface
{
    void Attack(Player player);
}

you can simply do

someVariableName.Instance.Attack(player);

The property drawer allows you to drag a whole gameobject onto the field. It would search for the first component on that gameobject that implements that interface. If there are multiple components on the same gameobject it would display a context menu with all components that match the interface type in the order they appear in the inspector. That may be relevant if the same component is attached several times to the same gameobject. Though the usual two inspector approach does of course work as well.

4 Likes