Correct pattern in Unity for many objects with different params

I’m trying to figure out the best (correct?) way of implementing a pattern where I have a single type of object but with many configurations.

Let me give two simple examples for you.

In an RPG (let’s use Diablo 3), your character has a set of skills. These skills all have the same structure and variables (level requirement, mana cost, cooldown, UI image, etc.) and they all execute some logic.

In a trading card game (let’s use Magic for this one), there is a collection of cards. As with the previous example, all of the cards are the same except for the contents of a few variables (card image, description, colour, cost) and the logic.

You could apply the same idea to enemy types, weapons, rewards, or really any object in your game that has a high amount of developer defined variance.

What would be considered the correct way to implement this kind of system in a scalable way? Digging through various tutorials and available projects (which are rarely going to be representative of a full product), it seems that I should be creating a new prefab for each skill or each card, but that feels wrong to me, and so here I am.

To take it one step further, take either of these examples and imagine them working in a system. Let’s take the TCG example. Let’s say I want to give a player a random card. How can I know all of the cards? It is just a hand maintained array in a script somewhere? I suppose you could use a tag in that example, but what if you wanted to filter by, I don’t know, rarity or set (sorry, the example is getting a little abstract, but hopefully you follow)?

This is what polymorphism is for.

C# offers up polymorphism in 2 distinct ways:

Class Inheritance -
you have a base class that all like types inherit from. Such as a ‘Weapon’ class that all weapons inherit from. The base class will have all the properties that are intrinsic to all weapons, and the sub classes just implement the how stuff works. (there’s a function called ‘Activate’, but a sword plays a swinging animation on Activate, where as a gun shoots a projectile on strike… which in turn creates another weapon, a bullet, which will too ‘Activate’ when it hits something).

Interface Implementation -
With an ‘interface’ you define the… well, interface (names & types of methods & properties that an object should have). Then your class can implement said interface. Such as a ‘IWeapon’ interface that all weapons implement.

Remember you can inherit interfaces from interfaces adding more to the contract. So you could have IWeapon as well as IRangedWeapon that have properties specific to ranged weapons… like range.

I personally use a combination. I define an interface for my various types, but always have a base class to inherit from. This way the base class can act as boiler plate code so I don’t have to do ALL the implementation of the interface (like the properties) every time I implement it… instead I just inherit from the base class.

Here is an example where we have a IWeapon interface, and a Sword weapon.

//I like all my component based interfaces to have a generalized IComponent interface it inherits from
public interface IComponent
{

    Component component { get; set; }
    GameObject gameObject { get; }
    Transform transform { get; }
    bool enabled { get; set; }

}

public interface IWeapon : IComponent
{
    float Damage { get; set; }

    void Activate();
}

public abstract class AbstractWeapon : MonoBehaviour, IWeapon
{
    //note, MonoBehaviour already implements most of the properties that IComponent require

    [SerializeField()]
    private float _damage;

    public Component component { get { return this; } }

    public float Damage
    {
        get { return _damage; }
        set { _damage = value; }
    }

    public abstract void Activate();

}

public class Sword : AbstractWeapon
{

    public override void Activate()
    {
        //do sword swing
    }

}

BUT if say I have some class that needs to be a weapon, but I need to also inherit from some other class as well, I can instead implement that interface and tack on the implementation needed.

Lets say for instance we have a IProjectile interface, and projectiles aren’t always necessarily a weapon (maybe you can throw innocuous things…). But you need a projectile that is also a weapon, like a bullet.

public interface IProjectile : IComponent
{

    float Range { get; }

    void Launch();

}

public class AbstractProjectile : MonoBehaviour, IProjectile
{

    [SerializeField()]
    private float _range;

    public Component component { get { return this; } }

    public float Range
    {
        get { return _range; }
        set { _range = value; }
    }

    public abstract void Launch();

}

public class Bullet : AbstractProjectile, IWeapon
{

    [SerializeField()]
    private float _damage;

    public float Damage
    {
        get { return _damage; }
        set { _damage = value; }
    }

    public override void Launch()
    {
        //shoot the bullet
    }

    public void Activate()
    {
        //bullet struck an enemy
    }

}

Then of course if you have various types of swords that vary only by the amount of damage they wield. They all get the Sword component, BUT you just set the damage value to what it needs to be.

You still need the prefabs (or some type of factory class), because things like the models that represent the sword/bullet/whatnot, need to be set up. And that isn’t a property on a class… those are usually sets of gameobjects and components.

So now you can set up a GameObject with a Sword component that has the graphics for a sword as a child of it, and configured to be a ‘Great Sword’ or something.

Now if you want to have some stats to distinguish certain swords by say… rarity. Ok, have a ‘Rarity’ property on the Sword class. Or even the IWeapon interface if all weapons are rare… or shit, if ALL items are rare, have an IRareItem interface that IWeapon also implements, that defines the rarity property.

public interface IRareItem : IComponent
{
    float Rarity { get; set; }
}

