# ScriptableObjects with unique methods (Spell System of sorts...)

I’m just testing around with a type of ability/spell system & have noticed that using scriptable objects seem like a fantastic way to get started.

Most of my abilities share the same variables in common:

  • Name & Description
  • In game cost (Mana, points, ect…)
  • Icon (Inventory reasons)

So I figured using ScriptableObjects could be a neat way of creating a flexible system. Yet I can’t work my way around a big problem, each ability is unique. They’re all different in a drastic way, from something like health boost to freezing time to a speed dash.

Now I figure the normal way around this is that what ever the trigger is I decide on that switches the ability on or off would check the ScriptableObject code for an enum or boolean value to determine the specific ability, yet wouldn’t that mean I would have to hard code every ability/spell? I’m not sure exactly.

I’m just curious if there is a way that I can grant a ScriptableObject a unique method for each one, which could just be toggled on or off from an AbilityManager (Ex. like attaching a MonoBehaviour ability method script to the ScriptableObject & toggle when ready.)

I honestly am still trying to understand the concept of a ScriptableObject, so I’m not sure if I’m creating some type of paradox asking this question, but any help is very much appreciated. Thank you.

ScriptableObject is just an asset. It like a component itself. If your ScriptableObjects have something in common, make this common a separate scriptable object and reference it instead of inheritance. Also scriptable objects may implement interfaces and have different implementations of common methods

Using method overriding is the most obvious way to handle your situation.

You can create an abstract base class that all your abilities extend:

public abstract class Ability : ScriptableObject
{
    public string name;
    public string description;
    public Texture icon;

    public abstract void OnActivated(); // all abilities must implement this method
}

Then all your different abilities can extend this base class, and override the abstract method, where the unique code for the ability is implemented.

public class FreezeAbility : Ability
{
    public override void OnActivated()
    {
        // add unique code for this ability here
    }
}
3 Likes

Inheritance is not supported well for ScriptableObjects. For example, if you add FreezeAbility to some List and serialize the list, you will not get FreezeAbility after deserialization. Unity will try to create Ability instance and will fail. ScriptableObject may be extended via composition and interfaces

public interface IAbility { float CalculcateDamage(); }
public interface ICardView { string getName(); sprite getSprite(); int getCost(); }
public class FreezeAbility : ScriptableObject, IAbility, IObjectView
{

}

or, easily, by adding all required fields to base type.

Actually that is not true. While unity can’t (yet) handle serializing polymorphic lists of normal c# classes, it works perfectly well with ScriptableObjects and components and all other classes that inherit from UnityEngine.Object.

2 Likes

The example you linked illustrating use of interfaces, not inheritance in scriptables. Sadly unity loses type info when serializing data.

Yeah I get it is possible to serialize one shared instance of custom class for multiple scriptable objects. How is this related to SO inheritance wich I suggested to avoid?

It isn’t. ScriptableObjects already support polymorphic serialization by default, like I said.

I suggest you read some more about how Unity serialization works, as you seem to have some misunderstandings about it. In particular, you should take a look at the section No support for polymorphism.

If you still don’t believe me, you can try it out very easily for yourself! Just add this script along with the Ability class examples I posted earlier to your project:

using UnityEngine;
using System.Collections.Generic;

public class TestPolymorphicSerialization : MonoBehaviour
{
    public List<Ability> abilities = new List<Ability>();
}

If you add the TestPolymorphicSerialization component to a scene, add some references to the FreezeAbility to the list, save the scene and then reopen it, you will see that the references indeed do persist.

2 Likes

@palex-nx using derived ScriptableObjects works fine, I’m using it all the time. The only limitation might be JsonUtility serialization which doesn’t work untill you know the type of the serialized ScriptableObject.

@SisusCo Reviving this old thread to ask if this implementation of an ability system would work for a multiplayer experience. If all players referred to the same FreezeAbility asset, and one player called the OnActivate() then wouldn’t all players experience the data effect of OnActivate()?

@dips234 For a multiplayer game you’d likely want to have separate methods that get called for server/host and the clients.

public class AbilityActivator : NetworkBehaviour
{
    public void Activate()
    {
        if(IsServer)
        {
            OnActivatedOnServer();
        }
        else
        {
            ClientRemoteCallOnActivatedOnServer();
        }
    }
  
    private void OnActivatedOnServer()
    {
        activeAbitility.OnActivatedOnServer();
        ServerRemoteCallOnActivatedOnAllClients();
    }
  
    [ServerRpc]
    private void ClientRemoteCallOnActivatedOnServer()
    {
        OnActivatedOnServer();
    }
  
    [ClientRpc]
    private void ServerRemoteCallOnActivatedOnAllClients()
    {
        activeAbitility.OnActivatedOnClient;
    }
}