How to use
- Copy the installation command and review the source package before installing it in your own agent environment.
- Check the source document for required tools, credentials and supported hosts.
- Describe your input and desired deliverable; review the output against the source material.
npx skills add https://github.com/mynamekse/godot-gecs --skill gecs-relationshipsBefore you start
- Installation does not include a model subscription, API allowance or permission to access your files.
- Repository stars and installation counts describe activity, not an independent quality or security test.
Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
GECS Relationships in Godot 4.3+
Express links between entities as first-class pairs in GECS v9. A Relationship connects a Source Entity to a Target through a Relation Component. Relationships support arbitrary metadata, stacked instances (e.g. poison stacks), wildcard queries, and graduated cleansing.
Addon: GECS · versionv9· Godot 4.3+ · MIT Related skills: gecs-core for primitives, gecs-queries for relationship queries, gecs-observers for.on_relationship_added().
1. Anatomy of a Relationship
A relationship consists of 3 elements:
- Source: The entity that owns the relationship.
- Relation: A Component instance defining the relationship type (e.g.,
R_ChildOf,R_Attacking). - Target: One of three target types:
- Entity: Linking two entities (e.g.,
attacker -> target). - Component Instance: Storing link-attached data directly on the relationship (e.g.,
unit -> C_Burning.new(10.0)). - Archetype / Class Type: Categorical tagging (e.g.,
unit -> Food).
flowchart LR
E1["Source Entity (e.g., Player)"] -- "Relationship (R_Attacks)" --> E2["Target Entity (e.g., Boss)"]
E1 -- "Relationship (R_StatusEffect)" --> C["Target Component (C_Poison.new(5s))"]
E1 -- "Relationship (R_Likes)" --> A["Target Archetype (Food)"]2. The Three Relationship Types
1. Entity → Entity Link
# e_alice is attacking e_bob
e_alice.add_relationship(Relationship.new(R_Attacks.new(), e_bob))2. Entity → Component Instance (Link Data)
Attach custom data directly to the connection without modifying the entity's direct archetype:
# Apply 50 fire damage over 5 seconds directly to target
entity.add_relationship(Relationship.new(R_Damaged.new(), C_FireDamage.new(50.0, 5.0)))3. Entity → Archetype
Tag relationships with high-level classifications:
e_unit.add_relationship(Relationship.new(R_Likes.new(), Food))3. Querying Relationships & Wildcards
Exact Match Queries
# Match entities attacking e_boss
var attackers = ECS.world.query \
.with_relationship([Relationship.new(R_Attacks.new(), e_boss)]) \
.execute()Wildcard Queries (ECS.wildcard)
# 1. Any entity attacking ANY target (wildcard target)
var in_combat = ECS.world.query \
.with_relationship([Relationship.new(R_Attacks.new(), ECS.wildcard)]) \
.execute()
# 2. Any relationship targeting e_boss (wildcard relation)
var targeted = ECS.world.query \
.with_relationship([Relationship.new(ECS.wildcard, e_boss)]) \
.execute()
# 3. Omitting target defaults to wildcard:
var has_buff = ECS.world.query \
.with_relationship([Relationship.new(R_Buff.new())]) \
.execute()Performance Rule: Wildcard queries scan multiple archetypes (O(N)). Always combine wildcards with a component filter like .with_all([C_Alive]) in performance-critical code!4. Limited-Count Removal (Buffs, Debuffs & Stacks)
The remove_relationship() method accepts an optional limit argument:
limit = -1(Default): Remove all matching relationships.limit = N(N > 0): Remove up toNmatching instances.limit = 0: Removes none (used for validation).
Example: Poison Stacks & Antidote
# Apply 4 poison stacks
for i in 4:
entity.add_relationship(Relationship.new(R_Poison.new(), null))
# Minor antidote cleanses 2 stacks
entity.remove_relationship(Relationship.new(R_Poison.new(), null), 2)
# Verify remaining stacks: entity still has 2 poison stacksBatch Cleansing
# Remove up to 2 stacks across multiple negative effects
entity.remove_relationships([
Relationship.new(R_Poison.new(), null),
Relationship.new(R_Bleed.new(), null)
], 2)5. The Rels Factory Pattern (Recommended)
To avoid allocating new relation objects repeatedly, create a static Rels helper class. See `examples/rels.gd`:
class_name Rels
static var attacks: Relationship = Relationship.new(R_Attacks.new(), null)
static var child_of: Relationship = Relationship.new(R_ChildOf.new(), null)
static func attacks_target(target: Entity) -> Relationship:
return Relationship.new(R_Attacks.new(), target)
static func child_of_entity(parent: Entity) -> Relationship:
return Relationship.new(R_ChildOf.new(), parent)Usage in gameplay code:
if entity.has_relationship(Rels.attacks):
pass
entity.remove_relationship(Rels.attacks, 1)6. Naming Conventions
| Type | Class Name | File Name |
|---|---|---|
| Relation Component | R_ChildOf, R_Attacks | r_child_of.gd, r_attacks.gd |
| Static Relationships | Rels.attacks_target(e) | rels.gd |

