# Godot 4.7 GDScript 2.0

Pinned to Godot 4.7.2 stable (18 August 2026), GDScript 2.0. Write only Godot 4 code. The list at the bottom is banned even if an old tutorial or an older model suggests it.

## Scenes and scripts
- Do not delete `.godot/`, `project.godot`, `.import`, or `.uid` files. Do not hand-edit `.import` files.
- Put gameplay code on the node that owns the behavior. Use `@onready` to cache child nodes. Do not call `get_node()` every frame.
- `@export` is for designer-facing values only. Give it a type and a default.

```gdscript
extends CharacterBody2D

@export var speed: float = 200.0
@onready var sprite: Sprite2D = $Sprite2D

func _physics_process(_delta: float) -> void:
    var input := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = input * speed
    move_and_slide()
```

## Movement
- Bodies are `CharacterBody2D` and `CharacterBody3D`. They own a `velocity` property (`Vector2` or `Vector3`).
- Set `velocity`, then call `move_and_slide()` with no arguments. Do not pass the velocity, an up vector, or a delta into `move_and_slide`.
- Do not multiply horizontal `velocity` by `delta`. `move_and_slide()` applies the timestep. Gravity is the exception: `velocity.y += gravity * delta` before the call.
- Read input with `Input.get_vector` or `Input.is_action_pressed` using actions from the Input Map. Do not hardcode key scancodes in gameplay code.
- Run movement in `_physics_process`, not `_process`.

## Signals and loading
- Connect with a Callable: `button.pressed.connect(_on_pressed)`. Disconnect with `disconnect`. Emit with `hit.emit(amount)`, not `emit_signal("hit", amount)`.
- Declare a signal as `signal hit(amount: int)`.
- Load a scene with `preload("res://player.tscn")` when the path is fixed, and instance it with `instantiate()`, not `instance()`.
- Change scene with `get_tree().change_scene_to_file("res://main.tscn")`.
- Read and write files with `FileAccess.open`. List directories with `DirAccess`. Both are static. There is no `File.new()`.

## Never write these (Godot 3, or removed)

| Do not write | Write instead |
| --- | --- |
| `KinematicBody2D`, `KinematicBody` | `CharacterBody2D`, `CharacterBody3D` |
| `yield(...)`, `yield(self, "completed")` | `await` |
| `export var`, `export(int)`, `onready var` | `@export var`, `@onready var` |
| `move_and_slide(velocity)` | set `velocity`, then `move_and_slide()` |
| `connect("pressed", self, "_on_pressed")` | `pressed.connect(_on_pressed)` |
| `emit_signal("hit", n)` | `hit.emit(n)` |
| `packed.instance()` | `packed.instantiate()` |
| `File.new()`, `Directory.new()` | `FileAccess`, `DirAccess` |
| `Spatial`, `SpatialMaterial` | `Node3D`, `StandardMaterial3D` |
| `rand_range(a, b)` | `randf_range(a, b)` |
| `PoolStringArray`, `PoolIntArray` | `PackedStringArray`, `PackedInt32Array` |
| `change_scene("res://x.tscn")` | `change_scene_to_file("res://x.tscn")` |
| `OS.get_window_size()`, `OS.window_fullscreen` | `DisplayServer` window methods |

If you are about to type any name in the left column, stop and use the right column.

## Typing and timing
- Use static types on variables, parameters, and return values (`var hp: int = 10`, `func hit(n: int) -> void`). Use `:=` only when the type is obvious from the right side.
- Wait for a timer with `await get_tree().create_timer(1.0).timeout`.
- Animate with `var tween := create_tween()`. Do not create a `Tween` node with `Tween.new()`.
- Mark editor scripts with `@tool` on the first line, and check `Engine.is_editor_hint()` inside them.
- Property accessors use the GDScript 2.0 `get:` / `set(value):` blocks, not `setget`.

## Done checklist
1. The Godot editor shows no parser errors on the edited scripts.
2. Nothing from the ban list appears in the diff.
3. You said whether the scene was actually run.
