Are template types planned?

Hello,

I wanted to see if a few features were somewhat planned or at least evoked.
I saw that interfaces will probably not be a thing but that a trait system is viable, although the ticket is quite old.

I wanted to know. Is it somewhat planned to have templates in GDScript? By that I mean:

func test[T, R](a: T) -> R

...
var result: R = test(5)

Or even just as arrays with Array[T]. Example you want to make a tree class (as in data structure trees) with type constraints. It would be cool to have var t: Tree[Node].

I know this would create quite a few overheads, especially for inference. I have not found any topics mentioning a possibility of this being considered.

Of course I’m not asking anything ! I know devs are hardworking on their freetime to add new features. I just wanted to know if there was already a decision on this or not.

1 Like

Is generics the same thing you’re talking about? If so, there is this proposal:

But I doubt it will be implemented any time soon, so don’t get your hopes up. The same with traits.

You can already (sorta) do type operations by using Variant and just utilize dynamic typing. It’s good enough for most cases.

Implementing these systems would destabilize the whole structure of the engine and make the code much harder to maintain. Therefore they hesitate on implementing it now and rather focusing on other more useful features.

2 Likes

I believe they’re trying to get traits in 4.7.

As for Templates, you’ll have to use GDExtension and write them in C++. Though you could just write whatever Nodes you want in C++ with GDExtension and then use them in GDScript.

However before going down that road, keep in mind any variable can be a Variant (or some other class - usually a node-derived one), and you can do class type checking. For example let’s say you are checking an Area2D anytime a character enters, and you want to do different things based on which one enters:

func _on_body_entered(body: Node) -> void:
	if body is Player:
		print("Player %s" % body.name)
	elif body is Enemy:
		print("Enemy %s" % body.name)

However, a better way to do that would be to have an Area2D that ONLY detects a single layer, to filter out all non-Player objects. So if the Player is on physics layer 2, you set its mask to 2 only, and take it off physics layer 1. This is faster for processing because there’s no if statement, and you can accept only a Player object as your argument, throwing an engine error without any extra coding.

func _on_body_entered(player: Player) -> void:
	print("Player %s" % player.name)

If you accidentally put anything other than a Player on layer 2, you’ll know as soon as it collides with the Area2D.

Can anybody here explain what templates and traits are in simple terms? I have no idea what I’m reading about here.

1 Like

Any source for this you could share? I remember Emi and Remi mentioning at GodotFest25 in November more or less what I mentioned in the last paragraph of my previous post.
Here’s Emi talking about it around 14:10 mark (even specifically mentioning traits, but in the overall context of focusing on stability).
Here’s Remi talking about it around 5:51 and 19:57 mark (mainly mentioning the focus on stability).

Not that it’s not coming at all (it’s on the priorities list afterall…), but that they have a different focus right now, especially that almost any requirement for traits can be covered by a different workaround. So it’s not something that would be a breakthrough in the development of the engine, more like a QoL improvement directed at type-safety coding.

This example doesn’t really show how you can dynamically pass and check the type. I.e. you can’t make your function work with Monster class now without changing its implementation.
Here’s a better example of my snippet that uses Variant as a passed argument of a function to get children of specific type:

Template types I’m not sure specifically, but it looks a lot like generics to me. Generics are a way for you to write type-agnostic functions, where you pass the type as an argument, so you can make it behave differently for different types. Current GDScript equivalent would be already mentioned Variant, that you can pass directly in the function - see my snippet I linked above. If generics were ever to be implemented in GDScript, it would just make this implementation cleaner, more robust, explicit and type-safe. But it’s not something that you can’t do already with some small caveats.

Traits are similar to interfaces and can be easily understood as a system allowing multiple inheritance. Currently, a class in GDScript can extend (inherit from) only one class. Let’s say you have a class Item that extends from class Collectable that extends from Area2D. This works well for your inventory system, because Collectable class has a function collect() that handles being collected into the inventory.
Now imagine you want to implement a Potion item, that shouldn’t only be Collectable, but also Consumable that allows you to consume() an item. You can’t inherit from both of these classes (because GDScript doesn’t let you without traits) and you can’t make all Collectable items be Consumable items, because you don’t want to consume() your Sword. So now your system slightly falls apart and you have to do all sorts of workarounds to make it work.
Traits would allow you to essentially inherit multiple classes to extend the behavior of that class with default implementations of such behaviors.

Hope that explanation was simple enough :slight_smile:

2 Likes

It’s still complicated, but I don’t think the example could get any simpler. So, thank you.

1 Like

You already have Variant / duck typing, that’s as template-y and generic as possible. GDScript cares about performance. Adding generics/templates would bog it down as generics only improve performance in compiled languages.

2 Likes

IIRC, Emi talked about it in Godot Tomorrow #18. I could have remembered what he said about it incorrectly though.

Agreed. You example using is_instance_of() is better. I’ve been trying to be less specific about answers lately and build to specifics if people care. Following @normalized’s playbook.

