Instruction file imported from nguyenthienthanh/aura-frog-cursor (
.cursor/rules/skills/experts/godot-expert.mdc). Copyright stays with the author.
Godot Expert
Type: Expert Skill
Agent: game-developer
Applies to: Godot 4.x
Overview
Comprehensive Godot game development patterns covering project structure, scene composition, GDScript best practices, physics, input handling, UI, animation, audio, performance optimization, multi-platform export, and testing with GDUnit.
1. Project Structure
res://
├── project.godot
├── scenes/ # .tscn files (player/, enemies/, levels/, ui/)
├── scripts/ # .gd files (mirrors scenes/ structure)
├── assets/ # sprites/, models/, audio/, fonts/, shaders/
├── autoload/ # Singleton scripts (globals.gd, events.gd)
├── resources/ # .tres files (themes/, data/)
├── addons/ # Plugins (gdunit4/)
└── test/ # GDUnit tests
Naming Conventions
naming[6]{type,pattern,example}:
Scenes,snake_case.tscn,player_controller.tscn
Scripts,snake_case.gd,player_controller.gd
Classes,PascalCase,PlayerController
Functions,snake_case,move_and_slide()
Variables,snake_case,max_health
Constants,SCREAMING_SNAKE,MAX_SPEED
2. Scene Composition
# Composition over inheritance
# Player (CharacterBody2D)
# ├── CollisionShape2D
# ├── Sprite2D
# ├── AnimationPlayer
# ├── StateMachine (Node)
# │ ├── IdleState / RunState / JumpState
# ├── Hitbox (Area2D)
# └── Hurtbox (Area2D)
# Preload for frequently used scenes
const BulletScene := preload("res://scenes/projectiles/bullet.tscn")
func shoot() -> void:
var bullet := BulletScene.instantiate() as Bullet
bullet.global_position = $Muzzle.global_position
get_tree().current_scene.add_child(bullet)
3. GDScript Patterns
Type Hints (ALWAYS USE)
var health: int = 100
var speed: float = 200.0
var items: Array[Item] = []
func calculate_damage(base: int, multiplier: float) -> int:
return int(base * multiplier)
Export Variables
@export var max_health: int = 100
@export_range(0, 100, 1) var health: int = 100
@export_enum("Warrior", "Mage", "Rogue") var player_class: String
@export_group("Movement")
@export var walk_speed: float = 100.0
@export var jump_force: float = 400.0
Signals
signal health_changed(new_health: int, max_health: int)
signal died
func take_damage(amount: int) -> void:
health -= amount
health_changed.emit(health, max_health)
if health <= 0:
died.emit()
func _ready() -> void:
$Button.pressed.connect(_on_button_pressed)
Onready & Async
@onready var sprite: Sprite2D = $Sprite2D
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func play_death_animation() -> void:
$AnimationPlayer.play("death")
await $AnimationPlayer.animation_finished
queue_free()
Singletons (Autoload)
# events.gd - Event bus pattern
extends Node
signal player_died
signal coin_collected(amount: int)
# Usage anywhere:
Events.player_died.emit()
Events.coin_collected.connect(_on_coin_collected)
4. Physics & Collision
CharacterBody2D Movement
extends CharacterBody2D
const SPEED := 300.0
const JUMP_VELOCITY := -400.0
const GRAVITY := 980.0
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += GRAVITY * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
var direction := Input.get_axis("move_left", "move_right")
velocity.x = direction * SPEED if direction else move_toward(velocity.x, 0, SPEED)
move_and_slide()
Hitbox/Hurtbox Pattern
# Hitbox
extends Area2D
class_name Hitbox
@export var damage: int = 10
func _ready() -> void:
area_entered.connect(_on_area_entered)
func _on_area_entered(area: Area2D) -> void:
if area is Hurtbox:
area.take_hit(self)
5. Animation & Audio
Tweens
func bounce_in() -> void:
var tween := create_tween()
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC)
tween.tween_property(self, "scale", Vector2.ONE, 0.5).from(Vector2.ZERO)
func fade_and_move() -> void:
var tween := create_tween().set_parallel(true)
tween.tween_property(self, "modulate:a", 0.0, 1.0)
tween.tween_property(self, "position:y", position.y - 50, 1.0)
tween.chain().tween_callback(queue_free)
6. Performance
Object Pooling
class_name ObjectPool
extends Node
var _pool: Array[Node] = []
var _scene: PackedScene
func get_object() -> Node:
for obj in _pool:
if not obj.visible:
obj.show()
obj.set_process(true)
return obj
var new_obj := _scene.instantiate()
_pool.append(new_obj)
get_parent().add_child(new_obj)
return new_obj
7. Export Targets
platforms[6]{name,format,requirements}:
HTML5,.html+.wasm,WebGL 2.0 browser
Android,.apk/.aab,Android SDK + JDK
iOS,.ipa,Xcode + Apple Developer
Windows,.exe,Windows SDK (optional)
macOS,.app/.dmg,Xcode CLI tools
Linux,Binary,None
8. Testing with GDUnit
extends GdUnitTestSuite
var player: Player
func before_test() -> void:
player = auto_free(preload("res://scenes/player/player.tscn").instantiate())
add_child(player)
func test_initial_health() -> void:
assert_int(player.health).is_equal(100)
func test_take_damage() -> void:
player.take_damage(25)
assert_int(player.health).is_equal(75)
Quick Reference
patterns[8]{name,use_case}:
State Machine,Complex entity behavior
Object Pool,Frequent spawn/despawn
Event Bus,Decoupled communication
Resource,Shared data/configuration
Autoload,Global managers
Scene Inheritance,Enemy variants
Composition,Modular abilities
Command,Input/action replay
extensions[6]{ext,purpose}:
.gd,GDScript source
.tscn,Scene (text format)
.scn,Scene (binary format)
.tres,Resource (text)
.res,Resource (binary)
.import,Import settings
Version: 1.11.0 Last Updated: 2026-02-13