CommunityProgramación y desarrollogithub.com

ziva-sh/godot-4-api-pitfalls

Corrige APIs de Godot 3 y sintaxis GDScript que los asistentes de IA escriben en proyectos de Godot 4, como KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, connect() con cadenas, instance(), rand_range, el nodo Tween, File, change_scene, Spatial, propiedades rect_*, Pool*Array, funciones de tiempo de OS y la antigua API de TileMap. Úsalo al escribir, revisar o portar GDScript para Godot 4.x, cuando un script falla con "not declared", "not found in base", "was removed in Godot 4" o "is not present".

¿Qué es godot-4-api-pitfalls?

Este skill ayuda a desarrolladores de Godot a migrar código de Godot 3 a Godot 4 de forma confiable. La mayoría del código GDScript en tutoriales, foros y datos de entrenamiento de modelos está escrito para Godot 3, y Godot 4 introdujo cambios importantes en nombres de clases, métodos, propiedades y sintaxis. El skill proporciona una tabla de reescritura completa que cubre más de 40 transformaciones comunes, incluyendo KinematicBody → CharacterBody, cambios en export/onready/setget, yield → await, la nueva API de Tweens, FileAccess, TileMapLayer, y muchas más. También incluye ejemplos detallados de 'antes y después' para controladores de personajes, propiedades/eventos/timers, y archivos/tiempo/aleatoriedad/escenas. Además, explica qué errores no detecta el parser de Godot y cómo usar opciones de verificación más estrictas (override.cfg) para encontrar problemas en tiempo de compilación, así como cambios de comportamiento que ningún parser detecta. Incluye comandos de autoverificación con grep para buscar rápidamente nombres de Godot 3 en los scripts y recomienda usar `godot --check-only` y ejecutar las escenas para detectar errores en tiempo de ejecución.

Compatible con~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 Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Errores comunes de la API de Godot 4

La mayor parte del GDScript en tutoriales, respuestas de foros y datos de entrenamiento de modelos es de Godot 3. Godot 4 renombró clases, métodos y propiedades y cambió la sintaxis de GDScript. Algunos códigos de Godot 3 fallan al analizarse, y otros se analizan pero fallan solo en tiempo de ejecución. Antes de escribir código, confirma la versión: project.godot tiene una línea como config/features=PackedStringArray("4.7", ...).

Tabla de reescritura

Godot 3 (incorrecto en Godot 4)Godot 4
KinematicBody2D, KinematicBodyCharacterBody2D, CharacterBody3D
velocity = move_and_slide(velocity, Vector2.UP)Establece la propiedad velocity, luego llama a move_and_slide() sin argumentos. up_direction y floor_snap_length son propiedades
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, o un bloque set(value): en línea
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 forma de cadena aún funciona, pero los errores tipográficos solo se detectan en tiempo de ejecución)
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) o randi_range(a, b)
stepify, deg2rad, rad2deg, range_lerp, str2varsnapped, deg_to_rad, rad_to_deg, remap, str_to_var
Nodo Tween: $Tween.interpolate_property(...), start()var tween := create_tween(), luego tween.tween_property(node, "property", final_value, duration)
File.new(), file.open(path, File.READ)FileAccess.open(path, FileAccess.READ) (devuelve null en caso de fallo, consulta 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() (redibujar un CanvasItem)queue_redraw()
TileMap.set_cellv(pos, id), world_to_mapTileMapLayer.set_cell(coords, source_id, atlas_coords), local_to_map (consulta el skill godot-tilemaplayer)
BUTTON_LEFT, BUTTON_WHEEL_UPMOUSE_BUTTON_LEFT, MOUSE_BUTTON_WHEEL_UP
event.scancode, event.controlevent.keycode (o 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 basado en 0)set_collision_layer_value(1, true) (número de capa basado en 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 (solo la autoridad puede llamarlo), is_multiplayer_authority(). master func no tiene un equivalente directo: usa @rpc("any_peer") y comprueba multiplayer.get_remote_sender_id()
Engine.editor_hint, filenameEngine.is_editor_hint(), scene_file_path
RectangleShape2D.extents (mitad del tamaño)size (tamaño completo, por lo tanto duplica el valor anterior)
ResourceSaver.save(path, resource)ResourceSaver.save(resource, path)

Renombres menos comunes (3D, XR, navegación, sombreadores, hilos) están en references/renames.md.

Antes y después

Controlador de personaje:

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()

Propiedades, señales, temporizadores y 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()

Archivos, tiempo, aleatoriedad y escenas:

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("No se puede abrir el archivo de guardado: %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")

Lo que el analizador no detecta

godot --check-only informa de un miembro desconocido como un error solo cuando el receptor es self, un tipo incorporado (Array, Vector2, Color) o un nombre de clase llamado estáticamente (OS.get_unix_time()); los recuentos y tipos de argumentos incorrectos también son errores. Un miembro que falta en un objeto, incluso uno tipado como var shape := RectangleShape2D.new(), se trata como "puede existir en un subtipo" y se analiza con la configuración predeterminada. Estas líneas de Godot 3 pasan todas --check-only en Godot 4.7.2 y fallan solo cuando se ejecutan:

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

Para hacer que el analizador las rechace durante una verificación, coloca estas líneas en un override.cfg temporal junto a project.godot (Godot lo lee además de project.godot, incluido --check-only) y elimínalo después; si el proyecto ya tiene un override.cfg, añádelo y restaura en su lugar. 2 significa Error:

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

No dejes esto en el project.godot del usuario a menos que se solicite: con Error, cada llamada sin tipo como body.take_damage(1) o $Sprite.play() es un error de análisis, y el juego aún se inicia con código de salida 0, imprimiendo solo Failed to load script. El código tipado pasa, por lo que es preferible usar referencias tipadas: @onready var panel: Panel = $Panel, y var key := event as InputEventKey antes de leer key.keycode.

Cambios de comportamiento que ningún analizador detecta:

  • Los métodos de ciclo de vida como _ready() ya no llaman a la implementación de la clase padre implícitamente. Llama a super() cuando un script heredado lo necesite.
  • Array.slice(begin, end) excluye end.
  • Camera2D.zoom está invertido: valores más grandes acercan.
  • String.right(n) devuelve los últimos n caracteres.
  • randomize() se ejecuta automáticamente al inicio. Usa seed() para una aleatoriedad reproducible.
  • call_group() es inmediato. Usa call_group_flags(SceneTree.GROUP_CALL_DEFERRED, ...) para el comportamiento diferido anterior.
  • AnimatedSprite2D.playing fue eliminado. Llama a play().
  • TileMap aún existe pero está obsoleto. El código nuevo usa un nodo TileMapLayer por capa.

Autoverificación

  1. Busca en los scripts que escribiste o modificaste nombres de Godot 3 (GNU grep; añade -P o adapta para otros 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 *$' .
    

    Reescribe cada coincidencia con la tabla anterior. Las constantes de tema "margin_left" entrecomilladas de MarginContainer son nombres de Godot 4 y no coinciden. Que no haya coincidencias no prueba que el código sea de Godot 4: la lista es el subconjunto común.

  2. Analiza cada script modificado desde la raíz del proyecto: godot --headless --path . --check-only --script res://path/to/script.gd. Lee la salida, no solo el código de salida. Con el override.cfg temporal anterior, los miembros renombrados en objetos tipados también fallan aquí.

  3. Ejecuta la escena que usa el script y lee su salida, porque los errores en tiempo de ejecución no cambian el código de salida. El skill godot-verify tiene los comandos.

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 relacionados