1 Like

IMO, Templates in C++ are very rarely worth the time and trouble. They are great for certain low-level things, but I believe there is typically a better way architecturally to deal with the same needs in most cases.

1 Like

I just checked and indeed in the Godot Tomorrow #17, around 40:46 mark he mentioned ā€œthey’re trying to make it happen for 4.7ā€. Cool, would be interesting, thanks for that! :slight_smile:

2 Likes

No problem. Sorry I got the wrong one. I watched like 6 in two days so I get them confused.

2 Likes

Thanks for all your replies !

Variant

Indeed, Godot works like C (where void* would be somewhat of an equivalent) and I absolutely agree with the fact that it can be used as such !
I just wanted to know if a more type-safe approach could someday emerge, but I’m completely fine with the variant version.

Traits

That’s such a better idea. I mean, there are pros & cons, but it’s a more modern, composition-oriented approach which, combined with traditional inheritance, is definitely a win.

It is not 100% mandatory of course, as I guess there are ways to still make a modular system without it. I am not yet fully familiar with those methods.

Thank you all for your quick and detailed answers !

2 Likes

IMO, good use of both Inheritance and Composition with Nodes allows you to do pretty much anything you might accomplish with Traits. It’s also allows you to be very strict with type-checking if that’s your thing. (It’s my thing. I turn on warnings for missing strict typing.)

As I’ve learned more about how Godot works, I’ve learned about the power and flexibility of Nodes and Resources. Like most people who come from other languages and have a lot of programming experience, I spent the first year or so trying to optimize things that didn’t matter, by using RefCounted and Object classes instead of Nodes and Resources. At this point, I could probably write a book on all the pitfalls of coming to Godot and GDScript with outside experience.

An example of this is learning curve is the difference between my Camera3D Plugin which I initially architected last April, and my Camera2D Plugin, which I started this January.

Camera3D Plugin

My Camera3D Plugin has two classes, Cameras and CameraMount.

The Cameras node sits as a child of a CharacterBody3D node and handles multiple camera angles for a player. It responds to a change_camera Action that the plugin creates (and deletes if you uninstall it), switching between each Camera3D or CameraMount3D node that you attach to it.

The CameraMount node contains a SpringArm3D, Camera3D, and two Node3D pivot nodes - which are used to prevent Gimbal Lock with Euler Transforms. It handles everything for a default 3D 1st-person or 3rd-person camera. (All input is handled by my Controller Plugin. It supports both mouse/keyboard and gamepad input seamlessly.)


When I created this, I was focusing on inheritance and having as few nodes as possible. I also wanted it to require no work from me to use. I didn’t want to have to recreate camera controls every time I made a new 3D game. I originally intended this plugin to be for 2D and 3D - a result of which is that I forgot to change the addon folder name from dragonforge_camera to dragonforge_camera_3d. (I prepend ā€œdragonforgeā€ at the beginning of all my plugins to prevent namespace collisions - a habit from making Java Maven packages.)

At the time, I basically wanted an object to manage all the cameras - but being against naming things ā€œManagerā€, I called it Cameras - as it is a collection of cameras. Then all the cameras were hung beneath it. Originally, I exported a list of cameras, but I found that was just extra work - and at least for my current uses, overkill. So I changed it to look for Camera3D and CameraMount3D child nodes when it is made ready. (I would now change that to check them entering or leaving the tree instead.)

Camera 2D Plugin

When I started making this plugin, I had a version of a BoundCamera2D from a game I’ve been working on since last year. That camera looked for a signal from a TileMapLayer and bound the camera limits to the used tiles in it. It also handled zooming, panning, and following multiple characters. As I started building the plugin, I realized that I wanted to have multiple camera functions and I ran into an issue of multiple inheritance - a problem that could be solved by traits. But I realized, that Godot already had a solution. Composition through nodes.

When I created my State Machine Plugin, I learned a LOT about how the Godot Node system works. I had taken a Resource-based state machine from a C# tutorial, and decided to remake it in GDScript. As I did, I realized it made a lot more sense to make it Node-based.

It was a lot easier to see and edit in the Godot editor, and it didn’t really take up that much more memory. While it made it a bit more complicated to add and remove states while the game was running, I was able to leverage the Node functionality to do a lot of heavy-lifting. It also allowed me to make a pull state machine (based on the Kanban lean manufacturing and Kanban software development processes).

I learned how to handle nodes entering and leaving the tree, activating and deactivating processing and inputs, and how to wait for parent nodes to be ready. (Nodes are created and enter the tree top-to-bottom, but made ready bottom to top.)

So, I decided to just make all the added functionality of the Camera2D as nodes that could be added to a Camera2D. In this way, if someone wanted to have their own custom Camera2D, they could add functionality by just adding a node.

