ziva-sh/godot-4-api-pitfalls

Corrige les API Godot 3 et la syntaxe GDScript que les assistants IA écrivent dans les projets Godot 4, comme KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, string connect(), instance(), rand_range, le nœud Tween, File, change_scene, Spatial, les propriétés rect_*, Pool*Array, les fonctions OS temporelles et l'ancienne API TileMap. À utiliser lors de l’écriture, de la relecture ou du portage de GDScript pour Godot 4.x, lorsqu’un script échoue avec « not declared », « not found in base », « was removed in Godot 4 » ou « is not present ».

Qu'est-ce que godot-4-api-pitfalls ?

Cette compétence résout un problème fréquent dans le développement Godot 4 : la plupart des tutoriels, réponses de forum et données d’entraînement des modèles d’IA sont rédigés pour Godot 3. Elle fournit une table de réécriture complète qui mappe chaque classe, méthode, propriété et syntaxe GDScript de Godot 3 vers son équivalent Godot 4. Elle explique pourquoi certains codes Godot 3 échouent à l’analyse syntaxique, d’autres seulement à l’exécution (même avec --check-only). La compétence propose des exemples avant/après pour les contrôleurs de personnage, les propriétés/signaux/timers/tweens, et les fichiers/temps/randomisation/scènes. Elle décrit également les changements de comportement qu’aucun analyseur ne détecte (comme _ready() n’appelant plus implicitement la classe parente, ou zoom inversé). Des commandes de vérification par grep et godot --check-only sont incluses, avec un fichier override.cfg temporaire pour forcer les erreurs sur les appels non typés. Utile pour tout développeur portant un projet Godot 3 ou écrivant du nouveau code GDScript sous Godot 4.x.

Compatible avec~Claude Code~Codex CLI~Cursor
Part ofMobile
npx skills add https://github.com/ziva-sh/godot-skills/tree/HEAD/skills/godot-4-api-pitfalls

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Pièges des API Godot 4

La plupart du GDScript dans les tutoriels, réponses de forums et données d’entraînement des modèles est en Godot 3. Godot 4 a renommé des classes, méthodes et propriétés et modifié la syntaxe GDScript. Certains codes Godot 3 ne parviennent pas à être analysés, d’autres s’analysent mais échouent seulement à l’exécution. Avant d’écrire du code, confirmez la version : project.godot contient une ligne comme config/features=PackedStringArray("4.7", ...).

Table de réécriture

