@jarrodmedrano/claude-skills 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/bevy/SKILL.md +406 -0
- package/.claude/skills/bevy/references/bevy_specific_tips.md +385 -0
- package/.claude/skills/bevy/references/common_pitfalls.md +217 -0
- package/.claude/skills/bevy/references/ecs_patterns.md +277 -0
- package/.claude/skills/bevy/references/project_structure.md +116 -0
- package/.claude/skills/bevy/references/ui_development.md +147 -0
- package/.claude/skills/domain-driven-design/SKILL.md +459 -0
- package/.claude/skills/domain-driven-design/references/ddd_foundations_and_patterns.md +664 -0
- package/.claude/skills/domain-driven-design/references/rich_hickey_principles.md +406 -0
- package/.claude/skills/domain-driven-design/references/visualization_examples.md +790 -0
- package/.claude/skills/domain-driven-design/references/wlaschin_patterns.md +639 -0
- package/.claude/skills/godot/SKILL.md +728 -0
- package/.claude/skills/godot/assets/templates/attribute_template.gd +109 -0
- package/.claude/skills/godot/assets/templates/component_template.gd +76 -0
- package/.claude/skills/godot/assets/templates/interaction_template.gd +108 -0
- package/.claude/skills/godot/assets/templates/item_resource.tres +11 -0
- package/.claude/skills/godot/assets/templates/spell_resource.tres +20 -0
- package/.claude/skills/godot/references/architecture-patterns.md +608 -0
- package/.claude/skills/godot/references/common-pitfalls.md +518 -0
- package/.claude/skills/godot/references/file-formats.md +491 -0
- package/.claude/skills/godot/references/godot4-physics-api.md +302 -0
- package/.claude/skills/godot/scripts/validate_tres.py +145 -0
- package/.claude/skills/godot/scripts/validate_tscn.py +170 -0
- package/.claude/skills/guitar-fretboard-mastery/SKILL.md +179 -0
- package/.claude/skills/guitar-fretboard-mastery/guitar-fretboard-mastery.skill +0 -0
- package/.claude/skills/react-three-fiber/SKILL.md +2055 -0
- package/.claude/skills/react-three-fiber/scripts/build-scene.ts +171 -0
- package/package.json +1 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
# Common Godot Pitfalls and Solutions
|
|
2
|
+
|
|
3
|
+
This document catalogs frequent mistakes, gotchas, and their solutions when working with Godot 4.x projects.
|
|
4
|
+
|
|
5
|
+
## Initialization and @onready Timing
|
|
6
|
+
|
|
7
|
+
### The Problem
|
|
8
|
+
|
|
9
|
+
`@onready` variables are initialized when `_ready()` is called, but the order of initialization across the scene tree can cause issues.
|
|
10
|
+
|
|
11
|
+
### Common Pitfall: Null References from Parent Methods
|
|
12
|
+
|
|
13
|
+
**Problem:**
|
|
14
|
+
```gdscript
|
|
15
|
+
# In child component
|
|
16
|
+
@onready var player: CharacterBody3D = get_parent()
|
|
17
|
+
@onready var camera: Camera3D = player.get_camera() # ❌ Returns null!
|
|
18
|
+
|
|
19
|
+
func _ready():
|
|
20
|
+
# camera is null here - get_camera() wasn't available during @onready
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Why it fails:**
|
|
24
|
+
- `@onready` runs before `_ready()` in the scene tree
|
|
25
|
+
- If `get_camera()` returns a node that's dynamically set up, it may not exist yet
|
|
26
|
+
- Parent initialization might not be complete
|
|
27
|
+
|
|
28
|
+
**Solution: Use dynamic getters**
|
|
29
|
+
```gdscript
|
|
30
|
+
# ✅ Better approach
|
|
31
|
+
@onready var player: CharacterBody3D = get_parent()
|
|
32
|
+
|
|
33
|
+
func _get_camera() -> Camera3D:
|
|
34
|
+
if player and player.has_method("get_camera"):
|
|
35
|
+
return player.get_camera()
|
|
36
|
+
return null
|
|
37
|
+
|
|
38
|
+
func perform_action():
|
|
39
|
+
var camera = _get_camera()
|
|
40
|
+
if camera:
|
|
41
|
+
# Use camera
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Solution: Initialize in _ready()**
|
|
45
|
+
```gdscript
|
|
46
|
+
# ✅ Alternative approach
|
|
47
|
+
var player: CharacterBody3D
|
|
48
|
+
var camera: Camera3D
|
|
49
|
+
|
|
50
|
+
func _ready():
|
|
51
|
+
player = get_parent()
|
|
52
|
+
if player and player.has_method("get_camera"):
|
|
53
|
+
camera = player.get_camera()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Common Pitfall: Accessing Child Nodes Too Early
|
|
57
|
+
|
|
58
|
+
**Problem:**
|
|
59
|
+
```gdscript
|
|
60
|
+
# Parent node
|
|
61
|
+
@onready var child_component = $ChildComponent
|
|
62
|
+
|
|
63
|
+
func _ready():
|
|
64
|
+
child_component.setup() # ❌ Might fail if child's _ready() hasn't run
|
|
65
|
+
|
|
66
|
+
# Child node
|
|
67
|
+
var is_initialized: bool = false
|
|
68
|
+
|
|
69
|
+
func _ready():
|
|
70
|
+
# Complex initialization
|
|
71
|
+
is_initialized = true
|
|
72
|
+
|
|
73
|
+
func setup():
|
|
74
|
+
if not is_initialized:
|
|
75
|
+
push_error("Called setup() before initialization!")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Solution: Use call_deferred or signals**
|
|
79
|
+
```gdscript
|
|
80
|
+
# ✅ Parent waits for child to be ready
|
|
81
|
+
func _ready():
|
|
82
|
+
await get_tree().process_frame # Wait one frame
|
|
83
|
+
child_component.setup()
|
|
84
|
+
|
|
85
|
+
# ✅ Or use signals
|
|
86
|
+
func _ready():
|
|
87
|
+
child_component.initialized.connect(_on_child_ready)
|
|
88
|
+
|
|
89
|
+
func _on_child_ready():
|
|
90
|
+
# Child is definitely ready now
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### _ready() Execution Order
|
|
94
|
+
|
|
95
|
+
**Key principle**: `_ready()` is called **bottom-up** in the scene tree (children before parents).
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
SceneRoot
|
|
99
|
+
├─ Parent (ready called THIRD)
|
|
100
|
+
├─ Child1 (ready called FIRST)
|
|
101
|
+
└─ Child2 (ready called SECOND)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Implications:**
|
|
105
|
+
- Children's `_ready()` complete before parent's `_ready()` starts
|
|
106
|
+
- Parent can safely access child nodes in `_ready()`
|
|
107
|
+
- Children should NOT assume parent is ready during their `_ready()`
|
|
108
|
+
|
|
109
|
+
**Example:**
|
|
110
|
+
```gdscript
|
|
111
|
+
# Child component
|
|
112
|
+
func _ready():
|
|
113
|
+
var parent = get_parent()
|
|
114
|
+
# ❌ Don't call parent.initialize() - parent's _ready() hasn't run yet
|
|
115
|
+
# ✅ Instead, emit a signal or wait
|
|
116
|
+
ready.emit()
|
|
117
|
+
|
|
118
|
+
# Parent
|
|
119
|
+
func _ready():
|
|
120
|
+
for child in get_children():
|
|
121
|
+
# ✅ Children are fully ready here
|
|
122
|
+
if child.has_method("configure"):
|
|
123
|
+
child.configure(some_data)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Node References and get_node()
|
|
127
|
+
|
|
128
|
+
### Common Pitfall: Hardcoded NodePaths Breaking
|
|
129
|
+
|
|
130
|
+
**Problem:**
|
|
131
|
+
```gdscript
|
|
132
|
+
@onready var health_bar = $"../UI/HealthBar" # ❌ Fragile - breaks if hierarchy changes
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Solution: Use groups or signals**
|
|
136
|
+
```gdscript
|
|
137
|
+
# ✅ Add HealthBar to "ui_health" group in editor
|
|
138
|
+
func _ready():
|
|
139
|
+
var health_bar = get_tree().get_first_node_in_group("ui_health")
|
|
140
|
+
|
|
141
|
+
# ✅ Or use signals
|
|
142
|
+
signal health_changed(current: float, max: float)
|
|
143
|
+
|
|
144
|
+
func take_damage(amount: float):
|
|
145
|
+
health -= amount
|
|
146
|
+
health_changed.emit(health, max_health) # UI listens to this
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Common Pitfall: Using get_node() in @onready with Complex Paths
|
|
150
|
+
|
|
151
|
+
**Problem:**
|
|
152
|
+
```gdscript
|
|
153
|
+
@onready var camera = get_node("../../CameraPivot/Camera3D") # ❌ Error-prone
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Solution: Find node by type or group**
|
|
157
|
+
```gdscript
|
|
158
|
+
# ✅ Find by type
|
|
159
|
+
func _get_camera() -> Camera3D:
|
|
160
|
+
var current = get_parent()
|
|
161
|
+
while current:
|
|
162
|
+
if current is Camera3D:
|
|
163
|
+
return current
|
|
164
|
+
for child in current.get_children():
|
|
165
|
+
if child is Camera3D:
|
|
166
|
+
return child
|
|
167
|
+
current = current.get_parent()
|
|
168
|
+
return null
|
|
169
|
+
|
|
170
|
+
# ✅ Or add camera to "camera" group and find it
|
|
171
|
+
func _ready():
|
|
172
|
+
var camera = get_tree().get_first_node_in_group("main_camera")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Signal Connection Issues
|
|
176
|
+
|
|
177
|
+
### Common Pitfall: Connecting Signals in Wrong Order
|
|
178
|
+
|
|
179
|
+
**Problem:**
|
|
180
|
+
```gdscript
|
|
181
|
+
func _ready():
|
|
182
|
+
$Button.pressed.connect(_on_button_pressed)
|
|
183
|
+
$Button.pressed.emit() # ❌ Connection might not be active yet
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Solution: Wait one frame or use call_deferred**
|
|
187
|
+
```gdscript
|
|
188
|
+
func _ready():
|
|
189
|
+
$Button.pressed.connect(_on_button_pressed)
|
|
190
|
+
await get_tree().process_frame
|
|
191
|
+
$Button.pressed.emit() # ✅ Connection is active
|
|
192
|
+
|
|
193
|
+
# Or
|
|
194
|
+
func _ready():
|
|
195
|
+
$Button.pressed.connect(_on_button_pressed)
|
|
196
|
+
$Button.pressed.emit.call_deferred() # ✅ Emits after _ready() completes
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Common Pitfall: Memory Leaks from Signal Connections
|
|
200
|
+
|
|
201
|
+
**Problem:**
|
|
202
|
+
```gdscript
|
|
203
|
+
func _ready():
|
|
204
|
+
some_node.signal_name.connect(callback)
|
|
205
|
+
# ❌ If this node is freed but some_node remains, connection persists
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Solution: Disconnect in cleanup or use weak references**
|
|
209
|
+
```gdscript
|
|
210
|
+
var connected_node: Node
|
|
211
|
+
|
|
212
|
+
func _ready():
|
|
213
|
+
connected_node = some_node
|
|
214
|
+
connected_node.signal_name.connect(callback)
|
|
215
|
+
|
|
216
|
+
func _exit_tree():
|
|
217
|
+
if connected_node and connected_node.signal_name.is_connected(callback):
|
|
218
|
+
connected_node.signal_name.disconnect(callback)
|
|
219
|
+
|
|
220
|
+
# Or use one-shot connections
|
|
221
|
+
func _ready():
|
|
222
|
+
some_node.signal_name.connect(callback, CONNECT_ONE_SHOT)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Resource Loading and Modification
|
|
226
|
+
|
|
227
|
+
### Common Pitfall: Modifying Shared Resources
|
|
228
|
+
|
|
229
|
+
**Problem:**
|
|
230
|
+
```gdscript
|
|
231
|
+
# item_resource.tres is shared across all instances
|
|
232
|
+
@export var item: ItemResource
|
|
233
|
+
|
|
234
|
+
func _ready():
|
|
235
|
+
item.quantity += 1 # ❌ Modifies the .tres file for ALL instances!
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Solution: Duplicate resources when modifying**
|
|
239
|
+
```gdscript
|
|
240
|
+
@export var item: ItemResource
|
|
241
|
+
var local_item: ItemResource
|
|
242
|
+
|
|
243
|
+
func _ready():
|
|
244
|
+
local_item = item.duplicate() # ✅ Create instance-specific copy
|
|
245
|
+
local_item.quantity += 1 # Only affects this instance
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Common Pitfall: preload() in .tres Files
|
|
249
|
+
|
|
250
|
+
**Problem:**
|
|
251
|
+
```tres
|
|
252
|
+
[resource]
|
|
253
|
+
script = preload("res://script.gd") # ❌ WRONG - .tres files don't support preload()
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Solution: Use ExtResource**
|
|
257
|
+
```tres
|
|
258
|
+
[ext_resource type="Script" path="res://script.gd" id="1"]
|
|
259
|
+
|
|
260
|
+
[resource]
|
|
261
|
+
script = ExtResource("1") # ✅ Correct
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## CharacterBody3D Movement
|
|
265
|
+
|
|
266
|
+
### Common Pitfall: Velocity Not Persisting
|
|
267
|
+
|
|
268
|
+
**Problem:**
|
|
269
|
+
```gdscript
|
|
270
|
+
func _physics_process(delta):
|
|
271
|
+
var velocity = Vector3.ZERO # ❌ Resets velocity every frame!
|
|
272
|
+
velocity.x = input.x * speed
|
|
273
|
+
move_and_slide()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Solution: Use the velocity property**
|
|
277
|
+
```gdscript
|
|
278
|
+
func _physics_process(delta):
|
|
279
|
+
velocity.x = input.x * speed # ✅ Modifies persistent velocity
|
|
280
|
+
velocity.y -= gravity * delta
|
|
281
|
+
move_and_slide()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Common Pitfall: Floor Detection Issues
|
|
285
|
+
|
|
286
|
+
**Problem:**
|
|
287
|
+
```gdscript
|
|
288
|
+
func _physics_process(delta):
|
|
289
|
+
if is_on_floor():
|
|
290
|
+
velocity.y = 0 # ❌ Causes jittering on slopes
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Solution: Only reset vertical velocity when needed**
|
|
294
|
+
```gdscript
|
|
295
|
+
func _physics_process(delta):
|
|
296
|
+
# Apply gravity
|
|
297
|
+
if not is_on_floor():
|
|
298
|
+
velocity.y -= gravity * delta
|
|
299
|
+
|
|
300
|
+
# Jump
|
|
301
|
+
if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
302
|
+
velocity.y = jump_force # ✅ Only set when jumping
|
|
303
|
+
|
|
304
|
+
move_and_slide()
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Transform and Basis Confusion
|
|
308
|
+
|
|
309
|
+
### Common Pitfall: Wrong Direction Vector
|
|
310
|
+
|
|
311
|
+
**Problem:**
|
|
312
|
+
```gdscript
|
|
313
|
+
var forward = transform.basis.z # ❌ This is actually backward in Godot!
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Solution: Negate Z for forward**
|
|
317
|
+
```gdscript
|
|
318
|
+
var forward = -transform.basis.z # ✅ Forward direction
|
|
319
|
+
var right = transform.basis.x # ✅ Right direction
|
|
320
|
+
var up = transform.basis.y # ✅ Up direction
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Common Pitfall: Mixing Local and Global Transforms
|
|
324
|
+
|
|
325
|
+
**Problem:**
|
|
326
|
+
```gdscript
|
|
327
|
+
position += Vector3.FORWARD * speed # ❌ Moves in global forward, not local
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Solution: Use basis to transform direction**
|
|
331
|
+
```gdscript
|
|
332
|
+
position += -transform.basis.z * speed # ✅ Moves in local forward direction
|
|
333
|
+
|
|
334
|
+
# Or use global_transform for global operations
|
|
335
|
+
global_position += Vector3.FORWARD * speed # ✅ Explicitly global
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Input Handling
|
|
339
|
+
|
|
340
|
+
### Common Pitfall: Input Processed in _process() and _physics_process()
|
|
341
|
+
|
|
342
|
+
**Problem:**
|
|
343
|
+
```gdscript
|
|
344
|
+
func _process(delta):
|
|
345
|
+
if Input.is_action_just_pressed("jump"):
|
|
346
|
+
jump() # ❌ Might miss input if physics runs at different rate
|
|
347
|
+
|
|
348
|
+
func _physics_process(delta):
|
|
349
|
+
# Physics code
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Solution: Handle input where it's used**
|
|
353
|
+
```gdscript
|
|
354
|
+
# For movement: use _physics_process
|
|
355
|
+
func _physics_process(delta):
|
|
356
|
+
if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
357
|
+
velocity.y = jump_force
|
|
358
|
+
move_and_slide()
|
|
359
|
+
|
|
360
|
+
# For UI/non-physics: use _input or _unhandled_input
|
|
361
|
+
func _input(event):
|
|
362
|
+
if event.is_action_pressed("menu"):
|
|
363
|
+
open_menu()
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Common Pitfall: Mouse Capture Issues
|
|
367
|
+
|
|
368
|
+
**Problem:**
|
|
369
|
+
```gdscript
|
|
370
|
+
func _ready():
|
|
371
|
+
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
372
|
+
|
|
373
|
+
# ❌ Escape key doesn't work - mouse is captured forever
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Solution: Toggle mouse mode with escape**
|
|
377
|
+
```gdscript
|
|
378
|
+
func _input(event):
|
|
379
|
+
if event.is_action_pressed("ui_cancel"):
|
|
380
|
+
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
|
381
|
+
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
382
|
+
else:
|
|
383
|
+
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Type Safety and Static Typing
|
|
387
|
+
|
|
388
|
+
### Common Pitfall: Weak Typing Hiding Errors
|
|
389
|
+
|
|
390
|
+
**Problem:**
|
|
391
|
+
```gdscript
|
|
392
|
+
var node = get_node("Player")
|
|
393
|
+
node.health = 100 # ❌ No error if Player doesn't have health property
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Solution: Use static typing**
|
|
397
|
+
```gdscript
|
|
398
|
+
var player: Player = get_node("Player") as Player
|
|
399
|
+
if player:
|
|
400
|
+
player.health = 100 # ✅ Error if Player class doesn't have health
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Common Pitfall: Null Checks Missing
|
|
404
|
+
|
|
405
|
+
**Problem:**
|
|
406
|
+
```gdscript
|
|
407
|
+
var target = get_tree().get_first_node_in_group("player")
|
|
408
|
+
var distance = global_position.distance_to(target.global_position) # ❌ Crashes if no player
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Solution: Always check for null**
|
|
412
|
+
```gdscript
|
|
413
|
+
var target = get_tree().get_first_node_in_group("player")
|
|
414
|
+
if target:
|
|
415
|
+
var distance = global_position.distance_to(target.global_position) # ✅ Safe
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Scene Instancing
|
|
419
|
+
|
|
420
|
+
### Common Pitfall: Forgetting to Add Instanced Scene to Tree
|
|
421
|
+
|
|
422
|
+
**Problem:**
|
|
423
|
+
```gdscript
|
|
424
|
+
var scene = preload("res://enemy.tscn")
|
|
425
|
+
var enemy = scene.instantiate()
|
|
426
|
+
enemy.position = spawn_point # ❌ Enemy exists but isn't in the scene tree!
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Solution: Add to tree**
|
|
430
|
+
```gdscript
|
|
431
|
+
var scene = preload("res://enemy.tscn")
|
|
432
|
+
var enemy = scene.instantiate()
|
|
433
|
+
add_child(enemy) # ✅ Add to tree
|
|
434
|
+
enemy.global_position = spawn_point # Use global_position after add_child
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Common Pitfall: Setting Position Before Adding to Tree
|
|
438
|
+
|
|
439
|
+
**Problem:**
|
|
440
|
+
```gdscript
|
|
441
|
+
var enemy = scene.instantiate()
|
|
442
|
+
enemy.position = Vector3(10, 0, 10) # ❌ Local position, might be wrong after add_child
|
|
443
|
+
add_child(enemy)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
**Solution: Set global position after adding**
|
|
447
|
+
```gdscript
|
|
448
|
+
var enemy = scene.instantiate()
|
|
449
|
+
add_child(enemy)
|
|
450
|
+
enemy.global_position = Vector3(10, 0, 10) # ✅ Global position after in tree
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Tween Issues
|
|
454
|
+
|
|
455
|
+
### Common Pitfall: Creating Tweens Without Cleanup
|
|
456
|
+
|
|
457
|
+
**Problem:**
|
|
458
|
+
```gdscript
|
|
459
|
+
func animate():
|
|
460
|
+
var tween = create_tween()
|
|
461
|
+
tween.tween_property(self, "position", target, 1.0)
|
|
462
|
+
# ❌ If called repeatedly, creates multiple tweens conflicting
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Solution: Kill previous tweens or check is_valid()**
|
|
466
|
+
```gdscript
|
|
467
|
+
var current_tween: Tween
|
|
468
|
+
|
|
469
|
+
func animate():
|
|
470
|
+
if current_tween and current_tween.is_valid():
|
|
471
|
+
current_tween.kill() # ✅ Stop previous animation
|
|
472
|
+
|
|
473
|
+
current_tween = create_tween()
|
|
474
|
+
current_tween.tween_property(self, "position", target, 1.0)
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Common Pitfall: Tween Callbacks Not Firing
|
|
478
|
+
|
|
479
|
+
**Problem:**
|
|
480
|
+
```gdscript
|
|
481
|
+
func swing_weapon():
|
|
482
|
+
var tween = create_tween()
|
|
483
|
+
tween.tween_property(weapon, "rotation", target_rotation, 0.5)
|
|
484
|
+
tween.tween_callback(finish_swing) # ❌ Might not fire if tween is killed
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Solution: Use await or check tween validity**
|
|
488
|
+
```gdscript
|
|
489
|
+
func swing_weapon():
|
|
490
|
+
var tween = create_tween()
|
|
491
|
+
tween.tween_property(weapon, "rotation", target_rotation, 0.5)
|
|
492
|
+
await tween.finished # ✅ Waits for tween to complete
|
|
493
|
+
finish_swing()
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Summary: Quick Checklist
|
|
497
|
+
|
|
498
|
+
When encountering issues, check:
|
|
499
|
+
|
|
500
|
+
- [ ] Is `@onready` causing timing issues? → Use dynamic getters or initialize in `_ready()`
|
|
501
|
+
- [ ] Are you accessing parent methods in child's `_ready()`? → Wait a frame or use signals
|
|
502
|
+
- [ ] Is a node reference null? → Check scene tree structure and initialization order
|
|
503
|
+
- [ ] Using `get_node()` with complex paths? → Use groups or find by type
|
|
504
|
+
- [ ] Modifying a shared resource? → Duplicate it first
|
|
505
|
+
- [ ] Movement not working? → Check you're using `velocity` property, not local variable
|
|
506
|
+
- [ ] Direction vector wrong? → Remember `-transform.basis.z` is forward
|
|
507
|
+
- [ ] Tween not working? → Kill previous tweens, use await for callbacks
|
|
508
|
+
- [ ] Input missed? → Process input where it's used (_physics_process for movement)
|
|
509
|
+
- [ ] Null reference error? → Add null checks before accessing properties
|
|
510
|
+
- [ ] Instance not appearing? → Remember to `add_child()` and use `global_position`
|
|
511
|
+
|
|
512
|
+
## When in Doubt
|
|
513
|
+
|
|
514
|
+
1. **Print debug info**: `print()` is your friend
|
|
515
|
+
2. **Check the scene tree**: Use Remote tab in editor while game runs
|
|
516
|
+
3. **Enable visible collision shapes**: Debug → Visible Collision Shapes
|
|
517
|
+
4. **Read error messages carefully**: They often point to the exact issue
|
|
518
|
+
5. **Consult official docs**: https://docs.godotengine.org/
|