Community编程与开发github.com

ziva-sh/godot-4-api-pitfalls

修正AI助手在Godot 4项目中编写的Godot 3 API和GDScript语法,例如KinematicBody2D、move_and_slide(velocity)、export、onready、setget、yield、字符串connect()、instance()、rand_range、Tween节点、File、change_scene、Spatial、rect_*属性、Pool*Array、OS时间函数以及旧版TileMap API。适用于编写、审查或移植Godot 4.x的GDScript,以及脚本报错“not declared”、“not found in base”、“was removed in Godot 4”或“is not present”时使用。

godot-4-api-pitfalls 是什么?

该技能旨在解决AI辅助开发Godot 4项目时,因训练数据多基于Godot 3而引入的不兼容代码问题。它提供了一个全面的API迁移对照表,涵盖了Godot 3到4的主要改动,包括节点名称变更(如KinematicBody2D→CharacterBody2D)、GDScript语法变化(如export→@export、yield→await)、以及各种内置函数和属性的重命名。该技能不仅适用于Claude Code等AI助手,也可作为手动代码审查的参考,帮助开发者快速定位并修正Godot 3的遗留痕迹。技能还详细说明了哪些错误无法被Godot解析器捕获(如运行时类型错误),并提供了使用`--check-only`进行静态检查和编写类型安全代码的建议。最后,它提供了一个自检grep命令清单和验证步骤,确保代码完全兼容Godot 4。

兼容平台~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 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Godot 4 API 陷阱

大多数教程、论坛回答和模型训练数据中的GDScript都是Godot 3的。Godot 4重命名了类、方法和属性,并更改了GDScript语法。部分Godot 3代码无法解析,部分可以解析但只在运行时出错。编写代码前,请确认版本:project.godot 中包含类似 config/features=PackedStringArray("4.7", ...) 的行。

重写对照表

Godot 3(在Godot 4中错误)Godot 4
KinematicBody2D, KinematicBodyCharacterBody2D, CharacterBody3D
velocity = move_and_slide(velocity, Vector2.UP)设置 velocity 属性,然后无参调用 move_and_slide()。up_direction 和 floor_snap_length 现在是属性
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,或内联的 set(value): 块
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()(字符串形式仍然有效,但拼写错误只会在运行时发现)
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) 或 randi_range(a, b)
stepify, deg2rad, rad2deg, range_lerp, str2varsnapped, deg_to_rad, rad_to_deg, remap, str_to_var
Tween 节点:$Tween.interpolate_property(...), start()var tween := create_tween(),然后 tween.tween_property(node, "property", final_value, duration)
File.new(), file.open(path, File.READ)FileAccess.open(path, FileAccess.READ)(失败时返回 null,查看 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()(重绘 CanvasItem)queue_redraw()
TileMap.set_cellv(pos, id), world_to_mapTileMapLayer.set_cell(coords, source_id, atlas_coords), local_to_map(参见 godot-tilemaplayer 技能)
BUTTON_LEFT, BUTTON_WHEEL_UPMOUSE_BUTTON_LEFT, MOUSE_BUTTON_WHEEL_UP
event.scancode, event.controlevent.keycode(或 physical_keycode), event.ctrl_pressed
array.empty(), array.remove(i), array.invert()is_empty(), remove_at(i), reverse()
set_collision_layer_bit(0, true)(0起始位)set_collision_layer_value(1, true)(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(仅权威方可调用), is_multiplayer_authority()。master func 无直接等效项:使用 @rpc("any_peer") 并检查 multiplayer.get_remote_sender_id()
Engine.editor_hint, filenameEngine.is_editor_hint(), scene_file_path
RectangleShape2D.extents(半尺寸)size(全尺寸,因此需将旧值翻倍)
ResourceSaver.save(path, resource)ResourceSaver.save(resource, path)

较少见的重命名(3D、XR、导航、着色器、线程)请参见 references/renames.md。

前后对比

角色控制器:

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

属性、信号、计时器和 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()

文件、时间、随机性和场景:

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("Cannot open save file: %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")

解析器无法捕获的内容

godot --check-only 仅在接收者为 self、内置类型(Array, Vector2, Color)或静态调用的类名(OS.get_unix_time())时,将未知成员报告为错误;参数数量和类型错误也会报错。但在对象(即使是类型化的对象,如 var shape := RectangleShape2D.new())上缺少的成员会被视为“可能存在于子类型”,并使用默认设置进行解析。以下 Godot 3 代码在 Godot 4.7.2 上均能通过 --check-only,仅在运行时失败:

  • PackedScene 上的 scene.instance(),get_tree().change_scene(path),get_tree().set_input_as_handled()
  • RectangleShape2D 上的 shape.extents,$Panel.rect_size
  • InputEvent 上的 event.scancode 或 event.control

若要在检查时让解析器拒绝这些代码,请将这些行放入 project.godot 旁边的临时 override.cfg 中(Godot 会读取它覆盖 project.godot,包括 --check-only),然后删除它;如果项目已有 override.cfg,请追加后恢复。2 表示错误:

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

除非用户要求,否则不要将这些保留在用户的 project.godot 中:在 Error 模式下,每个未类型化的调用(如 body.take_damage(1) 或 $Sprite.play())都是解析错误,游戏仍会以退出码 0 启动,仅打印 Failed to load script。类型化代码可通过,因此请优先使用类型化引用:@onready var panel: Panel = $Panel,并在读取 key.keycode 之前使用 var key := event as InputEventKey。

解析器无法捕获的行为变更:

  • 生命周期方法(如 _ready())不再隐式调用父类实现。当继承的脚本需要时,调用 super()。
  • Array.slice(begin, end) 不包含 end。
  • Camera2D.zoom 已反转:值越大放大越多。
  • String.right(n) 返回最后 n 个字符。
  • randomize() 在启动时自动运行。使用 seed() 进行可复现的随机性。
  • call_group() 是立即执行的。使用 call_group_flags(SceneTree.GROUP_CALL_DEFERRED, ...) 来实现旧的延迟行为。
  • AnimatedSprite2D.playing 已被移除。请调用 play()。
  • TileMap 仍然存在但已弃用。新代码每层使用一个 TileMapLayer 节点。

自检

  1. 在您编写或修改的脚本中搜索 Godot 3 名称(GNU grep;添加 -P 或根据其他 grep 进行调整):

    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 *$' .
    

    使用上表重写每个匹配项。MarginContainer 的带引号 "margin_left" 主题常量是 Godot 4 名称,不会被匹配。没有匹配不代表代码就是 Godot 4:列表只是常见子集。

  2. 从项目根目录解析每个更改后的脚本:godot --headless --path . --check-only --script res://path/to/script.gd。读取输出,而不仅仅是退出码。使用上面的临时 override.cfg,类型化对象上重命名的成员也会在此处失败。

  3. 运行使用该脚本的场景并读取其输出,因为运行时错误不会改变退出码。godot-verify 技能包含相关命令。

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

相关技能