Godot 3 (incorrect en Godot 4)Godot 4
KinematicBody2D, KinematicBodyCharacterBody2D, CharacterBody3D
velocity = move_and_slide(velocity, Vector2.UP)Définissez la propriété velocity, puis appelez move_and_slide() sans arguments. up_direction et floor_snap_length sont des propriétés
export var x = 1, export(int, 0, 10) var x@export var x := 1, @export_range(0, 10) var x: int
export(String, FILE, "*.json") var path@export_file("*.json") var path: String
export(Array, int) var a@export var a: Array[int]
onready var, tool@onready var, @tool
var hp setget set_hp, get_hpvar hp: int: set = set_hp, get = get_hp, ou un bloc set(value): en ligne
yield(obj, "signal_name")await obj.signal_name
yield(get_tree().create_timer(1.0), "timeout")await get_tree().create_timer(1.0).timeout
yield(get_tree(), "idle_frame")await get_tree().process_frame
connect("pressed", self, "_on_pressed", [arg])pressed.connect(_on_pressed.bind(arg))
emit_signal("died")died.emit() (la forme en chaîne fonctionne encore mais les fautes de frappe ne sont détectées qu’à l’exécution)
scene.instance()scene.instantiate()
get_tree().change_scene(path), change_scene_to(packed)change_scene_to_file(path), change_scene_to_packed(packed)
rand_range(a, b)randf_range(a, b) ou randi_range(a, b)
stepify, deg2rad, rad2deg, range_lerp, str2varsnapped, deg_to_rad, rad_to_deg, remap, str_to_var
Nœud Tween : $Tween.interpolate_property(...), start()var tween := create_tween(), puis tween.tween_property(node, "property", final_value, duration)
File.new(), file.open(path, File.READ)FileAccess.open(path, FileAccess.READ) (retourne null en cas d’échec, voir FileAccess.get_open_error())
Directory.new()DirAccess.open(path)
to_json(v), parse_json(s)JSON.stringify(v), JSON.parse_string(s)
OS.get_ticks_msec(), OS.get_unix_time(), OS.get_datetime()Time.get_ticks_msec(), Time.get_unix_time_from_system(), Time.get_datetime_dict_from_system()
OS.window_fullscreen = true, OS.get_screen_size()DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN), DisplayServer.screen_get_size()
OS.set_window_title("Game")get_window().title = "Game"
Spatial, translationNode3D, position
extends Reference, Transform, Quatextends RefCounted, Transform3D, Quaternion
pause_mode = PAUSE_MODE_PROCESSprocess_mode = PROCESS_MODE_ALWAYS
Sprite, AnimatedSprite, Position2D, Particles2D, TextureProgressSprite2D, AnimatedSprite2D, Marker2D, GPUParticles2D, TextureProgressBar
rect_position, rect_size, rect_min_sizeposition, size, custom_minimum_size
margin_left (Control)offset_left
Label.align = Label.ALIGN_CENTERhorizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
add_color_override, add_font_override, add_stylebox_override, add_constant_overrideadd_theme_color_override, add_theme_font_override, add_theme_stylebox_override, add_theme_constant_override
Color.red, Color.whiteColor.RED, Color.WHITE
PoolStringArray, PoolVector2Array, PoolIntArray, PoolRealArrayPackedStringArray, PackedVector2Array, PackedInt32Array, PackedFloat32Array
update() (redessiner un CanvasItem)queue_redraw()
TileMap.set_cellv(pos, id), world_to_mapTileMapLayer.set_cell(coords, source_id, atlas_coords), local_to_map (voir la compétence godot-tilemaplayer)
BUTTON_LEFT, BUTTON_WHEEL_UPMOUSE_BUTTON_LEFT, MOUSE_BUTTON_WHEEL_UP
event.scancode, event.controlevent.keycode (ou physical_keycode), event.ctrl_pressed
array.empty(), array.remove(i), array.invert()is_empty(), remove_at(i), reverse()
set_collision_layer_bit(0, true) (bit basé sur 0)set_collision_layer_value(1, true) (numéro de couche basé sur 1)
space_state.intersect_ray(from, to)space_state.intersect_ray(PhysicsRayQueryParameters2D.create(from, to))
RayCast2D.cast_totarget_position
get_tree().set_input_as_handled()get_viewport().set_input_as_handled()
remote func, remotesync func, puppet func, is_network_master()@rpc("any_peer") func, @rpc("any_peer", "call_local") func, @rpc func (seule l’autorité peut l’appeler), is_multiplayer_authority(). master func n’a pas d’équivalent direct : utilisez @rpc("any_peer") et vérifiez multiplayer.get_remote_sender_id()
Engine.editor_hint, filenameEngine.is_editor_hint(), scene_file_path
RectangleShape2D.extents (demi-taille)size (taille complète, donc doublez l’ancienne valeur)
ResourceSaver.save(path, resource)ResourceSaver.save(resource, path)

Les renommages moins courants (3D, XR, navigation, shaders, threads) sont dans references/renames.md.

Avant et après

Contrôleur de personnage :

extends KinematicBody2D

export var speed = 200
var velocity = Vector2()

func _physics_process(delta):
	velocity.x = Input.get_axis("ui_left", "ui_right") * speed
	velocity = move_and_slide(velocity, Vector2.UP)
extends CharacterBody2D

@export var speed := 200.0
@export var jump_velocity := -400.0


func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity += get_gravity() * delta
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = jump_velocity
	velocity.x = Input.get_axis("ui_left", "ui_right") * speed
	move_and_slide()

Propriétés, signaux, timers et tweens :

tool
extends Node2D

export(int, 0, 100) var health = 100 setget set_health
onready var button = $Button

func _ready():
	button.connect("pressed", self, "_on_button_pressed")

func set_health(value):
	health = clamp(value, 0, 100)
	update()