So now I had a Camera2DLimit node that could take a TileMapLayer as an @export variable, but could also programmatically take a TileMapLayer or Rect2i to do the same thing. I added zoom functionality for mouse, keyboard and gamepad in a Camera2DZoom node. I added pinch zoom support in a separate Camera2DPinchZoom node. I added panning with edge scrolling, keyboard and gamepad, plus touch screen two touch scrolling all in one Camera2DPan node. I even added camera shake, with optional controller vibration as a Camera2DShake node.

While I was at it, I learned how to make warnings appear in the editor, so it was clear that they had to be added to a Camera2D directly. All of my composition nodes inherit from Camera2DComponent, which handles the warnings, and tracks the Camera2D node it is attached to.

image

Ultimately, I used composition over inheritance to allow for inheritance for the Camera2D node at a later time, but I also used inheritance in my composite nodes.

Camera3D Plugin - Changes I Would Make Now

If I were to redo this plugin - which at this point I have no desire to do so (because it works) - I might prefer composition over inheritance more. I would make the CameraMount3D node into two - one for first-person and one for 3rd-person cameras. When I first made this plugin, you couldn’t add custom nodes from the Add Node dialog. I also couldn’t find enough camera icons I liked. Now I’d separate them out because you may not need both.

I would also programmatically create the CameraMount3D node so I didn’t have to store a separate scene, and it could be added through the Add Node dialog. I learned how to do this when making my Curved Terrain 2D Plugin and Curved Text 2D Plugin (which I have not made public yet).

I also might make the Cameras node less reliant on cameras being direct children of it, and instead search for anything attached to the CharacterBody3D node. I might also add in an @export variable again as an optional way to add more cameras. Say you have fixed level cameras like Metal Gear Solid - you might want to use those instead of something attached to the CharacterBody3D.

Alternately, I might consider making it an Autoload, and then let it register all Camera3D nodes as they load in the game.

Conclusion

The Godot way of doing things is much more flexible than people think when they first start using it. The team has done an excellent job of making GDScript more performant than C# in most cases. So using it as-is, is often the best way to reduce memory usage and increase performance - rather than trying to tinker with things underneath. Most of the tasks that are more performant in C# and C++ are math related - but not math related to physics or graphics - in those cases GDScript is often the winner.

Godot’s Node and Resource objects are very performant, and offer a lot of the flexibility people think they need, but don’t know they already have.

3 Likes

Man, your camera3D plugin sounds pretty useful. My game’s camera works fine, but it’s the spring arm that’s wonky. Sometimes, it clips through walls.

I’ll open a question about it eventually.

2 Likes

Take a look at mine. See what you can learn from it. I’m working on an update to my Character 3D Plugin, but you can literally download that and play the project and you’ll get a test scene and character and can rotate through the cameras with the ā€˜C’ key on the keyboard or pressing the right-stick on a gamepad.

2 Likes

This is quite impressive !

I have been used to approaches that are more casual. Godot (and Unity) are both two softwares that I sortof struggle with sometimes. I am used to Roblox Studio. Of course, it provides a set of integrated objets to easily interact with the engine. But it’s way more limiting than Godot.

I have made a StateMachine instance with states as nodes, and transitions as resources. I tackled it that way to get more ease with both nodes & resources with one single project, and some people recommend to make transitions as resources, although I somewhat struggled to understand why.

At uni, we have hardly learnt framework usage. They always want us to reprogram everything, as an academic exercise, but they never introduced us to working with softwares to make entire projects - like Godot.

I really like your post, and I appreciate the fact you took the time to not only write it in an organized fashion, but also provide images and links. A huge thank you for that.

Aside from that, I’m continuing to learn of course ! I still have a basic approach of creating your own classes, instead of taking advantage of all the already-existing nodes. That being said, I think I should continue to see even more design patterns (like behavior trees that uses the decorator DP if I remember well), but also implement it on my own within Godot.

I have an easy time combining nodes for interfaces, … because that’s how to make them. But when it comes to extending existing nodes to customize the behavior, and taking advantage of node composition at the same time, that’s a bit harder to conceptualize.

2 Likes

ā€œBut when it comes to extending existing nodes to customize the behavior, and taking advantage of node composition at the same time, that’s a bit harder to conceptualize.ā€

I tend to blend both. I think with the way Godot was made you really can take whichever approach you want and not have to worry too much about performance on most games.

1 Like

I think that at some point, it’s just about prototyping, trying to find a way to achieve an effect and then, when the desired effect is achieved, thinking about how to make the solution modular and reusable. I tend to skip the first step because I want everything to be perfect directly, which is somewhat really stupid !

1 Like

We won’t really know that until there is at least 1 implementation.
This is still being discussed and it could go north or south.
It would impress if implementation 1 makes it to 4.7. I would have thought 5.0 to be the target.

In one of the recent Godot Tomorrow stream, Emi said they have no plans to do a 5.0. The Godot team follows semantic versioning, so to roll to 5, they would need a change that is not backwards compatible. We could be on 4.x for a long time.

3 Likes