This is going to be a shorter post today, as there’s not that much to the class_name keyword, but it can come in handy quite often so I’m going to go ahead and cover it. I’ll be using some assets provided by Kenney today.
Simply put, the class_name keyword lets you use static typing in Godot for your custom classes (or scripts, as all scripts ARE classes in Godot). Doing this is as simple as adding
class_name MyClass to the top of your script. You may also optionally pass in the path to an image to get a custom icon for the script as well by using the format
class_name MyClass, "res://my_cool_icon.png. In the source code for today’s sample project, for instance, the RigidBody2D node representing a ball looks like the following:
extends RigidBody2D class_name Ball, "res://assets/ball/ball_red_large_alt.png" # We'll look at this function in just a bit func do_something(): print('Did something!')
With that script, the
Ball class is now registered in the global namespace, and we can reference it anywhere else in our game. For instance, there is anzArea2D node that should take an action when a ball enters it. The script for it is written as follows:
extends Area2D func _ready(): connect("body_entered", self, "on_body_entered") func on_body_entered(body: PhysicsBody2D): if body is Ball: print('Ball entered area') # ... # other code for later # ...
You can also get code completion suggestions when a script uses the
class_name keyword, just like you would with a built-in type. For instance, the Ball class has a method named
do_something(). We can therefore get a suggestion for it when writing some code elsewhere:
And since we passed in the icon parameter for our custom class, we’ll see (after restarting the editor) our custom icon both in the tree and in the scene tab with its icon, making it easy to quickly find.
class_name keyword also lets you use the
extends keyword to subclass your custom classes instead of referencing a specific path to a script:
extends Ball class_name BlueBall, "res://assets/ball/ball_blue_large_alt.png"
And you can instance your script at runtime using the format
var util = MyCustomUtil.new(). The one thing to note is that this method ONLY instantiates the script, not the scene the script is attached to. Instantiating the ball class (
var myBall = Ball.new()), for instance, would give us a script for A rigidbody, but not THE SCENE made previously. So be mindful of that!
There are two main gotchas to be aware of when using
- The global namespace
- Cyclic dependencies
The global namespace
As mentioned above, the class is registered in the global namespace so that it’s available everywhere. This is a generally a good thing as it’s what makes
class_name useful. The issue it brings is that every class must have a unique name across your entire application. This isn’t really a problem for high-level classes, such as a Player class, a SkeletonEnemy class, a Knight class, etc, but it can be a problem if you’re doing something like a state machine where each entity may have a state with the same name. All of the previous examples could have a
MoveState class, for instance, but that is not allowed in the global state. You could give each state a unique name, such as
PlayerMoveState, but really, that’s a situation where you shouldn’t need a global reference to the class anyways.
You also have to make sure not to use a class name that is already in use in Godot, such as “Sprite” or “Timer”. So just think about the implications of throwing a class into the global namespace before doing it.
These are the bane of every developer using Godot at some point or another. Let’s say we have a couple of utility classes in our game that we want available globally:
PolygonMathfor doing calculations for a given polygon
LineMathfor doing calculations on a given line
Even just writing the code below starts spitting out errors in Godot:
# polygon.gd extends Node class_name PolygonMath func intersects_line(line: LineMath): pass
# line.gd extends Node class_name LineMath func intersects_polygon(target: PolygonMath): pass
res://assets/circular_dependencies_example/polygon.gd:4 - Parse Error: The class “LineMath” couldn’t be fully loaded (script error or cyclic dependency). modules/gdscript/gdscript.cpp:585 - Method failed. Returning: ERR_PARSE_ERROR
The issue? Godot tries to load the PolygonMath class and sees that this class requires the LineMath class, so it goes to load up the LineMath class and sees it requires the PolygonMath class. It doesn’t know how to load the scripts since they both rely on one another and therefore spits out the cyclic dependency error. According to the Godot developers, this will be fixed in Godot 4.0. In the meantime, you’ll just have to be mindful of how and when you use the class_name keyword, and occasionally just accept duck typing instead of strict typing to avoid errors.
And that’s it for this post! Just a short (and hopefully sweet) look at typing your custom classes in Godot. Check out the source code for this post for concrete examples if you’re still uncertain of how this all works together.