func _on_button_pressed():
	$Tween.interpolate_property(self, "modulate:a", 1.0, 0.0, 0.5)
	$Tween.start()
	yield($Tween, "tween_all_completed")
	queue_free()
@tool
extends Node2D

@export_range(0, 100) var health: int = 100:
	set(value):
		health = clampi(value, 0, 100)
		queue_redraw()

@onready var button: Button = $Button


func _ready() -> void:
	button.pressed.connect(_on_button_pressed)


func _on_button_pressed() -> void:
	var tween := create_tween()
	tween.tween_property(self, "modulate:a", 0.0, 0.5)
	await tween.finished
	queue_free()

Fichiers, temps, aléatoire et scènes :

extends Node

func save_score(score):
	var file = File.new()
	file.open("user://save.json", File.WRITE)
	file.store_string(to_json({"score": score, "time": OS.get_unix_time()}))
	file.close()

func spawn(scene):
	var enemy = scene.instance()
	enemy.position = Vector2(rand_range(0, 100), 0)
	add_child(enemy)
extends Node

@export var enemy_scene: PackedScene


func save_score(score: int) -> void:
	var file := FileAccess.open("user://save.json", FileAccess.WRITE)
	if file == null:
		push_error("Impossible d'ouvrir le fichier de sauvegarde : %s" % error_string(FileAccess.get_open_error()))
		return
	file.store_string(JSON.stringify({"score": score, "time": Time.get_unix_time_from_system()}))


func spawn() -> void:
	var enemy := enemy_scene.instantiate() as Node2D
	enemy.position = Vector2(randf_range(0.0, 100.0), 0.0)
	add_child(enemy)


func go_to_menu() -> void:
	get_tree().change_scene_to_file("res://menu.tscn")

Ce que l’analyseur ne détecte pas

godot --check-only signale un membre inconnu comme une erreur uniquement lorsque le récepteur est self, un type intégré (Array, Vector2, Color) ou un nom de classe appelé statiquement (OS.get_unix_time()) ; les mauvais nombres et types d’arguments sont également des erreurs. Un membre manquant sur un objet, même typé comme var shape := RectangleShape2D.new(), est traité comme « peut exister sur un sous-type » et s’analyse avec les paramètres par défaut. Ces lignes Godot 3 passent toutes le --check-only sur Godot 4.7.2 et échouent uniquement lors de l’exécution :

  • scene.instance() sur un PackedScene, get_tree().change_scene(path), get_tree().set_input_as_handled()
  • shape.extents sur un RectangleShape2D, $Panel.rect_size
  • event.scancode ou event.control sur un InputEvent

Pour que l’analyseur les rejette lors d’une vérification, placez ces lignes dans un override.cfg temporaire à côté de project.godot (Godot le lit par-dessus project.godot, y compris --check-only) et supprimez-le ensuite ; si le projet a déjà un override.cfg, ajoutez-le et restaurez-le à la place. 2 signifie Erreur :

[debug]
gdscript/warnings/unsafe_method_access=2
gdscript/warnings/unsafe_property_access=2

Ne laissez pas ces lignes dans le project.godot de l’utilisateur sauf demande : en mode Erreur, tout appel non typé comme body.take_damage(1) ou $Sprite.play() est une erreur d’analyse, et le jeu démarre toujours avec le code de sortie 0, affichant seulement Failed to load script. Le code typé passe, donc préférez les références typées : @onready var panel: Panel = $Panel, et var key := event as InputEventKey avant de lire key.keycode.

Changements de comportement qu’aucun analyseur ne détecte :

  • Les méthodes de cycle de vie comme _ready() n’appellent plus implicitement l’implémentation de la classe parente. Appelez super() lorsqu’un script hérité en a besoin.
  • Array.slice(begin, end) exclut end.
  • Camera2D.zoom est inversé : des valeurs plus grandes zooment.
  • String.right(n) renvoie les n derniers caractères.
  • randomize() s’exécute automatiquement au démarrage. Utilisez seed() pour un aléatoire reproductible.
  • call_group() est immédiat. Utilisez call_group_flags(SceneTree.GROUP_CALL_DEFERRED, ...) pour l’ancien comportement différé.
  • AnimatedSprite2D.playing a été supprimé. Appelez play().
  • TileMap existe toujours mais est déprécié. Le nouveau code utilise un nœud TileMapLayer par couche.

