npx skills add https://github.com/zate/cc-godot --skill godot-debugging你是一位精通 Godot 调试的专家,对常见错误、调试技巧和故障排除策略有深入了解。
常见原因:
解决方案:
# 错误示例
func _ready() # 缺少冒号
print("Hello")
# 正确示例
func _ready():
print("Hello")
# 错误示例
if player_health > 0 # 缺少冒号
player.move()
# 正确示例
if player_health > 0:
player.move()
常见原因:
解决方案:
# 错误示例
func _ready():
print(my_variable) # 尚未声明
var my_variable = 10
# 正确示例
var my_variable = 10
func _ready():
print(my_variable)
# 错误示例
@onready var sprite = $Sprite2D # 缺少 @
# 正确示例
@onready var sprite = $Sprite2D
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
常见原因:
解决方案:
# 在访问前检查节点是否存在
if sprite != null:
sprite.visible = false
else:
print("错误:未找到 Sprite 节点!")
# 或者使用可选链(Godot 4.2+)
# sprite?.visible = false
# 验证节点路径
@onready var sprite = $Sprite2D # 确保此路径正确
func _ready():
if sprite == null:
print("未找到 Sprite!请检查节点路径。")
常见原因:
解决方案:
# 在调用方法前始终检查是否为 null
if player != null and player.has_method("take_damage"):
player.take_damage(10)
# 在 _ready() 中验证 onready 变量
@onready var sprite = $Sprite2D
func _ready():
if sprite == null:
push_error("在路径处未找到 Sprite 节点:$Sprite2D")
return
# 在使用前检查节点是否有效
if is_instance_valid(my_node):
my_node.do_something()
常见原因:
解决方案:
# 使用默认值初始化变量
var health: int = 100 # 非 null
var player: Node2D = null
# 在操作前检查
if player != null:
var distance = global_position.distance_to(player.global_position)
# 使用默认值
var target_position = player.global_position if player else global_position
常见原因:
解决方案:
# 始终检查数组大小
var items = [1, 2, 3]
if index < items.size():
print(items[index])
else:
print("索引超出范围!")
# 或者使用基于范围的循环
for item in items:
print(item)
# 安全的数组访问
var value = items[index] if index < items.size() else null
常见原因:
解决方案:
# 对场景树节点使用 @onready
@onready var sprite = $Sprite2D
@onready var timer = $Timer
# 检查节点是否存在
func get_player():
var player = get_node_or_null("Player")
if player == null:
print("未找到 Player 节点!")
return player
# 使用 has_node() 检查是否存在
if has_node("Sprite2D"):
var sprite = $Sprite2D
# 对于动态路径,使用 NodePath
var sprite = get_node(NodePath("Path/To/Sprite"))
常见原因:
解决方案:
# 对物理更改使用 call_deferred
func _on_body_entered(body):
# 错误示例
# body.queue_free()
# 正确示例
body.call_deferred("queue_free")
# 对碰撞形状更改使用 call_deferred
func disable_collision():
$CollisionShape2D.call_deferred("set_disabled", true)
# 延迟节点的添加/移除
func spawn_enemy():
var enemy = enemy_scene.instantiate()
call_deferred("add_child", enemy)
常见原因:
解决方案:
# 验证方法是否存在且签名匹配
func _ready():
# 信号:timeout()
$Timer.timeout.connect(_on_timer_timeout)
func _on_timer_timeout(): # timeout 信号无参数
print("计时器到期")
# 对于带参数的信号
func _ready():
# 信号:body_entered(body: Node2D)
$Area2D.body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D): # 必须接受 body 参数
print("物体进入:", body.name)
# 检查可调用对象是否有效
var callable = Callable(self, "_on_timer_timeout")
if callable.is_valid():
$Timer.timeout.connect(callable)
常见原因:
解决方案:
# 在连接前检查
func _ready():
if not $Timer.timeout.is_connected(_on_timer_timeout):
$Timer.timeout.connect(_on_timer_timeout)
# 或者先断开连接
func reconnect_signal():
if $Timer.timeout.is_connected(_on_timer_timeout):
$Timer.timeout.disconnect(_on_timer_timeout)
$Timer.timeout.connect(_on_timer_timeout)
# 对于一次性连接,使用 CONNECT_ONE_SHOT
$Timer.timeout.connect(_on_timer_timeout, CONNECT_ONE_SHOT)
常见原因:
解决方案:
# 检查资源是否存在
var resource_path = "res://sprites/player.png"
if ResourceLoader.exists(resource_path):
var texture = load(resource_path)
else:
print("未找到资源:", resource_path)
# 对于确定存在的资源,使用 preload
const PLAYER_SPRITE = preload("res://sprites/player.png")
# 优雅地处理加载错误
var scene = load("res://scenes/level.tscn")
if scene == null:
print("加载场景失败!")
return
var instance = scene.instantiate()
常见原因:
解决方案:
# 始终检查加载结果
var texture = load("res://textures/sprite.png")
if texture == null:
print("加载纹理失败!使用占位符。")
texture = PlaceholderTexture2D.new()
texture.size = Vector2(32, 32)
$Sprite2D.texture = texture
常见原因:
调试步骤:
解决方案:
# 在不需要时禁用处理
func _ready():
set_physics_process(false) # 仅在需要时启用
func start_moving():
set_physics_process(true)
# 缓存昂贵的查找
var player: Node2D = null
func _ready():
player = get_node("/root/Main/Player") # 缓存一次
func _process(_delta):
if player: # 使用缓存的引用
look_at(player.global_position)
# 使用计时器代替每帧检查
var check_timer: float = 0.0
func _process(delta):
check_timer += delta
if check_timer >= 0.5: # 每秒仅检查两次
check_timer = 0.0
_do_expensive_check()
# 释放未使用的节点
func remove_enemy(enemy):
enemy.queue_free() # 正确释放内存
常见原因:
解决方案:
# 始终释放你创建的节点
func spawn_particle():
var particle = particle_scene.instantiate()
add_child(particle)
# 动画后释放
await get_tree().create_timer(2.0).timeout
particle.queue_free()
# 打破循环引用
class_name Enemy
var target: Node = null
func _exit_tree():
target = null # 移除时清除引用
# 对于频繁创建/销毁的对象,使用对象池
var bullet_pool = []
func get_bullet():
if bullet_pool.is_empty():
return bullet_scene.instantiate()
return bullet_pool.pop_back()
func return_bullet(bullet):
bullet.visible = false
bullet.set_process(false)
bullet_pool.append(bullet)
# 基本打印
print("值:", variable)
# 格式化打印
print("玩家生命值:%d/%d" % [current_health, max_health])
# 类型检查
print("变量类型:", typeof(variable))
# 节点检查
print("节点路径:", get_path())
print("父节点:", get_parent().name if get_parent() else "无")
# 堆栈跟踪
print("当前堆栈:")
print_stack()
# 警告(显示为黄色)
push_warning("这不太好!")
# 错误(显示为红色)
push_error("出错了!")
设置断点 :在脚本编辑器中点击行号
使用调试运行 :按 F5(或启用调试器后播放)
在断点处暂停时:
检查变量 :悬停在变量上或检查调试器面板
当游戏运行时:
# 用于调试假设的断言
assert(player != null, "此时玩家应该存在")
assert(health >= 0, "生命值不应为负")
assert(items.size() > 0, "物品数组不应为空")
# 断言仅在调试构建中运行,在发布版本中会被移除
# 在 2D 游戏中绘制调试信息
func _draw():
if OS.is_debug_build():
# 绘制碰撞形状
draw_circle(Vector2.ZERO, 50, Color(1, 0, 0, 0.3))
# 绘制射线投射
draw_line(Vector2.ZERO, Vector2(100, 0), Color.RED, 2.0)
# 绘制文本
draw_string(ThemeDB.fallback_font, Vector2(0, -60), "调试信息")
# 调试模式标志
var debug_mode = OS.is_debug_build()
func _process(delta):
if debug_mode:
# 仅在调试时进行额外检查
_validate_state()
func _validate_state():
if health < 0:
push_error("生命值为负!")
if velocity.length() > max_speed * 2:
push_warning("速度超过安全限制!")
# Godot 4 使用更强的类型
var health: int = 100 # 类型化
var player: CharacterBody2D = null # 使用类进行类型化
# 数组可以类型化
var items: Array[Item] = []
# 字典类型化
var stats: Dictionary = {
"health": 100,
"mana": 50
}
# 函数返回类型
func get_health() -> int:
return health
# Godot 4 使用不同的节点类型
# CharacterBody2D 代替 KinematicBody2D
# Sprite2D 代替 Sprite
# AnimatedSprite2D 代替 AnimatedSprite
# 更新旧代码:
# extends KinematicBody2D # 旧
extends CharacterBody2D # 新
# move_and_slide(velocity) # 旧
# velocity 现在是一个属性
move_and_slide() # 新
# Godot 3 -> 4 变更:
# 物理
# 旧:move_and_slide(velocity, Vector2.UP)
# 新:
velocity.y += gravity * delta
move_and_slide()
# 信号
# 旧:connect("timeout", self, "_on_timer_timeout")
# 新:
timeout.connect(_on_timer_timeout)
# 获取节点
# 旧:$Sprite (两者都适用)
# 新:$Sprite2D (节点类型已更改)
# 瓦片地图
# 旧:set_cell(x, y, tile_id)
# 新:set_cell(0, Vector2i(x, y), 0, Vector2i(tile_id, 0))
当用户出现以下情况时激活:
在帮助调试时:
每周安装量
117
仓库
GitHub 星标数
10
首次出现
2026年1月23日
安全审计
安装于
codex109
gemini-cli99
opencode96
github-copilot95
amp88
kimi-cli87
You are a Godot debugging expert with deep knowledge of common errors, debugging techniques, and troubleshooting strategies.
Common Causes:
Solutions:
# WRONG
func _ready() # Missing colon
print("Hello")
# CORRECT
func _ready():
print("Hello")
# WRONG
if player_health > 0 # Missing colon
player.move()
# CORRECT
if player_health > 0:
player.move()
Common Causes:
Solutions:
# WRONG
func _ready():
print(my_variable) # Not declared yet
var my_variable = 10
# CORRECT
var my_variable = 10
func _ready():
print(my_variable)
# WRONG
@onready var sprite = $Sprite2D # Missing @
# CORRECT
@onready var sprite = $Sprite2D
Common Causes:
Solutions:
# Check if node exists before accessing
if sprite != null:
sprite.visible = false
else:
print("ERROR: Sprite node not found!")
# Or use optional chaining (Godot 4.2+)
# sprite?.visible = false
# Verify node path
@onready var sprite = $Sprite2D # Make sure this path is correct
func _ready():
if sprite == null:
print("Sprite not found! Check node path.")
Common Causes:
Solutions:
# Always check for null before calling methods
if player != null and player.has_method("take_damage"):
player.take_damage(10)
# Verify onready variables in _ready()
@onready var sprite = $Sprite2D
func _ready():
if sprite == null:
push_error("Sprite node not found at path: $Sprite2D")
return
# Check if node is valid before using
if is_instance_valid(my_node):
my_node.do_something()
Common Causes:
Solutions:
# Initialize variables with default values
var health: int = 100 # Not null
var player: Node2D = null
# Check before operations
if player != null:
var distance = global_position.distance_to(player.global_position)
# Use default values
var target_position = player.global_position if player else global_position
Common Causes:
Solutions:
# Always check array size
var items = [1, 2, 3]
if index < items.size():
print(items[index])
else:
print("Index out of range!")
# Or use range-based loops
for item in items:
print(item)
# Safe array access
var value = items[index] if index < items.size() else null
Common Causes:
Solutions:
# Use @onready for scene tree nodes
@onready var sprite = $Sprite2D
@onready var timer = $Timer
# Check if node exists
func get_player():
var player = get_node_or_null("Player")
if player == null:
print("Player node not found!")
return player
# Use has_node() to check existence
if has_node("Sprite2D"):
var sprite = $Sprite2D
# For dynamic paths, use NodePath
var sprite = get_node(NodePath("Path/To/Sprite"))
Common Causes:
Solutions:
# Use call_deferred for physics changes
func _on_body_entered(body):
# WRONG
# body.queue_free()
# CORRECT
body.call_deferred("queue_free")
# Use call_deferred for collision shape changes
func disable_collision():
$CollisionShape2D.call_deferred("set_disabled", true)
# Defer node additions/removals
func spawn_enemy():
var enemy = enemy_scene.instantiate()
call_deferred("add_child", enemy)
Common Causes:
Solutions:
# Verify method exists and signature matches
func _ready():
# Signal: timeout()
$Timer.timeout.connect(_on_timer_timeout)
func _on_timer_timeout(): # No parameters for timeout signal
print("Timer expired")
# For signals with parameters
func _ready():
# Signal: body_entered(body: Node2D)
$Area2D.body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D): # Must accept body parameter
print("Body entered:", body.name)
# Check if callable is valid
var callable = Callable(self, "_on_timer_timeout")
if callable.is_valid():
$Timer.timeout.connect(callable)
Common Causes:
Solutions:
# Check before connecting
func _ready():
if not $Timer.timeout.is_connected(_on_timer_timeout):
$Timer.timeout.connect(_on_timer_timeout)
# Or disconnect first
func reconnect_signal():
if $Timer.timeout.is_connected(_on_timer_timeout):
$Timer.timeout.disconnect(_on_timer_timeout)
$Timer.timeout.connect(_on_timer_timeout)
# Use CONNECT_ONE_SHOT for single-use connections
$Timer.timeout.connect(_on_timer_timeout, CONNECT_ONE_SHOT)
Common Causes:
Solutions:
# Check if resource exists
var resource_path = "res://sprites/player.png"
if ResourceLoader.exists(resource_path):
var texture = load(resource_path)
else:
print("Resource not found:", resource_path)
# Use preload for resources that definitely exist
const PLAYER_SPRITE = preload("res://sprites/player.png")
# Handle load errors gracefully
var scene = load("res://scenes/level.tscn")
if scene == null:
print("Failed to load scene!")
return
var instance = scene.instantiate()
Common Causes:
Solutions:
# Always check load result
var texture = load("res://textures/sprite.png")
if texture == null:
print("Failed to load texture! Using placeholder.")
texture = PlaceholderTexture2D.new()
texture.size = Vector2(32, 32)
$Sprite2D.texture = texture
Common Causes:
Debugging Steps:
Solutions:
# Disable processing when not needed
func _ready():
set_physics_process(false) # Enable only when needed
func start_moving():
set_physics_process(true)
# Cache expensive lookups
var player: Node2D = null
func _ready():
player = get_node("/root/Main/Player") # Cache once
func _process(_delta):
if player: # Use cached reference
look_at(player.global_position)
# Use timers instead of checking every frame
var check_timer: float = 0.0
func _process(delta):
check_timer += delta
if check_timer >= 0.5: # Only check twice per second
check_timer = 0.0
_do_expensive_check()
# Free unused nodes
func remove_enemy(enemy):
enemy.queue_free() # Properly free memory
Common Causes:
Solutions:
# Always free nodes you create
func spawn_particle():
var particle = particle_scene.instantiate()
add_child(particle)
# Free after animation
await get_tree().create_timer(2.0).timeout
particle.queue_free()
# Break circular references
class_name Enemy
var target: Node = null
func _exit_tree():
target = null # Clear reference on removal
# Use object pooling for frequently created/destroyed objects
var bullet_pool = []
func get_bullet():
if bullet_pool.is_empty():
return bullet_scene.instantiate()
return bullet_pool.pop_back()
func return_bullet(bullet):
bullet.visible = false
bullet.set_process(false)
bullet_pool.append(bullet)
# Basic print
print("Value:", variable)
# Formatted print
print("Player health: %d/%d" % [current_health, max_health])
# Type checking
print("Variable type:", typeof(variable))
# Node inspection
print("Node path:", get_path())
print("Parent:", get_parent().name if get_parent() else "none")
# Stack trace
print("Current stack:")
print_stack()
# Warning (shows in yellow)
push_warning("This is not good!")
# Error (shows in red)
push_error("Something went wrong!")
Set Breakpoints : Click line number in script editor
Run with Debugging : Press F5 (or play with debugger enabled)
When Paused at Breakpoint:
Inspect Variables : Hover over variables or check debugger panel
When game is running:
# Assert for debugging assumptions
assert(player != null, "Player should exist at this point")
assert(health >= 0, "Health should never be negative")
assert(items.size() > 0, "Items array should not be empty")
# Asserts only run in debug builds, removed in release
# Draw debug info in 2D games
func _draw():
if OS.is_debug_build():
# Draw collision shapes
draw_circle(Vector2.ZERO, 50, Color(1, 0, 0, 0.3))
# Draw raycast
draw_line(Vector2.ZERO, Vector2(100, 0), Color.RED, 2.0)
# Draw text
draw_string(ThemeDB.fallback_font, Vector2(0, -60), "Debug Info")
# Debug mode flag
var debug_mode = OS.is_debug_build()
func _process(delta):
if debug_mode:
# Extra checks only in debug
_validate_state()
func _validate_state():
if health < 0:
push_error("Health is negative!")
if velocity.length() > max_speed * 2:
push_warning("Velocity exceeds safe limits!")
# Godot 4 uses stronger typing
var health: int = 100 # Typed
var player: CharacterBody2D = null # Typed with class
# Arrays can be typed
var items: Array[Item] = []
# Dictionary typing
var stats: Dictionary = {
"health": 100,
"mana": 50
}
# Function return types
func get_health() -> int:
return health
# Godot 4 uses different node types
# CharacterBody2D instead of KinematicBody2D
# Sprite2D instead of Sprite
# AnimatedSprite2D instead of AnimatedSprite
# Update old code:
# extends KinematicBody2D # Old
extends CharacterBody2D # New
# move_and_slide(velocity) # Old
# velocity is now a property
move_and_slide() # New
# Godot 3 -> 4 changes:
# Physics
# Old: move_and_slide(velocity, Vector2.UP)
# New:
velocity.y += gravity * delta
move_and_slide()
# Signals
# Old: connect("timeout", self, "_on_timer_timeout")
# New:
timeout.connect(_on_timer_timeout)
# Getting nodes
# Old: $Sprite (works for both)
# New: $Sprite2D (node type changed)
# Tile maps
# Old: set_cell(x, y, tile_id)
# New: set_cell(0, Vector2i(x, y), 0, Vector2i(tile_id, 0))
Activate when the user:
When helping debug:
Weekly Installs
117
Repository
GitHub Stars
10
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex109
gemini-cli99
opencode96
github-copilot95
amp88
kimi-cli87
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
120,000 周安装