Should I be using Scriptable Objects that only contain a method implementation?

So, I want to create an ability system for my game (rpg) and I want it to be as modularized as possible, since I will be reusing a lot of functionality and I want to have an easy way of creating and tweaking them.

I have then decided to make this system heavily based on Scriptable Objects, with abilities being created as such and containing another Scriptable Object that has targeting data(such as range, Area of Effect, etc) as well as a list of On Use Effects.

Now comes my problem : I was about to create those On Use Effects as Scriptable Objects as well, when I realized that most of the time they will be nothing but a single method implementation (The OnUse method) and there is this voice in my head that keeps saying “Scriptable Objects are supposed to hold data”, and I can clearly see there is a fundamental difference between this and my previously created Scriptable Objects, since with no fields I would be only ever having one instance for each implementation. This seems like I’m doing it the wrong way, but I still need to do it in a way that communicates well with the setup I have.

So, is it bad design to use Scriptable Objects that only hold a method implementation? Is there something more appropriate for this?

I think it really depends on what you consider to be overkill.

As an aside, there’s no reason you can’t have a ScriptableObject contain methods though. I’m guessing there’s a good chance you’ve seen it, but this talk is really great at showing a number of ways to use ScriptableObjects.

But back to my point, it all comes down to how far you want to go. If you want to have a plug-n-play behavior available to your designers, then giving them ScriptableObjects representing functionality isn’t illegal or even discouraged. On the other hand, if you think you’ll end up only having one OnUseEffect per ability, its not really worth it.

Another way around the problem is to create your OnUseEffect class as a straight C# class, which is created by a Construct method in an OnUseEffectSettings scriptable object. This way, if you need to, you can pass settings into it on create, and you aren’t relying on the ScriptableObject for functionality (which opens up possibilities as far as tracking your own state go) (look up Dependency Injection for why this sort of thing is awesome).

In the end, don’t worry so much about using Unity the way Unity wants you to use it. There’s a lot of good stuff Unity put in, and knowing how they want you to go about things is a great starting point until you find that its getting in the way. And when that happens, do what you need to do. There’s also a lot of great material out there describing other ways of using Unity. Even that talk I linked to above (which is by a Unity employee, talking about a different way to use Unity) is an example.

Anyways, I hope this helps!
Best of luck