create-component
Skill2 starsChanged 6 months agoMITView on GitHub
Generate reusable component scenes and scripts — HealthComponent, HurtboxComponent, HitboxComponent, MoveComponent, InteractableComponent, InventoryComponent. Composition-first, signal-driven.
12 other projects use a version of this.
Adopted by
Most recently taken up by
- agentconfig/agentconfig.orgtheir own version, first seen Nov 8, 2026
- buildpad-ai/skillstheir own version, first seen Oct 16, 2026
- MUZEEBURRAHAMAN/forgetheir own version, first seen Oct 15, 2026
- monegit/agent-skillstheir own version, first seen Oct 13, 2026
- Endava/BEEQtheir own version, first seen Oct 8, 2026
- thoughtfulllc/gridlandtheir own version, first seen Oct 8, 2026
Other versions12 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 12
- gluestack/gluestack-ui★ 5.3ksame name
- DataDog/datadog-agent★ 3.8ksame name
- compose-miuix-ui/miuix★ 1.6ksame name
- thoughtfulllc/gridland★ 363same name
- Endava/BEEQ★ 162same name
- incluud/astro-agent-skills★ 10same name
- agentconfig/agentconfig.org★ 7same name
- viknesh20-20/claude-code-tool-kit★ 7same name
- buildpad-ai/skills★ 1same name
- MUZEEBURRAHAMAN/forge★ 1same name
2 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository33 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- bootstrap-godot-projectSkill
- codebase-surveySkill
- create-autoloadSkill
- create-resourceSkill
- create-sceneSkill
- create-state-machineSkill
- export-configSkill
- feature-planSkill
- feature-specSkill
- game-brainstormingSkill
- gdd-writerSkill
- genre-pack-3d-actionSkill
- genre-pack-platformerSkill
- genre-pack-topdownSkill
- genre-pack-turnbasedSkill
- godot-patternsSkill
- gut-test-writerSkill
- networking-foundationSkill
- performance-auditSkill
- save-schema-migrationSkill
- setup-collision-layersSkill
- setup-git-godotSkill
- setup-input-mapSkill
- setup-localizationSkill
- setup-save-systemSkill
- sfx-generatorSkill
- shader-writerSkill
- subagent-dev-modeSkill
- ui-patterns-godotSkill
- update-docsSkill
- using-godot-superpowersSkill
- writing-game-planSkill
The file
Files
Create Component
Generate a reusable component as a scene + script. Composition pattern: drop the component as a child of the entity that should "have" the behavior.
Save scenes to scenes/components/<name>.tscn and scripts to scripts/components/<name>.gd.
HealthComponent
Decoupled HP from the entity. Any entity that can take damage drops one in.
scripts/components/health_component.gd:
class_name HealthComponent
extends Node
signal health_changed(old: int, new: int)
signal died
signal revived
@export var max_health: int = 100
@export var invulnerable: bool = false
var current_health: int
func _ready() -> void:
current_health = max_health
func take_damage(amount: int, _source: Node = null) -> void:
if invulnerable or current_health <= 0:
return
var prev := current_health
current_health = maxi(0, current_health - amount)
if current_health != prev:
health_changed.emit(prev, current_health)
if current_health == 0:
died.emit()
func heal(amount: int) -> void:
if current_health <= 0:
return
var prev := current_health
current_health = mini(max_health, current_health + amount)
if current_health != prev:
health_changed.emit(prev, current_health)
func revive(amount: int = -1) -> void:
var was_dead := current_health == 0
current_health = max_health if amount < 0 else mini(max_health, amount)
if was_dead:
revived.emit()
health_changed.emit(0, current_health)
func get_health_ratio() -> float:
return float(current_health) / float(max_health) if max_health > 0 else 0.0scenes/components/health_component.tscn: Node root, attach the script.
HurtboxComponent (2D and 3D variants)
Receives damage. Attach as child Area2D / Area3D with a CollisionShape sibling.
scripts/components/hurtbox_component_2d.gd:
class_name HurtboxComponent2D
extends Area2D
## Receives damage. Forwards to a HealthComponent reference.
@export var health_component: HealthComponent
@export var damage_modifier: float = 1.0 ## e.g. 0.5 = takes half damage
func _ready() -> void:
area_entered.connect(_on_area_entered)
func _on_area_entered(area: Area2D) -> void:
if area is HitboxComponent2D and health_component:
var dmg := int(area.damage * damage_modifier)
health_component.take_damage(dmg, area.owner)scripts/components/hurtbox_component_3d.gd: same but Area3D / HitboxComponent3D.
HitboxComponent (2D and 3D)
Deals damage when its area overlaps an opposing hurtbox.
class_name HitboxComponent2D
extends Area2D
@export var damage: int = 10
@export var knockback: float = 200.0
@export var disabled_on_start: bool = false
func _ready() -> void:
monitoring = false # off by default; enable during attacks
if disabled_on_start:
return
enable()
func enable() -> void:
monitoring = true
func disable() -> void:
monitoring = falsePair Hurtbox + Hitbox with proper collision layers (see setup-collision-layers):
- HurtboxComponent on the receiver, layer =
PlayerHurtboxorEnemyHurtbox - HitboxComponent on the dealer, layer =
PlayerHitboxorEnemyHitbox, mask = opposing hurtbox layer
MoveComponent (genre-agnostic skeleton)
This foundation skill ships only the generic MoveComponent — a thin CharacterBody2D driver that owns horizontal velocity and the move_and_slide() call. Genre-specific behavior (gravity tuning, jump shaping, coyote / buffer windows, dashes, wall jumps, top-down 8-direction movement, 3D camera-relative motion) lives in the matching genre pack.
class_name MoveComponent2D
extends Node
## Genre-agnostic 2D mover. Drives a CharacterBody2D parent's horizontal velocity.
## Vertical motion (gravity, jump, fall behavior) is intentionally NOT handled here —
## a genre pack (or your own subclass) layers it on top via `physics_step`.
@export var max_speed: float = 250.0
@export var acceleration: float = 1500.0
@export var friction: float = 1500.0
var body: CharacterBody2D
func _ready() -> void:
body = get_parent() as CharacterBody2D
assert(body, "MoveComponent2D parent must be CharacterBody2D")
func physics_step(_delta: float, input_x: float) -> void:
if absf(input_x) > 0.0:
body.velocity.x = move_toward(body.velocity.x, input_x * max_speed, acceleration * _delta)
else:
body.velocity.x = move_toward(body.velocity.x, 0.0, friction * _delta)
body.move_and_slide()For 3D, swap CharacterBody2D → CharacterBody3D and use Vector3 velocity.
Genre specializations
| Genre | Skill | Adds on top of MoveComponent |
| 2D platformer | genre-pack-platformer | gravity + variable jump + coyote time + jump buffer + wall jump / dash |
| 2D top-down | genre-pack-topdown | 8-direction movement, no gravity, optional A* path follow |
| 3D action | genre-pack-3d-action | camera-relative movement, SpringArm follow, lock-on facing |
| Turn-based | genre-pack-turnbased | tile-snap movement, action-queue integration |
Pick exactly one genre pack per project (per the writing-game-plan sequencing rules) before tuning player feel.
InteractableComponent
Marks an entity as interactable. The player checks for nearby InteractableComponent and calls interact().
class_name InteractableComponent
extends Area2D
signal interacted(by: Node)
@export var prompt: String = "Interact"
@export var one_shot: bool = false
var _consumed: bool = false
func interact(by: Node) -> void:
if _consumed:
return
interacted.emit(by)
if one_shot:
_consumed = truePlayer code:
func _try_interact() -> void:
var bodies := $InteractRange.get_overlapping_areas()
for area in bodies:
if area is InteractableComponent:
area.interact(self)
returnInventoryComponent
class_name InventoryComponent
extends Node
signal item_added(item: Item, count: int)
signal item_removed(item: Item, count: int)
signal inventory_changed
@export var max_slots: int = 20
var _slots: Array[Dictionary] = [] # each: {"item": Item, "count": int}
func add(item: Item, count: int = 1) -> int:
var remaining := count
if item.max_stack > 1:
for slot in _slots:
if slot.item == item and slot.count < item.max_stack:
var add_count := mini(item.max_stack - slot.count, remaining)
slot.count += add_count
remaining -= add_count
if remaining == 0:
break
while remaining > 0 and _slots.size() < max_slots:
var add_count := mini(item.max_stack, remaining) if item.max_stack > 0 else remaining
_slots.append({"item": item, "count": add_count})
remaining -= add_count
if remaining < count:
item_added.emit(item, count - remaining)
inventory_changed.emit()
return count - remaining
func remove(item: Item, count: int = 1) -> int:
var remaining := count
for i in range(_slots.size() - 1, -1, -1):
if _slots[i].item != item:
continue
var taken := mini(_slots[i].count, remaining)
_slots[i].count -= taken
remaining -= taken
if _slots[i].count == 0:
_slots.remove_at(i)
if remaining == 0:
break
if remaining < count:
item_removed.emit(item, count - remaining)
inventory_changed.emit()
return count - remaining
func has(item: Item, count: int = 1) -> bool:
var total := 0
for slot in _slots:
if slot.item == item:
total += slot.count
if total >= count:
return true
return falseItem is a custom Resource — see create-resource skill.
Composition example
Player scene:
Player (CharacterBody2D)
├── Sprite2D
├── CollisionShape2D
├── Hurtbox (HurtboxComponent2D)
│ └── CollisionShape2D
├── Hitbox (HitboxComponent2D) -> enabled during attack states
│ └── CollisionShape2D
├── HealthComponent
├── MoveComponent -> generic 2D mover (this skill); a genre pack may swap it
├── InventoryComponent
└── StateMachine -> states are project- and genre-specific; a platformer
├── Idle would add Jump / Fall / WallSlide, a top-down would
└── Run add Aim / Roll, etc. — see the matching genre pack.Player script wires components in _ready:
@onready var _health: HealthComponent = $HealthComponent
@onready var _hurtbox: HurtboxComponent2D = $Hurtbox
@onready var _move: MoveComponent2D = $MoveComponent
func _ready() -> void:
_hurtbox.health_component = _health
_health.died.connect(_on_died)
func _physics_process(delta: float) -> void:
# Genre-agnostic input drive. A genre pack will add gravity, jump, dash, etc.
var input_x := Input.get_axis("move_left", "move_right")
_move.physics_step(delta, input_x)Notes
- Component nodes own one concern. Don't bundle health + inventory into one node.
- Components communicate via signals or direct references injected at
_ready— no autoload coupling. - For state-bearing components (HealthComponent), implement
save_data()/load_data()if the parent is inpersistgroup.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

