Last active
May 31, 2024 01:55
-
-
Save WolfgangSenff/0a9c1d800db42a9a9441b2d0288ed0fd to your computer and use it in GitHub Desktop.
GDScript 1.0 to 2.0 Beginner Friendly Conversion Guide
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Beginner-friendly GDScript 1 to GDScript 2 Conversion Guide | |
First and foremost, this should not be considered a replacement for the official migration guide, | |
found at https://docs.godotengine.org/en/latest/tutorials/migrating/upgrading_to_godot_4.html! | |
Instead, this document is intended to be a friendly guide to helping you upgrade your projects from | |
GDScript 1 to 2. To do this, I'm going to list the most common conversions I've run into while upgrading | |
a few of my games and tools. This document is written as of the first release candidate version of Godot 4.0. | |
The first thing to do when converting a project (no need if you're starting from new) is to either have your | |
project in source control, or make a duplicate of your entire project's folder and rename it. This will | |
ensure that you can go back if something breaks beyond repair during conversion. | |
The second thing to do is to actually try to import your project into the Godot 4 editor! You can do this | |
by opening the 4.0 executable as normal, then go to the Import button and tapping that, navigating to your | |
project, selecting it and hitting import. This will warn you that the project needs to be upgraded to the | |
latest version - go ahead and proceed with the upgrade; I always suggest doing a full upgrade instead of | |
just project-file-only. When it's done, you can open your project. | |
At this point, your project will have a *ton* of changes, including some things that aren't exactly errors, | |
but may appear to be, and will not be as pretty as they could be - we'll fix those as well. You can treat | |
this as a guide you can use to update the code to modernize it using new GDScript 2 syntax, or a guide that | |
can be used to understand how to start writing new code in GDScript 2! | |
The first and probably largest change you'll notice has to do with signals. Any sufficiently complex system | |
in Godot tends to make heavy use of signals, and the changes to them mdoernize signals to remove the unsafe | |
"stringy" nature of them and turn them into a first class language feature. The converter, however, uses a | |
long-hand form for signal conversion because it's the only way to do it that works in all circumstances | |
without having to make other changes. So, to rectify that, we can close the gap here and make the code a lot | |
cleaner. | |
In all places, you'll generally see signal connections changed to something like this: | |
`_animation_player.connect("animation_finished",Callable(self,"_on_damage_animation_finished").bind(),CONNECT_ONE_SHOT)` | |
The first thing to notice is that it appears to still be using a sort of older form of the code that uses | |
mostly strings rather than the new syntax changes. As previously mentioned, this is because this is | |
_technically_ the only way to ensure that the conversion always works, but I'll explain when and why you | |
might have to do it this way vs a much simpler way. The much simpler way would be to convert the above to | |
the following: | |
`_animation_player.animation_finished.connect(_on_damage_animation_finished, CONNECT_ONE_SHOT)` | |
This is not only much simpler to understand, it utilizes two new elegant changes to GDScript 2.0: signals | |
as first-class entities, and functions as first-class entities. | |
Signals being first-class entities means they can be referenced in code rather than in a string, allowing | |
a signal to be passed around to other functions in a type-safe fashion. This will ensure that signals | |
don't require any kind of weird type-checking to ensure that the signal you're trying to connect to exists | |
on the type of node that you've got: the compiler can tell for you if it exists or not and help you out | |
with an error if it doesn't exist. | |
Functions being first-class entities means they, too, can be passed around to other functions in a type-safe | |
fashion without having to rely on a generic "call" method, nor on FuncRefs. While FuncRefs served for a while | |
as an almost-successful stand-in for lambda functions that so many other languages have had for a long time, | |
the new function as an entity allows us to finally have lambdas as well! The syntax for that is, for example: | |
`func(): _current_value += float(_text.text)`. Do note however that, from experience, converting to a lambda | |
doesn't always make sense, and can lead to code that is hard to maintain - it is often better just to pass an | |
actual function instead. To do that, you can just pass it like I do in the above example with | |
_on_damage_animation_finished - note that there are no quotes; it is referencing the function directly. | |
The reason the project converter ultimately takes a much longer form of this is that when you have a function | |
on a different script that you still want to connect to, or you want to ensure that extra parameters are passed | |
to the methods that get called when the signal is raised (commonly done in GDScript 1.0 by passing the array | |
binds in your connect call, i.e. `connect("signal", self, "function", [])`), the longer form is mostly the only | |
way to handle it universally. Note that, in general, you won't have to do the long form: it seems like the short | |
form works best in most cases. To pass extra values with bindings into the functions that get called when the | |
signal is raised, you can include the `.bind()` syntax on the end of the function name that is being connected, | |
and include any variable values you want to be included in the parentheses. | |
One thing you'll notice is a quick rename, as well: if you've ever connected to a signal in 3.x and had to do a | |
oneshot connection, you'll remember at the end of the connect call, you'd include a CONNECT_ONESHOT enum value | |
passed in. In GDScript 2.0, there's a large number of updated API names to modernize and simplify (even if it | |
makes them longer) the names for beginners. Quite a few of the changes are documented here | |
https://gist.github.com/WolfgangSenff/168cb0cbd486c8c9cd507f232165b976. You can search this other document easily | |
in your browser, so if you get an error in Godot 4.0 that isn't clear how to fix, take the code word that may be | |
the error and search for it in this gist. If you don't find it, you can post a question about it on the gist and | |
I'll try to find it for you! Here are a few common examples so you can see the types of changes: | |
CONNECT_ONESHOT -> CONNECT_ONE_SHOT | |
change_scene -> change_scene_to_file | |
change_scene_to -> change_scene_to_packed | |
update -> queue_redraw | |
TileMap's world_to_map -> local_to_map and vice versa | |
deg2rad -> deg_to_rad and vice versa | |
PoolVector2Array -> PackedVector2Array - this is true for all PoolXArray versions, they all change to PackedXArray | |
File -> FileAccess (some functions formerly of Directory have been changed to FileAccess) | |
Directory -> DirAccess | |
instance -> intantiate | |
idle_frame (when yielding) -> process_frame (when awaiting) | |
A very common node that has been renamed and comes with appropriate API updates is KinematicBody2D (and 3D): | |
it has now been updated to CharacterBody2D, and one of the changes that's going to propagate throughout your | |
GDScript code is this: instead of having a bunch of unclear parameters passed to `move_and_slide` or | |
`move_and_collide`, you now set a lot of those parameters ahead of time and just call `move_and_slide`. An | |
example of this might be: | |
``` | |
func _physics_process(delta: float) -> void: | |
velocity += Vector2.DOWN * delta * GRAVITY # velocity is a new property included on CharacterBody2D automatically | |
move_and_slide() | |
``` | |
Note you no longer need to include a velocity property in your script at all - it is included automatically | |
as a matter of course. | |
Shaders have had a number of renames and changes. See the bottom of the document here for a list of common | |
ones: https://gist.github.com/WolfgangSenff/168cb0cbd486c8c9cd507f232165b976 | |
The next biggest set of changes is likely going to be with your exported variables. These are fairly well-defined | |
and the converter does a great job of converting them, but if you're working on a new project, the following | |
should help you understand the differences between 1.0 and 2.0. | |
First, most "annotations" such as "export" and "onready" have been changed to have an at symbol (@) before the | |
annotation name in order to make it clearer what their purpose is. It also makes things simpler internally for | |
Godot, but that's not the point of this document. An example of what this conversion may look like could be: | |
3.x: | |
`onready var _anim = $AnimationPlayer` | |
4.0: | |
`@onready var _anim = $AnimationPlayer` | |
Note it also adds the ability to have a newline after the annotations if you like. For instance: | |
``` | |
@onready | |
var HPValue | |
``` | |
This will function as if it was all on the exact same line. | |
A similar change can be seen with @export. However, with the @export annotation, there are a number | |
of new annotations to keep in mind. An example of them is: | |
@export | |
@export_range | |
@export_enum | |
@export_node_path | |
See others here: https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_exports.html | |
Finally on the topic of annotations, one change that I've personally been having to update a lot is | |
my set of tool scripts. Tool scripts have `tool` at the top of them in 3.x, but in 4.0, it's simply changed | |
to @tool. | |
Properties (commonly known as values with setters and getters) have been upgraded as well. Previously, you | |
defined a setter and a getter like this in 3.x: | |
`var property setget set_property, get_property # not pretty if you want just a getter and no setter: var property setget , get_property - yikes!` | |
Then, later on in the script, you'd define functions for set_property and get_property. Now, the setters | |
and getters have a much cleaner syntax and are defined much closer to the definition of the property, which | |
is nice, as previously they could easily get lost in among the other functions of the script. Now, properties | |
with setters and getters look like: | |
var hp_value: int: | |
set(value): | |
hp_value = value | |
hpvalue_changed.emit(hp_value) # (example of how to emit a signal now that they are first-class entities) | |
get: | |
return hp_value | |
Note that `get` here does not have a parameter, and elides the functional parentheses as such. | |
Finally for this document, the changes you're most likely to need next have to do with yielding. In | |
previous versions of Godot, yield was built on top of the concept of coroutines, or the idea that a | |
function can be placed into an executable set of steps that can be stopped and resumed at will, returning | |
the function automatically to the parent to manage the executing/paused state. This is an extremely powerful | |
concept, but added a lot of syntactical idiosyncracies in the code that were sometimes hard to follow. An | |
example of this in 3.x is when you want to yield a function so that it only gives you control back after | |
all yields used in that function were completed. For beginners, most didn't even know you could do this, | |
and for advanced users, it wasn't clear how it worked, just that it did. Coroutines are what made that happen, | |
utilizing a return type called GDScriptFunctionState. You could then do something like: | |
`yield(function_to_execute(), "completed")` | |
The problem with this is obvious: if the script which is running function_to_execute does not contain a | |
"completed" signal, where on earth did it come from?! It is actually coming from the GDScriptFunctionState | |
return type listed above! Which, if we're being honest, was not clear at all. | |
With 4.0, we get a much cleaner, clearer syntax for how these things work, with no extra trappings of | |
mysterious completed signals or anything like that. In fact, awaiting signals or functions is effectively | |
the same thing now with absolutely no extraneous cruft required to be memorized. An example of the above | |
using the new await syntax is: | |
`await function_to_execute()` | |
And that's it! No longer do you have to await for a seemingly-random "completed" signal coming from | |
who-knows-where. It's really nice. | |
Note that the autoconverter will often get this wrong, and instead change it to: | |
`await function_to_execute().completed` | |
You can just delete the completed signal off the end of it and get the exact same functionality | |
you had as before. | |
To await a signal, it's similar to the problematic code above (you can see why the converter | |
struggles with this): | |
`await $AnimationPlayer.animation_finished` | |
Edit: Adding one last thing that gets a lot of people. Directory was replaced with DirAccess, and File functions are mostly FileAccess now. See the more advanced guide here for more (search DirAccess): https://gist.github.com/WolfgangSenff/168cb0cbd486c8c9cd507f232165b976 | |
Okay, that's all for now! If you have any questions about conversions, feel free to drop me a | |
line here down below, on Twitter (@KyleTheCoder), Mastodon (https://mastodon.social/@BackAt50Ft), | |
or wherever you see me on Discord (typically @BackAt50Ft)! | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment