Back to skills
SKILL.md
Add Mechanic
ASecurityAdd game mechanics with correct GDScript 4.x patterns -- movement, health, inventory, save/load
- 4 stars
- 0 votes
- 0 copies
- 1 view
- Added September 2, 2026
Works with
Security analysis
100/100npx -y skills add n24q02m/claude-plugins --skill add-mechanic --agent claude-codeAre you the author of Add Mechanic?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/n24q02m-add-mechanic)---
name: add-mechanic
description: Add game mechanics with correct GDScript 4.x patterns -- movement, health, inventory, save/load
argument-hint: "[mechanic type: movement, health, inventory, save/load]"
---
# Add Mechanic
Add game mechanics using correct GDScript 4.x syntax. Prevents common LLM mistakes with outdated GDScript 3.x patterns.
## GDScript 4.x Syntax Rules
These changed from Godot 3 to 4. LLMs frequently generate the OLD syntax:
| Correct (GDScript 4.x) | Wrong (GDScript 3.x -- will error) |
|---|---|
| `@export var speed: float = 200.0` | `export var speed = 200.0` |
| `@onready var sprite = $Sprite2D` | `onready var sprite = $Sprite2D` |
| `signal health_changed(new_hp: int)` | `signal health_changed` (no typed params) |
| `func _ready() -> void:` | `func _ready():` (return type optional but preferred) |
| `velocity = Vector2(...)` then `move_and_slide()` | `move_and_slide(velocity, Vector2.UP)` (args removed in 4.x) |
| `super()` | `.method()` for parent calls |
| `await get_tree().create_timer(1.0).timeout` | `yield(get_tree().create_timer(1.0), "timeout")` |
| `%UniqueNode` | `get_node("path/to/node")` when unique name is set |
## Movement Patterns
### Platformer Movement
```gdscript
extends CharacterBody2D
@export var speed: float = 300.0
@export var jump_velocity: float = -400.0
@export var gravity: float = 980.0
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity.y += gravity * delta
# Jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# Horizontal movement
var direction := Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
move_and_slide()
```
Key: `move_and_slide()` takes NO arguments in Godot 4. Velocity is set on the `velocity` property directly.
### Top-Down Movement
```gdscript
extends CharacterBody2D
@export var speed: float = 200.0
func _physics_process(_delta: float) -> void:
var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()
```
### Point-and-Click Movement
```gdscript
extends CharacterBody2D
@export var speed: float = 200.0
var target_position: Vector2
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed:
target_position = get_global_mouse_position()
func _physics_process(_delta: float) -> void:
if position.distance_to(target_position) > 5.0:
velocity = position.direction_to(target_position) * speed
else:
velocity = Vector2.ZERO
move_and_slide()
```
## Health/Damage System
```gdscript
extends Node
class_name HealthComponent
signal health_changed(new_hp: int, max_hp: int)
signal died
@export var max_hp: int = 100
var current_hp: int
func _ready() -> void:
current_hp = max_hp
func take_damage(amount: int) -> void:
current_hp = maxi(current_hp - amount, 0)
health_changed.emit(current_hp, max_hp)
if current_hp <= 0:
died.emit()
func heal(amount: int) -> void:
current_hp = mini(current_hp + amount, max_hp)
health_changed.emit(current_hp, max_hp)
```
Usage: Add as child node of any entity. Connect `died` signal to handle death logic.
Pattern: Component node (not inheritance) -- allows reuse on Player, Enemy, Destructible, etc.
## Inventory System
```gdscript
extends Node
class_name Inventory
signal item_added(item: ItemResource)
signal item_removed(item: ItemResource)
@export var max_slots: int = 20
var items: Array[ItemResource] = []
func add_item(item: ItemResource) -> bool:
if items.size() >= max_slots:
return false
items.append(item)
item_added.emit(item)
return true
func remove_item(item: ItemResource) -> void:
items.erase(item)
item_removed.emit(item)
func has_item(item_name: String) -> bool:
return items.any(func(i: ItemResource) -> bool: return i.name == item_name)
```
Item resource:
```gdscript
extends Resource
class_name ItemResource
@export var name: String
@export var description: String
@export var icon: Texture2D
@export var stackable: bool = false
@export var max_stack: int = 1
```
Use `Resource` (not Node) for items -- they are data, not scene objects.
## Save/Load System
### ConfigFile (simple key-value)
```gdscript
extends Node
const SAVE_PATH := "user://save_data.cfg"
func save_game() -> void:
var config := ConfigFile.new()
config.set_value("player", "position", player.global_position)
config.set_value("player", "health", player.health)
config.set_value("game", "level", current_level)
config.save(SAVE_PATH)
func load_game() -> void:
var config := ConfigFile.new()
if config.load(SAVE_PATH) != OK:
return # No save file
player.global_position = config.get_value("player", "position", Vector2.ZERO)
player.health = config.get_value("player", "health", 100)
current_level = config.get_value("game", "level", 1)
```
### JSON (complex/nested data)
```gdscript
func save_game_json() -> void:
var data := {
"player": { "x": player.global_position.x, "y": player.global_position.y, "hp": player.health },
"inventory": items.map(func(i: ItemResource) -> String: return i.name),
"timestamp": Time.get_datetime_string_from_system()
}
var file := FileAccess.open("user://save.json", FileAccess.WRITE)
file.store_string(JSON.stringify(data, "\t"))
func load_game_json() -> void:
if not FileAccess.file_exists("user://save.json"):
return
var file := FileAccess.open("user://save.json", FileAccess.READ)
var data: Dictionary = JSON.parse_string(file.get_as_text())
player.global_position = Vector2(data.player.x, data.player.y)
```
Use `user://` path (NOT `res://`) for save files -- `res://` is read-only in exported games.
## Implementation Steps
1. **Identify mechanic** from user description
2. **Create script** using the appropriate template above:
```
scripts(action="create", script_path="res://scripts/<name>.gd", content="...")
```
3. **Attach to scene node** if needed:
```
nodes(action="set_property", scene_path="<scene>.tscn", name="<node>", property="script", value="res://scripts/<name>.gd")
```
4. **Add companion nodes** (e.g., HealthComponent as child node)
5. **Connect signals** in the scene or via script
6. **Test**: `project(action="run", scene_path="<scene>.tscn")`
## When to Use
- Adding player movement to a character scene
- Implementing health/damage for any entity
- Setting up an inventory system
- Adding save/load functionality
- Any gameplay mechanic that needs correct GDScript 4.x patterns
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.