Here's how I often build lists of generics:
public abstract class AbilitySettingsBase : ScriptableObject {
public abstract IAbility InstantiateAbility(GameObject gameObject);
public abstract void RemoveAbility(GameObject gameObject);
}
public abstract class AbilitySettings<T> : AbilitySettingsBase where T : Ability {
public override IAbility InstantiateAbility(GameObject gameObject) {
Assert.IsNotNull(gameObject);
T ability = gameObject.AddComponent<T>();
ability.Settings = this;
return ability;
}
public override void RemoveAbility(GameObject gameObject) {
T ability = gameObject.GetComponent<T>();
if (ability != null) Destroy(ability);
}
}
[CreateAssetMenu(menuName="Jump Settings")]
public class JumpSettings : AbilitySettings<JumpAbility> {
}
[CreateAssetMenu(menuName="Fight Settings")]
public class FightSettings : AbilitySettings<FightAbility> {
}
public class AbilityManager : MonoBehaviour
{
//This will show up in the Inspector and we can assign the appropriate assets
[SerializeField] private AbilitySettingsBase[] abilitySettings = { };
//...
}
Here we have a non-generic base type AbilitySettingsBase that defines some abstract functions. We can have a serialized list of AbilitySettingsBase because, even though it's abstract, it's not generic. This will expose the list in the Inspector and allow us to select asset files made from ScriptableObject scripts that extend AbilitySettingsBase.
Where it can get more complicated is if the Ability scripts need to understand the AbilitySettings type. If you try to use generics you might end up with a much more convoluted generic scheme, e.g.
public abstract class AbilitySettings<T, U> : AbilitySettingsBase where T : Ability<T, U> where U : AbilitySettings<T, U> {
//code ommitted
}
public abstract class Ability<T, U> where T : Ability<T, U> where U : AbilitySettings<T, U> {
//code ommitted
}
I'm not even sure off the top of my head if that would work. Another possibility is to not try to make the generic scheme more complex, and instead do something like this:
public class JumpAbility : Ability {
private JumpAbilitySettings settings;
override public AbilitySettingsBase Settings {
get => settings;
set {
Assert.IsNotNull(value);
JumpAbilitySettings settings = value as JumpAbilitySettings;
Assert.IsNotNull(settings);
this.settings = settings;
//apply settings here
}
}
}
This isn't as type-safe but will save some headaches in generic design.