I want to declare a global EnemyType enum, have a class that exports a field of this type and reference the enum in different scripts. This is the best I could come up with, does this look right?

1. Add an autoload script enums.gd to eventually hold all enums that should be globally available.

# enums.gd
extends Node

enum EnemyType {
    unknown,
    Baiter,
    Bomber,
    Lander,
    Mutant,
    Pod,
    Swarmer,
}

2. Autoload this script as Enums.

3. In script enemy.gd declare an enemy base class with an export value type of type Enums.EnemyType.

# enemy.gd
class_name Enemy extends Node2D

@export var type := Enums.EnemyType.unknown

# [...]

4. Create subclasses of Enemy and set their type in the editor. For example class Mutant:

5. Now somewhere in another script I can write something like this:

func _on_enemy_destroyed(enemy: Enemy):
    if enemy.type == Enums.EnemyType.Mutant:
        # do something

This all seems to work fine, I just wonder if this is the best way to use global enums? Or am I missing something?

Because in other languages I would declare the EnemyType enum directly in enemy.gd but if I do that then I am only able to use the enum in this script and not in other scripts, unless I am mistaken.

  • Erich_L replied to this.
  • I do it the same way, except that I use UPPER_SNAKE_CASE for enum keys:

    enum EnemyType {
        UNKNOWN,
        BAITER,

    That's the convention used by the API enums and the style guide.
    https://docs.godotengine.org/en/4.1/classes/class_control.html#enumerations

    Toxe I have also been having trouble with this. It could be now, but before I don't think global enums were possible. My solution has been rather simple because %100 of the time I only needed an enum in two places: one on the code for the enemy for example, and another on the code recording or storing data about enemies. The rock head solution then is just to keep the enums in two places exactly the same and leave a comment to yourself saying "# also exists in _____.gd".
    Not ideal I know but it works fine for me.

    • Toxe replied to this.

      I do it the same way, except that I use UPPER_SNAKE_CASE for enum keys:

      enum EnemyType {
          UNKNOWN,
          BAITER,

      That's the convention used by the API enums and the style guide.
      https://docs.godotengine.org/en/4.1/classes/class_control.html#enumerations

      • Toxe replied to this.

        Erich_L Yeah that works but it's a bit too much redundancy for my taste. But I also need to use these enums in more than one other place.

        DaveTheCoder Right, but I don't like that style too much. At least for my own code, I don't mind it in the Godot API or code from other people.

        I come from C++ and ALL_CAPS to me always means that it's a preprocessor declaration or macro and I usually tend to go with Enum::SomeValue or Enum::some_value. I can never really agree with myself which style I prefer haha. 😉

        But thanks, good to know that this seems to be a plausible way of using global enums.

        I had to declare some global enums today and I did it as you did (autoload script), but with a slightly different approach: Instead of creating an "Enums" script, I used an "EnemyUtilities" script. In it I've placed some general purpose functions related to the enemies, that may be used by the enemies themselves or other entities. For example, I have a function that defines how the enemies should move, which I also need to call from a separate node that draws a preview of the movement, or a function that returns a different color depending on the enemy type and that is widely used from many places for FX, UI, materials...Hope you find this useful 🙂

        • Toxe replied to this.

          correojon Yeah, I know what you mean. So basically not just some global enums but also global functions, but slightly grouped together thematically. Makes sense, indeed.

          Not sure if this was mentioned already but all enums are global, they are just guarded by class namespaces. There's no need for autoloads i.e. you don't need any objects to access them. If enum is declared in a named class, it will be accessible in all other classes.

            xyz this seems self-explanatory now that you mention it. I use FileAccess enums all the time without instancing it. unfortunately i wasnt listening to the little voices inside my computer telling me the obvious.
            thanks for making my hackjobs less hacky.

            xyz Can you give an example for this? Because when I have a file enemy.gd...

            # enemy.gd
            class_name Enemy extends Node2D
            
            enum Foo { red, green, blue }
            # [...]

            ...and then try to use the enum Foo from another script I cannot access it.

            # another.gd
            print(Foo.red)
            print(Enemy.Foo.red)

            This would only result in errors. Am I missing something here?

            • xyz replied to this.

              Toxe Enemy.Foo.red should work. Assuming you're in 4.x

              • Toxe replied to this.

                xyz I am, but it doesn't.

                • xyz replied to this.

                  Toxe Don't forget to save *.gd files.

                  • Toxe replied to this.

                    xyz Looks like I never really tried actually running that code because at runtime it does indeed work. But the editor gives an error message and suggestions don't work either. So in a way it does work if you don't mind a lot of red lines and error message in your code. 😉

                    Okay I restarted the editor and now the red error is gone and suggestions do indeed work. But once I add another value to the enum and try to print that the error comes back.

                    You should submit a bug report on the github tracker.

                    • Toxe replied to this.

                      I haven't encountered any funky behaviors so far. It works as expected. Anyway, bugs aside, the point is - enums are global by design.

                      • Toxe replied to this.

                        xyz If they are global shouldn't print(Foo.red) then work?

                        • xyz replied to this.