2

Last night I was working on a C-Sharp project trying to code around GameObjects hitting each other. I wanted different gate types (e.g. wood, stone, metal) that could be broken down by different weapons (e.g. club, sword, axe).

For detecting the collisions on the GateManager I was using tags for each gate. So for example:

    void OnTriggerEnter(Collider collider) {
       switch (gameObject.tag) {
         case "WoodenGate":
             ...
         case "StoneGate":
             ...

The above code isn't exact but should give an idea. It felt wrong to have each gate type set as a different tag (if every gate type has a different tag and every material type has a different tag, etc, then I will end up with hundreds of tags).

So I came up with an alternative. I set up an enum of GateType and created 3 values (WoodenGate, StoneGate, MetalGate). I then attached a public GateType property to the GateManager class. This allowed me to select which enum was relevant to each prefab within the 'Inspector' window of unity. It was very neat and I was really happy.

Then a problem arose: I added a 4th enum halfway up the list (e.g. GlassGate). Because the enums are simply int values, the 3rd item was no longer MetalGate but was now StoneGate. This meant the metal gate prefab suddenly had a GateType of stonegate. Which broke my game.

Apologies, for the wordy question, but my question therefore is how should I best label and identify lots of different types of items? I don't want to use tags (because I would need too many) and I don't want to use enums (because they form the brittle issue when used in conjuction with the unity inspector.

I assume this must be a common requirement for many games (e.g. in games you can use your pickaxe on lots of different gameobjects to gathering different resources) so just wondering the best practice?

Programmer
  • 121,791
  • 22
  • 236
  • 328
Charlie S
  • 4,366
  • 6
  • 59
  • 97
  • What kind of interaction are you trying to do with each gate type? Rather than having all the logic in your GateManager script, could you instead write a set of MonoBehaviour classes which each dictate a reaction, attach them as needed to each gate, and blindly call them upon collision with a gate? (Just have them all inherit from the same class/implement the same interface to easily retrieve and call them.) – Serlite Nov 17 '17 at 16:19
  • The logic is simply if the weapon colliding with the gate is of a high enough level, then set the gate not active (i.e. destroy it). So e.g. a sword can destroy a wooden gate but not a metal gate. So very simple logic indeed so having multiple classes probably would be overkill? – Charlie S Nov 17 '17 at 16:25
  • 2
    In that case, why not just assign a strength value to each object (or the material it's composed of), and a corresponding strength to each weapon? Then compare the two values on collision. If you have to make an enum for every kind of gate, then every kind of barricade/furniture item/block/etc...that's going to be a lot of enum values, which will become more and more unmaintainable as your project grows. – Serlite Nov 17 '17 at 16:32
  • Serlite - thank you. Cant believe I missed that as a way to code it. Awesome stuff – Charlie S Nov 19 '17 at 13:22

3 Answers3

4

1.You can declare and use many interface to accomplish this but the downside is that you will need to declare many of them and implement them in many of your script that is attached to each object:

public interface IDestroyable { }

public interface IOpenable { }

then you inherit from itin your scripts:

public class MyScript : MonoBehaviour, IDestroyable{}

public class MyOtherScript : MonoBehaviour, IOpenable{}

Check which one it is during collision:

void OnTriggerEnter(Collider collider)
{
    if (collider.GetComponent<IOpenable>() != null)
    {

    }

    else if (collider.GetComponent<IDestroyable>() != null)
    {

    }
}

2.You can also use enum you mentioned.

public enum GateType
{
    WoodenGate,
    StoneGate,
    MetalGate
}

Attach the single script that describes what type of gate this object is to all the Gate object prefabs then chose the enum for each one from the Editor or script

public class GateDescription : MonoBehaviour
{
    public GateType gateType;
}

Check which one it is during collision:

void OnTriggerEnter(Collider collider)
{
    GateDescription gateType = collider.GetComponent<GateDescription>();

    if (gateType != null)
    {
        switch (gateType.gateType)
        {
            case GateType.StoneGate:
                break;

            case GateType.WoodenGate:
                break;

            case GateType.MetalGate:
                break;
        }
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 1
    I think interfaces are the way to go. – Adam B Nov 20 '17 at 10:16
  • @AdamB The only downside of using interface is that you have to create many scripts for each gate that inherits from different interfaces. Other than that, it should be fine. – Programmer Nov 20 '17 at 10:19
  • Yes, but I think over the long term the code will be much more readable. A tad more work to set up, but infinitely more understandable and useable in the end. – Adam B Dec 05 '17 at 19:51
  • Yes, readability is one of the advantage of using interface for this. – Programmer Dec 05 '17 at 20:24
1

Enums are the most convenient way to do this. Add an index to the enum value and increment it manually.

public GateType gateType;

public enum GateType
{
    Wood = 0,
    Stone = 1,
    Brick = 2
}

Now if you want to add another one in the middle of the 'list' do it like this:

public GateType gateType;

public enum GateType
{
    Wood = 0,
    Metall = 3,
    Stone = 1,
    Brick = 2
}

Unity should keep the correct values on your objects, since you added the index.

Sandro Figo
  • 183
  • 10
0

You could set it up as per the spawn/init event. If you're going to use the enum method you could just define it when the object is spawned. This obviously depends on your game mechanic. However I would probably outsource this to a monobehaviour specifically for the material behaviour you want. That way if you want to change how the wood arches behaves you just have to update the one script and it's self contained. this will help as your codebase gets bigger and will improve the compile and load times if you use the features of 2017.3 that allow you to define which files go into which assemblies

Robert
  • 535
  • 3
  • 13