How to use AI to build a Godot 4.5+ feature, step by step
AI coding tools can write GDScript quickly. The catch is that years of tutorials, forum answers and sample code online were written for Godot 3, and the language changed a lot in Godot 4. So a common thing to get back is code that reads fine and won't even load: yield instead of await, export instead of @export, KinematicBody2D instead of CharacterBody2D.
This guide builds one small feature, a dash move with a cooldown, the way we'd do it with an AI tool open next to the editor: ask for one piece, check it against the Godot 4 docs, let Godot itself check the syntax, then test it before you even open the editor.
Not affiliated with or endorsed by the Godot Foundation or the Godot Engine project. If this guide and the Godot docs ever disagree, trust the docs.
Which Godot version?
Godot 4.5 is a stable release, and its latest patch is 4.5.2. It isn't the newest, though: the most recent stable release when we checked was 4.7.2. Everything in this guide uses plain Godot 4 GDScript, and we checked and tested the code on both 4.5.2 and 4.7.2.
Two things Godot 4.5 added to GDScript are worth knowing, because AI tools trained before it may not know about them: functions can take a variable number of arguments (...numbers: Array), and classes and methods can be marked @abstract. You don't need either one here.
What you'll build
A 2D player that walks with the arrow keys and dashes when you press Enter or Space. The dash lasts a fraction of a second, then there's a cooldown before you can dash again, and the player emits a dashed signal so other parts of the game (sound, particles, a screen shake) can react.
It's small on purpose. It has a node type, exported settings, input, a signal and a timer, which are the same spots where Godot 3 habits sneak in.
You need:
- Godot 4.5 or later, free from godotengine.org. The standard build is fine. You don't need the .NET one for GDScript.
- Your usual AI coding tool. Our game dev AI and AI coding assistants lists are a place to start.
Step 1: Tell the AI which Godot you're on
Name the version and the rules before it writes anything:
I'm using Godot 4.5 (GDScript, not C#). Godot 4 syntax only, no Godot 3.
Write player.gd for a CharacterBody2D:
- walk with Input.get_vector on ui_left/ui_right/ui_up/ui_down
- dash on ui_accept: short burst of speed, then a cooldown
- @export walk_speed, dash_speed, dash_time, dash_cooldown
- emit a `dashed(direction: Vector2)` signal when a dash starts
- use static types
- try_dash(direction) returns true/false right away, so I can test it
List every built-in class and method you used.
"Godot 4 syntax only" and the version number do most of the work. Asking for a list of what it used gives you a short checklist for Step 3. (We haven't scored AI runs with this prompt. It's a starting point, not a tested recipe.)
Step 2: Spot Godot 3 code at a glance
Here's the kind of answer you might get if the AI falls back on older examples:
# DON'T: Godot 3 syntax an AI may hand you. Godot 4 rejects it.
extends KinematicBody2D
export var speed = 200
var velocity = Vector2.ZERO
func _physics_process(_delta):
velocity = move_and_slide(velocity)
func dash():
yield(get_tree().create_timer(0.2), "timeout")
Each Godot 3 habit in that snippet fails in Godot 4. We ran each one through Godot 4.7.2's checker, and these are the errors it printed:
| Godot 3 | Godot 4 | Godot 4.7.2 says |
|---|---|---|
export var speed = 200 | @export var speed := 200.0 | The "export" keyword was removed in Godot 4. Use an export annotation ("@export", "@export_range", etc.) instead. |
extends KinematicBody2D | extends CharacterBody2D | Could not find base class "KinematicBody2D". |
yield(timer, "timeout") | await timer.timeout | "yield" was removed in Godot 4. Use "await" instead. |
velocity = move_and_slide(velocity) | set velocity, then call move_and_slide() | Too many arguments for "move_and_slide()" call. Expected at most 0 but received 1. |
Sources: the upgrading to Godot 4 guide lists the renamed nodes (KinematicBody2D became CharacterBody2D), the GDScript reference covers await and @export, and the CharacterBody2D page shows that velocity is now a property of the node.
A few other Godot 3 leftovers to watch for: onready var (now @onready), connect("signal", self, "method") (now signal.connect(method)), and tool at the top of a file (now @tool).
Step 3: The Godot 4 version
# player.gd: a CharacterBody2D that walks and dashes, with a cooldown.
class_name Player
extends CharacterBody2D
signal dashed(direction: Vector2)
@export var walk_speed: float = 200.0
@export var dash_speed: float = 600.0
@export var dash_time: float = 0.15
@export var dash_cooldown: float = 0.8
var _dashing := false
var _can_dash := true
var _dash_direction := Vector2.ZERO
func _physics_process(_delta: float) -> void:
if _dashing:
velocity = _dash_direction * dash_speed
else:
var input := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input * walk_speed
if Input.is_action_just_pressed("ui_accept"):
try_dash(input)
move_and_slide()
## Starts a dash in [param direction]. Returns false if on cooldown or no direction.
func try_dash(direction: Vector2) -> bool:
if not _can_dash or direction == Vector2.ZERO:
return false
_can_dash = false
_dashing = true
_dash_direction = direction.normalized()
dashed.emit(_dash_direction)
_finish_dash() # runs on its own; we don't wait for it here
return true
func _finish_dash() -> void:
await get_tree().create_timer(dash_time).timeout
_dashing = false
await get_tree().create_timer(dash_cooldown).timeout
_can_dash = true
What to check against the docs:
extends CharacterBody2Dand the built-invelocityproperty, thenmove_and_slide()with no arguments (CharacterBody2D).Input.get_vectortakes four action names and returns aVector2(Input). Theui_*actions exist in every new project.get_tree().create_timer(seconds)returns aSceneTreeTimer, andawait ... .timeoutpauses until it fires (SceneTree).signal dashed(direction: Vector2)anddashed.emit(...)are the Godot 4 way to declare and fire a signal.try_dashdoesn'tawait. It decides right away and hands the waiting to_finish_dash(). If youawaitinside a function, the function becomes a coroutine, and callers that expect an answer straight away get a surprise. AI tools mix these up often, so it's worth asking "does this function return before or after the timers?"
Step 4: Let Godot check the syntax
You don't need to open the editor to find out whether a script parses. Godot's command line has --headless (no window) and --check-only ("only parse for errors and quit", used with --script). From the project folder:
godot --headless --import
godot --headless --check-only --script res://player.gd
The first line builds the project's cache, so class_name Player gets registered. The second exits with code 0 if the script parses, and prints a SCRIPT ERROR line if it doesn't. Paste that error back to your AI tool, together with the line number. It works much better than "it doesn't work".
Step 5: Test it without playing it
The dash logic can be tested from a script that runs headless too. This one extends SceneTree, adds a player to the tree, and checks each rule:
# test_dash.gd: run with godot --headless --script res://test_dash.gd
extends SceneTree
var _passed := 0
var _total := 0
var _signal_count := 0
func check(name: String, ok: bool) -> void:
_total += 1
if ok:
_passed += 1
print(("PASS " if ok else "FAIL ") + name)
func _initialize() -> void:
_run.call_deferred()
func _run() -> void:
var player := Player.new()
player.dash_time = 0.1
player.dash_cooldown = 0.2
root.add_child(player)
player.dashed.connect(func(_d: Vector2) -> void: _signal_count += 1)
check("no dash without a direction", player._can_dash and not player._dashing)
check("zero direction ignored", player.try_dash(Vector2.ZERO) == false and _signal_count == 0)
check("try_dash returns true", player.try_dash(Vector2(3, 4)) == true)
check("dash starts", player._dashing and not player._can_dash)
check("dash direction normalized", player._dash_direction.is_equal_approx(Vector2(0.6, 0.8)))
check("dashed signal emitted once", _signal_count == 1)
check("second dash blocked during cooldown", player.try_dash(Vector2.RIGHT) == false and _signal_count == 1)
await create_timer(0.15).timeout
check("dash ends after dash_time", not player._dashing and not player._can_dash)
await create_timer(0.25).timeout
check("can dash again after cooldown", player._can_dash)
player.try_dash(Vector2.LEFT)
check("second dash works", _signal_count == 2 and player._dash_direction == Vector2.LEFT)
await create_timer(0.35).timeout # let the last cooldown finish
player.free()
print("%d/%d passed" % [_passed, _total])
quit(0 if _passed == _total else 1)
godot --headless --script res://test_dash.gd
Ours prints 10/10 passed on both Godot 4.5.2 and 4.7.2. It shortens the dash and cooldown so the test finishes in about a second. It doesn't test the arrow keys or how the dash feels. You still have to play it for that.
Then add the player to a scene: make a CharacterBody2D scene, attach player.gd, and give it a CollisionShape2D and a sprite, so you can see it and it collides with things. Run the scene, walk, dash, and try to break it by mashing dash, dashing into walls and dashing while standing still.
Common AI mistakes in Godot 4 code
| What the AI did | Why it's a problem | What to ask for instead |
|---|---|---|
yield, export, onready, tool | Godot 3 syntax, a parse error in 4 | "Godot 4.5 syntax only" |
KinematicBody2D, Spatial, other old node names | Class not found | Check the upgrading guide's rename table |
move_and_slide(velocity, Vector2.UP) | Godot 4 takes no arguments | "Set velocity and up_direction, then call move_and_slide()" |
connect("pressed", self, "_on_pressed") | Old string-based connect | "Use signal.connect(callable)" |
await inside a function that should answer straight away | Callers get the answer late | "Return first, then wait in a helper" |
| Untyped variables everywhere | Fewer errors caught early | "Use static types" |
| Invents a method that sounds right | Error at runtime | Look up the class page and ask for the list it used |
Next step
- Browse AI tools made for game development on our game dev AI page.
- Before you pick a coding tool, read our checklist for choosing an AI tool. It's written for FiveM, but the same questions apply to Godot.
Not affiliated with or endorsed by the Godot Foundation or the Godot Engine project. If this guide and the Godot docs ever disagree, trust the docs.