Make some test with terrain generator
This commit is contained in:
461
.agents/skills/godot-gdscript/SKILL.md
Normal file
461
.agents/skills/godot-gdscript/SKILL.md
Normal file
@@ -0,0 +1,461 @@
|
||||
---
|
||||
name: godot-gdscript
|
||||
description: "Develops GDScript code for Godot 4.6 projects. Use when writing, editing, or debugging GDScript files, creating nodes, signals, exports, or any Godot game development task."
|
||||
---
|
||||
|
||||
# Godot 4.6 GDScript Development
|
||||
|
||||
Write idiomatic GDScript for Godot 4.6 following these conventions and language features.
|
||||
|
||||
## File Structure
|
||||
|
||||
```gdscript
|
||||
@tool # Optional: runs in editor
|
||||
@icon("res://path/to/icon.svg") # Optional: custom icon
|
||||
class_name MyClass # Optional: registers global class
|
||||
extends BaseClass # Required (defaults to RefCounted)
|
||||
|
||||
## Signals
|
||||
signal health_changed(old_value, new_value)
|
||||
|
||||
## Enums
|
||||
enum State { IDLE, RUNNING, JUMPING }
|
||||
|
||||
## Constants
|
||||
const MAX_SPEED = 100.0
|
||||
|
||||
## Exports (inspector properties)
|
||||
@export var speed: float = 50.0
|
||||
@export_range(0, 100) var health: int = 100
|
||||
|
||||
## Public variables
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
|
||||
## Private variables (convention: prefix with _)
|
||||
var _internal_state: int = 0
|
||||
|
||||
## @onready variables (initialized in _ready)
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
|
||||
## Static variables
|
||||
static var instance_count: int = 0
|
||||
|
||||
## Built-in virtual methods
|
||||
func _init():
|
||||
pass
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
## Public methods
|
||||
func take_damage(amount: int) -> void:
|
||||
pass
|
||||
|
||||
## Private methods
|
||||
func _calculate_something() -> float:
|
||||
return 0.0
|
||||
|
||||
## Static methods
|
||||
static func get_instance_count() -> int:
|
||||
return instance_count
|
||||
```
|
||||
|
||||
## Type System
|
||||
|
||||
### Type Hints
|
||||
```gdscript
|
||||
var my_int: int = 5
|
||||
var my_string: String = "hello"
|
||||
var my_node: Node2D = null
|
||||
var inferred := Vector2(1, 2) # Type inferred as Vector2
|
||||
|
||||
func my_func(param: int, optional: String = "default") -> bool:
|
||||
return true
|
||||
```
|
||||
|
||||
### Typed Arrays and Dictionaries
|
||||
```gdscript
|
||||
var int_array: Array[int] = [1, 2, 3]
|
||||
var node_array: Array[Node] = []
|
||||
var typed_dict: Dictionary[String, int] = {"a": 1, "b": 2}
|
||||
```
|
||||
|
||||
### Casting
|
||||
```gdscript
|
||||
var node := $Sprite as Sprite2D # Returns null if wrong type
|
||||
if node is Sprite2D:
|
||||
node.texture = some_texture
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Signals
|
||||
```gdscript
|
||||
signal damage_taken(amount: int)
|
||||
|
||||
func take_damage(amount: int) -> void:
|
||||
health -= amount
|
||||
damage_taken.emit(amount)
|
||||
|
||||
# Connecting signals
|
||||
func _ready():
|
||||
$Button.pressed.connect(_on_button_pressed)
|
||||
# With bound arguments
|
||||
$Enemy.died.connect(_on_enemy_died.bind(enemy_name))
|
||||
|
||||
func _on_button_pressed():
|
||||
pass
|
||||
```
|
||||
|
||||
### Properties (Setters/Getters)
|
||||
```gdscript
|
||||
var health: int = 100:
|
||||
get:
|
||||
return health
|
||||
set(value):
|
||||
health = clamp(value, 0, max_health)
|
||||
health_changed.emit(health)
|
||||
```
|
||||
|
||||
### Export Annotations
|
||||
```gdscript
|
||||
@export var speed: float = 10.0
|
||||
@export_range(0, 100, 1) var health: int = 100
|
||||
@export_enum("Walk", "Run", "Jump") var movement: int
|
||||
@export_file("*.png") var texture_path: String
|
||||
@export_dir var save_directory: String
|
||||
@export_node_path("Sprite2D", "Sprite3D") var sprite_path: NodePath
|
||||
@export_flags("Fire", "Water", "Earth", "Wind") var elements: int
|
||||
@export_group("Movement")
|
||||
@export var walk_speed: float
|
||||
@export var run_speed: float
|
||||
@export_subgroup("Physics")
|
||||
@export var gravity: float
|
||||
```
|
||||
|
||||
### Node References
|
||||
```gdscript
|
||||
@onready var player := $Player as CharacterBody2D
|
||||
@onready var unique_node := %UniqueNodeName # Scene-unique node
|
||||
|
||||
# Get nodes dynamically
|
||||
var child = get_node("Path/To/Child")
|
||||
var parent = get_parent()
|
||||
```
|
||||
|
||||
### Awaiting Signals/Coroutines
|
||||
```gdscript
|
||||
func async_operation() -> void:
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
print("1 second passed")
|
||||
|
||||
var result = await some_async_func()
|
||||
print(result)
|
||||
```
|
||||
|
||||
### Match Statements
|
||||
```gdscript
|
||||
match state:
|
||||
State.IDLE:
|
||||
play_idle_animation()
|
||||
State.RUNNING:
|
||||
play_run_animation()
|
||||
_:
|
||||
push_warning("Unknown state")
|
||||
|
||||
# Pattern matching with guards
|
||||
match point:
|
||||
[var x, var y] when x == y:
|
||||
print("On diagonal")
|
||||
[var x, var y]:
|
||||
print("Point: ", x, ", ", y)
|
||||
```
|
||||
|
||||
### Lambda Functions
|
||||
```gdscript
|
||||
var double = func(x): return x * 2
|
||||
print(double.call(5)) # 10
|
||||
|
||||
# With type hints
|
||||
var add: Callable = func(a: int, b: int) -> int: return a + b
|
||||
```
|
||||
|
||||
### Variadic Functions (4.5+)
|
||||
```gdscript
|
||||
func log_values(prefix: String, ...values: Array) -> void:
|
||||
for v in values:
|
||||
print(prefix, v)
|
||||
```
|
||||
|
||||
## Built-in Types Reference
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `null` | Empty value |
|
||||
| `bool` | `true` or `false` |
|
||||
| `int` | 64-bit integer |
|
||||
| `float` | 64-bit float |
|
||||
| `String` | Unicode string |
|
||||
| `StringName` | Interned string (fast comparison) |
|
||||
| `NodePath` | Path to node/property |
|
||||
| `Vector2/Vector2i` | 2D vector |
|
||||
| `Vector3/Vector3i` | 3D vector |
|
||||
| `Rect2` | 2D rectangle |
|
||||
| `Transform2D/Transform3D` | Transformation matrix |
|
||||
| `Color` | RGBA color |
|
||||
| `Array` | Dynamic array |
|
||||
| `Dictionary` | Key-value map |
|
||||
| `Callable` | Function reference |
|
||||
| `Signal` | Signal reference |
|
||||
|
||||
## Memory Management
|
||||
|
||||
- `RefCounted` subclasses: Automatically freed when no references
|
||||
- `Node` subclasses: Must call `queue_free()` or `free()`
|
||||
- Use `weakref()` to avoid reference cycles
|
||||
|
||||
```gdscript
|
||||
var weak = weakref(some_object)
|
||||
if weak.get_ref():
|
||||
weak.get_ref().do_something()
|
||||
|
||||
if is_instance_valid(node):
|
||||
node.queue_free()
|
||||
```
|
||||
|
||||
## Abstract Classes (4.5+)
|
||||
```gdscript
|
||||
@abstract
|
||||
class_name BaseEnemy
|
||||
extends CharacterBody2D
|
||||
|
||||
@abstract func attack() -> void
|
||||
|
||||
class Zombie extends BaseEnemy:
|
||||
func attack() -> void:
|
||||
# Must implement abstract method
|
||||
melee_attack()
|
||||
```
|
||||
|
||||
## Style Guide
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| File names | snake_case | `yaml_parser.gd` |
|
||||
| Class names | PascalCase | `class_name YAMLParser` |
|
||||
| Node names | PascalCase | `Camera3D`, `Player` |
|
||||
| Functions | snake_case | `func load_level():` |
|
||||
| Variables | snake_case | `var particle_effect` |
|
||||
| Signals | snake_case (past tense) | `signal door_opened` |
|
||||
| Constants | CONSTANT_CASE | `const MAX_SPEED = 200` |
|
||||
| Enum names | PascalCase | `enum Element` |
|
||||
| Enum members | CONSTANT_CASE | `{EARTH, WATER, AIR}` |
|
||||
|
||||
### Private Members
|
||||
Prepend underscore for private/virtual methods and variables:
|
||||
```gdscript
|
||||
var _internal_state: int = 0
|
||||
func _calculate_damage() -> int:
|
||||
return 0
|
||||
```
|
||||
|
||||
### Code Order
|
||||
Organize scripts in this order:
|
||||
1. `@tool` (if needed)
|
||||
2. `@icon`
|
||||
3. `class_name`
|
||||
4. `extends`
|
||||
5. `## Documentation comments`
|
||||
6. Signals
|
||||
7. Enums
|
||||
8. Constants
|
||||
9. `@export` variables
|
||||
10. Public variables
|
||||
11. Private variables
|
||||
12. `@onready` variables
|
||||
13. Virtual methods (`_init`, `_ready`, `_process`, etc.)
|
||||
14. Public methods
|
||||
15. Private methods
|
||||
16. Inner classes
|
||||
|
||||
### Formatting Rules
|
||||
|
||||
**Indentation**: Use tabs, not spaces.
|
||||
|
||||
**Blank lines**:
|
||||
- Two blank lines between functions/class definitions
|
||||
- One blank line to separate logical sections within functions
|
||||
|
||||
**Line length**: Keep under 100 characters (prefer 80).
|
||||
|
||||
**Comments**:
|
||||
```gdscript
|
||||
# Regular comment (space after #)
|
||||
## Documentation comment
|
||||
#var disabled_code # No space for commented code
|
||||
#region Region Name
|
||||
#endregion
|
||||
```
|
||||
|
||||
**Multiline statements** - Use parentheses, put `and`/`or` at line start:
|
||||
```gdscript
|
||||
if (
|
||||
is_valid
|
||||
and has_permission
|
||||
and not is_disabled
|
||||
):
|
||||
do_something()
|
||||
```
|
||||
|
||||
**Enums** - One item per line:
|
||||
```gdscript
|
||||
enum State {
|
||||
IDLE,
|
||||
RUNNING,
|
||||
JUMPING,
|
||||
}
|
||||
```
|
||||
|
||||
## Static Typing
|
||||
|
||||
Static typing catches errors at compile time, improves autocompletion, and enables performance optimizations.
|
||||
|
||||
### Basic Syntax
|
||||
```gdscript
|
||||
# Variables
|
||||
var health: int = 100
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
var player: CharacterBody2D
|
||||
|
||||
# Type inference with :=
|
||||
var speed := 10.0 # Inferred as float
|
||||
var position := Vector2.ZERO # Inferred as Vector2
|
||||
|
||||
# Constants (type optional but recommended for arrays)
|
||||
const MAX_SPEED: float = 200.0
|
||||
const ITEMS: Array[String] = ["sword", "shield"]
|
||||
|
||||
# Function parameters and return types
|
||||
func calculate_damage(base: int, multiplier: float) -> int:
|
||||
return int(base * multiplier)
|
||||
|
||||
# Void return type
|
||||
func apply_damage(amount: int) -> void:
|
||||
health -= amount
|
||||
```
|
||||
|
||||
### Typed Collections
|
||||
```gdscript
|
||||
# Typed arrays
|
||||
var enemies: Array[Enemy] = []
|
||||
var scores: Array[int] = [10, 20, 30]
|
||||
|
||||
# Typed dictionaries (Godot 4.4+)
|
||||
var inventory: Dictionary[String, int] = {"gold": 100}
|
||||
var stats: Dictionary[StringName, float] = {}
|
||||
|
||||
# Typed for loops
|
||||
for enemy: Enemy in enemies:
|
||||
enemy.take_damage(10)
|
||||
|
||||
for name: String in ["Alice", "Bob"]:
|
||||
print(name)
|
||||
```
|
||||
|
||||
### Type Casting
|
||||
```gdscript
|
||||
# Safe casting with 'as' (returns null if wrong type)
|
||||
var sprite := $Sprite as Sprite2D
|
||||
if sprite:
|
||||
sprite.texture = new_texture
|
||||
|
||||
# Type checking with 'is'
|
||||
if node is CharacterBody2D:
|
||||
var body: CharacterBody2D = node
|
||||
body.move_and_slide()
|
||||
|
||||
# Casting node references for safe lines
|
||||
@onready var timer := $Timer as Timer
|
||||
@onready var player := $Player as CharacterBody2D
|
||||
```
|
||||
|
||||
### Safe vs Unsafe Lines
|
||||
The editor marks lines green (safe) or grey (unsafe):
|
||||
```gdscript
|
||||
# Safe - type is known
|
||||
var timer: Timer = $Timer as Timer
|
||||
|
||||
# Unsafe - type could fail at runtime
|
||||
var timer = $Timer # No type info
|
||||
```
|
||||
|
||||
### Typed Global Scope Methods
|
||||
Use typed versions for better performance:
|
||||
```gdscript
|
||||
# Prefer typed versions
|
||||
var node: Node = instance_from_id(id) as Node # Safe
|
||||
var clamped: int = clampi(value, 0, 100) # Typed int clamp
|
||||
var lerped: float = lerpf(0.0, 1.0, 0.5) # Typed float lerp
|
||||
|
||||
# Typed math functions
|
||||
var abs_val: int = absi(-5)
|
||||
var max_val: float = maxf(a, b)
|
||||
var min_val: int = mini(a, b)
|
||||
```
|
||||
|
||||
### Safe Property/Method Access
|
||||
```gdscript
|
||||
# Unsafe - warnings if property doesn't exist on type
|
||||
func process_node(node: Node2D) -> void:
|
||||
if "custom_property" in node:
|
||||
node.custom_property = 10 # UNSAFE_PROPERTY_ACCESS warning
|
||||
|
||||
# Safe - cast first, then access
|
||||
func process_node_safe(node: Node2D) -> void:
|
||||
if node is MyScript:
|
||||
var typed: MyScript = node as MyScript
|
||||
typed.custom_property = 10 # Safe
|
||||
```
|
||||
|
||||
### Limitations
|
||||
```gdscript
|
||||
# Cannot type individual array/dict elements inline
|
||||
var arr = [1, "two", 3.0] # Mixed types - can't specify
|
||||
|
||||
# Nested typed collections not supported
|
||||
var nested: Array[Array[int]] # ERROR - not allowed
|
||||
|
||||
# Use this instead
|
||||
var nested: Array[Array] = [[1, 2], [3, 4]]
|
||||
```
|
||||
|
||||
### Recommended Editor Settings
|
||||
Enable in **Project Settings > Debug > GDScript**:
|
||||
- `UNTYPED_DECLARATION` - Warn on untyped variables
|
||||
- `INFERRED_DECLARATION` - Prefer explicit over inferred types
|
||||
- `UNSAFE_PROPERTY_ACCESS` - Warn on unsafe property access
|
||||
- `UNSAFE_METHOD_ACCESS` - Warn on unsafe method calls
|
||||
|
||||
Enable in **Editor Settings > Text Editor > Completion**:
|
||||
- **Add Type Hints** - Auto-add types on completion
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use static typing** - Improves performance and catches errors
|
||||
2. **Prefer `@onready`** - For node references instead of `_ready()` assignments
|
||||
3. **Use signals** - For decoupled communication between nodes
|
||||
4. **Avoid `get_node()` in `_process()`** - Cache references with `@onready`
|
||||
5. **Use `StringName` for frequent comparisons** - `&"my_string"`
|
||||
6. **Group exports** - Use `@export_group` and `@export_subgroup`
|
||||
7. **Document with `##`** - Double hash for documentation comments
|
||||
8. **Declare locals near first use** - Don't declare at top of function
|
||||
9. **Use past tense for signals** - `health_depleted`, `door_opened`
|
||||
10. **Match file name to class** - `YAMLParser` → `yaml_parser.gd`
|
||||
Reference in New Issue
Block a user