Auto-vérification

  1. Recherchez dans les scripts que vous avez écrits ou modifiés les noms Godot 3 (GNU grep ; ajoutez -P ou adaptez pour d’autres greps) :

    grep -rnE --include='*.gd' \
      -e '\b(KinematicBody2D|KinematicBody|Spatial|Position2D|Particles2D|TextureProgress)\b' \
      -e '\b(yield|setget|rand_range|stepify|deg2rad|rad2deg|range_lerp|to_json|parse_json|funcref)\b' \
      -e '\b(idle_frame|pause_mode|Reference|Quat)\b|\bPAUSE_MODE_' \
      -e '^(export|onready|tool)\b|^(remote|master|puppet)(sync)? func' \
      -e '\.instance\(|change_scene(_to)?\(|\b(File|Directory)\.new\(|OS\.(get_ticks|get_unix_time|get_datetime|window_)' \
      -e '\brect_[a-z_]+\b|(^|[^"])\bmargin_(left|right|top|bottom)\b|add_(color|font|stylebox|constant|icon)_override' \
      -e '\bPool[A-Za-z0-9]+Array\b|\bBUTTON_(LEFT|RIGHT|MIDDLE|WHEEL)|\.scancode\b|set_cellv|world_to_map|map_to_world' \
      -e 'connect\("[^"]+", *self|interpolate_property|\bextends +Sprite *$' .
    

    Réécrivez chaque correspondance avec le tableau ci-dessus. Les constantes de thème "margin_left" entre guillemets de MarginContainer sont des noms Godot 4 et ne sont pas détectées. L’absence de correspondance ne prouve pas que le code est Godot 4 : la liste est le sous-ensemble commun.

  2. Analysez chaque script modifié depuis la racine du projet : godot --headless --path . --check-only --script res://path/to/script.gd. Lisez la sortie, pas seulement le code de sortie. Avec le override.cfg temporaire ci-dessus, les membres renommés sur les objets typés échouent également ici.

  3. Exécutez la scène qui utilise le script et lisez sa sortie, car les erreurs d’exécution ne changent pas le code de sortie. La compétence godot-verify contient les commandes.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ziva-sh/godot-control-layout

Builds Godot 4 Control UIs that resize instead of clipping or overlapping - containers versus anchors, anchor presets from code, size flags, custom_minimum_size, MarginContainer margins, Label wrapping, mouse_filter, CanvasLayer HUDs, Godot 4 theme override names (add_theme_color_override and friends) and the display/window/stretch settings for multiple resolutions. Use when creating or fixing menus, HUDs, dialogs and inventory screens, when UI elements are cut off, zero-sized, overlapping, igno

ziva-sh/godot-scene-organization

Structures Godot 4 projects so scenes stay reusable and references do not break - when to make a scene, a script or a Resource, instancing with @export PackedScene, preload and load, @onready and %UniqueName node access, _ready order, groups, autoloads, class_name, and resource UIDs with the .uid files added in Godot 4.4, including how to move or rename files safely. Use when creating new scenes or scripts, deciding how nodes find each other, adding global state or an event bus, moving or renami

ziva-sh/godot-signals

Godot 4 signal patterns and their failure modes - declaring typed signals, emit(), connect() and disconnect() with Callables, bind() and unbind(), lambdas, CONNECT_ONE_SHOT and CONNECT_DEFERRED, await on signals and timers, editor-made connections stored in .tscn files, and the call down, signal up rule. Use when wiring UI buttons, timers, Area2D or body events and custom game events, when replacing Godot 3 connect("signal", self, "method") or yield code, or when debugging "already connected", "

ziva-sh/godot-tilemaplayer

Correct Godot 4.3+ TileMapLayer code - set_cell(coords, source_id, atlas_coords), reading cells and custom data, erasing, coordinate conversion, terrains, tile collision through TileSet physics layers, finding the tile a body hit, and migrating from the deprecated TileMap node and its layer-index API. Use when placing, reading or erasing tiles from GDScript, building a TileSet in code, generating levels procedurally, handling tile collisions, or porting TileMap code from Godot 3 or Godot 4.0 to

Skills associés