OOP - Best way to create instances of child classes from the list of their names

Hi.
I have a “Character” class that contains list of abilities:

public class Character {
        private List<AAbility> Abilities = new List<AAbility>();
    }

AAbility is an abstract class that covers wide range of abilities.

bublic abstract class AAbility {
        ... general logic here ...
    }

Abilities can be anything from lighting attacks to dashes.
Currently there are around 20 of them.

public class LightningStrike : AAbility {
     ... logic here ...
    }
public class LightningDash : AAbility {
     ... logic here ...
    }

etc.

Which abilities a character has is defined in Character constructor.
Information is stored in XML file.
List of abilities for each character is stored as text.
Text corresponds to ability classes names.
E.g.:

<?xml version="1.0"?>
<character>
    <Name>Striker</Name>
        ...
    <AvailableAbilities>LightningDash,LightningStrike,Empower,Blink</AvailableAbilities>
</character>

What is the best way to instantiate objects in Character constructor?

Simplest idea is to have a switch where ability name string corresponds to respective class.

foreach (string AbilityName in AbilityList) {
        switch (AbilityName) {
            case "LightningStrike": Abilities.Add(new LightningStrike()); break;
            case "LightningDash": Abilities.Add(new LightningDash()); break;
            ...
        }
    }

I don’t really like this approach as it is rigid.

Maybe there is other way?

Is this for a save game? Or are you actually authoring game data from XML?

I would highly recommend using ScriptableObjects to define all your game abilities. XML is one of the last things anyone should consider in 2022.

If it’s for a save game, here’s some more notes:

Load/Save steps:

https://discussions.unity.com/t/799896/4

An excellent discussion of loading/saving in Unity3D by Xarbrough:

https://discussions.unity.com/t/870022/6

When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. The reason is they are hybrid C# and native engine objects, and when the JSON package calls new to make one, it cannot make the native engine portion of the object.

Instead you must first create the MonoBehaviour using AddComponent() on a GameObject instance, or use ScriptableObject.CreateInstance() to make your SO, then use the appropriate JSON “populate object” call to fill in its public fields.

If you want to use PlayerPrefs to save your game, it’s always better to use a JSON-based wrapper such as this one I forked from a fellow named Brett M Johnson on github:

https://gist.github.com/kurtdekker/7db0500da01c3eb2a7ac8040198ce7f6

Do not use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.

https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide

In the odd situation this comes up I generally make a look-up table of some sort. Usually this is a scriptable object with a dictionary (using Odin serialisation). if I can easily reference the SO. Otherwise, you could just manually write out a dictionary in a static class too, though it gets tedious to update honestly.

But also what Kurt said. Why use XML when you have scriptable objects.

1 Like

Check out this thread on stack overflow, i think it answers your question pretty well: c# - Create an instance of a class from a string - Stack Overflow

1 Like

A factory method with a switch case is a valid approach. But I would use the nameof operator instead of magic strings, so that the code won’t stop working if an ability class is renamed.

This pattern does break the open-closed principle, but in practice it shouldn’t be that bad, since you only need to modify one centralized script and not any of its existing clients.

It can lead to the occasional delayed runtime exception when a dev adds a new ability but forgets to register it in the factory method. To combat this a unit test can be created that uses TypeCache to validate that all classes have been properly registered.

Alternatively, if slightly worse performance isn’t an issue, reflection can be used to automate setting up the factory and to avoid breaking the open-closed principle.

private static readonly Dictionary<string, Type> namesToTypes = new Dictionary<string, Type>();

static AAbility()
{
    foreach(Type type in typeof(AAbility).Assembly.GetTypes().Where(t => t.BaseType == typeof(AAbility) && !t.IsAbstract))
    {
        namesToTypes[type.Name] = type;
    }
}

public static T CreateInstance<T>(string name) where T : AAbility
{
    return (T)Activator.CreateInstance(namesToTypes[name]);
}

Thank you.

I didn’t see ScriptableObjects as good option for me. I will need to export big amount of data from MS Access into Unity. Around 100 of characters. Currently I have generic prefab for a character and stats including list of abilties set in XML.

I will look into ScriptableObjects though.
I understood the point with Load/Save.

SisusCo thank you for the reply.
I’m actually using something like a factory to create new characters.

Yeees. This was my actual concern. :))
Currently factory class handles this with switch cases.
I was wondering if it’s possible to “cast” a string into a class. Activator.CreateInstance() is the thing I was interested in.

Wow! That’s really cool!
Theoretically performance tradeoff is not big. Operation happens rarely (on loading scene, on special events during combat) and number of calls is small (up to 30 at once).

Thank you very much SisusCo!

P.S.:
I will still look into SO I think.
There is a good point related to Save/Load.
At some point characters can have misc stat modifiers that will make characters different from stats in XML. I will see which approach is better. Both have their strong and weak points.

1 Like

Personally I would try to get away from as many long chains of software as I could and just import it all into ScriptableObjects at a single point in time, get it under source control inside of Unity.

That has the side benefit that you can start making a better character editor immediately, by building editor tooling on top of the existing ability that Unity already gives you to edit ScriptableObjects.

Hi Kurt-Dekker,

Thank you for the update.
I didn’t work with SO before. How difficult it is to modify some aspect of the objects on 100 characters?
E.g. add a state to animator mechanim, or add additional child object, or modify a component on one of child objects?
Currently a character is a prefab and all above is very easy to modify since it’s single point of reference.

When people ask “How difficult…” about something I’m always curious as to what units you expect the reply in.

“Oh I would say it is about 27 difficulty.”

Seriously, at runtime or in an editor script, put them in a collection and iterate them.

None of those things happen to a ScriptableObject. Those things happen to GameObjects, which may have scripts (Components) plugged in, which may have public fields that have ScriptableObjects in them.

I suggest going and doing some ScriptableObject tutorials because me typing in a box isn’t going to do it justice.

Relatively to non-scriptable objects approach using your experience and objective judgement.
Are there any pros and cons?
As I understood it will be easier to setup save/load with SOs.

To provide more info on this I’m using following approach.
Each characters assets (animations, sounds, etc) are stored in separate folder under “Resources”.
Factory class uses empty prefab (without any resources), assigns all resources from character folder on runtime and creates a complete game object.

I will definitely look into this.
Thank you!

Heya @Kurt-Dekker !

After some time and experience I found SOs to be a pretty interesting thing and allow really cool architecture designs.

Thanks for the direction!

4 Likes