public abstract class AbstractWeapon : MonoBehaviour, IWeapon, IRareItem
{
    //note, MonoBehaviour already implements most of the properties that IComponent require
 
    [SerializeField()]
    private float _damage;
    [SerializeField()]
    private float _rarity;
 
    public Component component { get { return this; } }
 
    public float Damage
    {
        get { return _damage; }
        set { _damage = value; }
    }
 
    public float Rarity
    {
        get { return _rarity; }
        set { _rarity = value; }
    }
 
    public abstract void Activate();
 
}

NOW, the only downside to all this interface business is that Unity 4 does not support interfaces when calling ‘GetComponent’.

Unity 5 DOES have support thankfully!

In Unity4, I just create an extension method for it:

public static class ComponentUtil
{
    public static T GetFirstLikeComponent<T>(this GameObject obj)
    {
        if (obj == null) return default(T);

        foreach (object comp in obj.GetComponents<Component>())
        {
            if (comp is T) return (T)comp;
        }

        return default(T);
    }
}

You can actually find a lot of this stuff as I use them in my spacepuppy framework.

This is my ComponentUtil:

Or this is my IComponent:

Lots of very useful information there and I definitely appreciate the response, but I probably should have mentioned my experience in the OP. I’ve been working in AAA game development for almost 7 years. I definitely know how polymorphism works. :wink:

My question relates to the data structure of things. To put it to the extreme, how do I generate and maintain a game with 50 variations of an enemy or 1000 different cards for a TCG. For simplicity sake, let’s just say all of the cards and enemies use the exact same logic and just have different values (health, damage, speed).

In other words, I don’t need to know how to create one entity, but many.

A bunch of prefabs? An XML file? Something else?

Sorry, I’m very used to more newb questions in relation to this context here on the forums. From you wording above, combined with that bias, I assumed you meant something different. Well hopefully I demonstrated I’m not an idiot, so my follow up comments may be more trustworthy??? heh…

Anyways.

Usually it’s just prefabs.

Which can get annoying, because prefabs don’t support inheritance/nesting. Our team uses this plugin for that… it’s AWESOME:

Like srsly, we had been searching for a nested/inherited prefab solution for a while as we don’t have the time to write our own. This thing does everything right, exactly as I expect it to, and even more. Unlike many other nested/inherited prefab libraries I had found from other people.

I’m telling you… GET THIS TOOL TODAY! It’s a life saver. I don’t know how anyone works on large scale games without it.

Then after that… yep, a ton of prefabs. Prefabs are the unity way of doing things.

You can implement some factory patterns if you want… so like say you have some ‘BaseOgre’ prefab that is the basic rough layout of any old Ogre (it’s graphics, the animations it has, so on so forth). But then the factory adjusts its stats, attaches a weapon to its weapon prop bone, etc etc.

Then at your spawn points you spawn via this factory. The factory references the prefabs it can spawn, and does the work of customizing it. So like maybe the spawn point hands off to the factory what weapons are available at this spawn point for the Ogre to hold.

Those weapons also being their own prefabs too. Of course.

1 Like

So to get more specific, such as the case of the 1000 different cards thing. Cause that’s a lot of cards, and maintaining them would become hell if you had to change something about EVERY card after they’d all been created.

This is where the nested/inherited prefabs come into play. And they’re GREAT.

I’d create a base card prefab. On it I’d set up all the parts of it that are specific to all cards. The skin of the card, the components all cards get, etc. etc.

Then I’d create child prefabs of that base card prefab for every card (or sub card types for groups of cards, like a black label set or something). And on those I’d add the stuff that is unique. Noting that if you adjust some property of a component in this child prefab, it stays unique from the parent prefab. Very nice.

YOU could also create a factory too, that had some xml that defined the unique aspects of the cards. And you created an instance of that BaseCard (or sub group). And then the factory adjusted the stats.

This could be useful for defining decks of cards… and like distributing download packs. Like you could have the download packs available on the web, and really all it is was a bunch of jpeg images and the xml file that goes with it that could be pulled from any old website. It could even allow for customization where users could write up their own decks using basic xml and drawing jpegs to some standard.

But that gets into the whole… what is the ‘correct’ way is very specific to YOU.

I would say that the xml way is tough to design up a consistent standard that is efficient to write the xml for. So your cards will probably end up having one xml schema, and your enemies will end up with another schema… it becomes an engineering quandry… ugh.

The prefab solution has a ‘this is the way we do it’ standard. It’s not perfect, but it’s a standard that exists and you don’t have to engineer yourself.

So really, what I mean is… I use XML for things that are super special and really need that xml to go with it as it’s just TOO convoluted to pull off with prefab inheritance/nesting. Another would be procedural generation as well… things like that obviously can’t be done with prefabs completely (something with 10^100 possible permutations would be STUPID to do in prefabs, but also stupid in xml or some other data schema as well).

1 Like

Thanks for the responses. This has been very helpful. It’s good to have some confirmation on this stuff.

Nested prefabs sounds super useful, as well, so I’ll definitely check that out. Thanks again. :slight_smile: