PlaceableDefinition Resource Guide

A PlaceableDefinition is a Godot .tres resource that defines a placeable object in GridPlacement. It is the primary source of truth for all placeable properties including manipulation capabilities (rotate, flip horizontal, flip vertical, can delete).


PlaceableDefinition vs PlaceableType

AspectPlaceableType (Core)PlaceableDefinition (Godot Resource)
LayerCore (pure C#, no engine deps)Godot (engine-specific Resource)
LocationCore/Configuration/PlaceableType.csaddons/grid_placement/placement/PlaceableDefinition.cs
Used byECS systems, YAML-first gamesGridPlacement’s Godot-side catalog
InheritanceExtended by game-specific types (e.g., IdlePlaceableConfiguration)Wrapped by ToCore() conversion
File formatC# class.tres (text-based, AI-editable)

The relationship:

1
2
3
4
PlaceableDefinition.tres → ToCore() → CorePlaceable (Core)
                                   Can be extended by
                                   game-specific types

Resource File Location

Place PlaceableDefinition resources in:

1
res://addons/grid_placement/placeable_definitions/

This folder is the conventional location GridPlacement’s catalog loader scans for definitions.


Creating a PlaceableDefinition

Option A: Godot Editor

  1. Right-click in FileSystem → Create New...Resource
  2. Set script to PlaceableDefinition
  3. Configure properties in inspector
  4. Save to res://addons/grid_placement/placeable_definitions/

Option B: Script (Programmatic)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var definition = new PlaceableDefinition
{
    display_name = "Barn",
    packed_scene = ResourceLoader.Load<PackedScene>("res://scenes/buildings/barn.tscn"),
    size = new Vector2I(2, 2),
    rotatable = true,
    flip_horizontal = true,
    flip_vertical = false,
    can_delete = true,
    tags = new Godot.Collections.Array<Resource>()
};
ResourceSaver.Save(definition, "res://addons/grid_placement/placeable_definitions/barn.tres");

Option C: From YAML (AI-Editable)

Generate .tres files from YAML configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# placeables.yaml
placeables:
  - id: barn
    display_name: "Barn"
    scene_path: "res://scenes/buildings/barn.tscn"
    size: [2, 2]
    rotatable: true
    flip_horizontal: true
    flip_vertical: false
    can_delete: true
    category: buildings
    tags:
      - wooden
      - large

Property Reference

Required Properties

PropertyTypeDefaultDescription
display_nameStringName""Human-readable name shown in UI
packed_scenePackedScene?nullThe scene to instantiate when placed

Size

PropertyTypeDefaultDescription
sizeVector2I(1, 1)Grid footprint. (2, 2) for multi-tile buildings

Manipulation Capabilities

PropertyTypeDefaultDescription
rotatablebooltrueCan player rotate this object during placement
flip_horizontalbooltrueCan player flip horizontally
flip_verticalbooltrueCan player flip vertically
can_deletebooltrueCan player demolish after placement

Optional Properties

PropertyTypeDefaultDescription
iconTexture2D?nullIcon shown in selection UI
tagsArray<Resource>[]PlaceableTagDefinition resources for categorization
placement_rulesArray<Resource>[]PlacementRule resources for validation
ignore_base_rulesVariantfalseSkip global rules when true

Catalog Integration

PlaceableDefinition resources are loaded into the PlaceableRuntimeCatalog via PlaceableCatalogBootstrap:

1
2
3
4
5
6
PlaceableDefinition.tres
         ▼ Load()
PlaceableRuntimeCatalog
    └── Get(hash) → PlaceableEntry
                      └── Definition → PlaceableDefinition

Catalog Lookup

When an entity is placed, its hash is stored in PlacedComponent.PlaceableHash. The adapter looks up capabilities:

1
2
3
// PlacementSceneAdapter.cs
var placeable = _nodeFactory.ResolvePlaceable(placed.PlaceableHash);
// placeable.CanRotate, placeable.CanFlipHorizontal, etc.

Tags

Tags categorize placeables for filtering in UI:

1
2
3
4
5
6
[gd_resource type="Resource" script_class="PlaceableTagDefinition" ...]
[resource]
script = ExtResource("1")
display_name = "Wooden"
tag_id = "wooden"
color = Color(0.6, 0.4, 0.2, 1.0)

Assign tags in PlaceableDefinition:

1
2
3
4
5
PlaceableDefinition.tres
tags = [
    ExtResource("wooden_tag"),
    ExtResource("building_tag")
]

Placement Rules

Rules validate placement in real-time:

1
2
3
4
5
[gd_resource type="Resource" script_class="TileCheckPlacementRule" ...]
[resource]
script = ExtResource("1")
rule_id = "tile_check_rule"
rule_type = "TileCheck"

Add rules to PlaceableDefinition:

1
2
3
4
PlaceableDefinition.tres
placement_rules = [
    ExtResource("tile_check_rule")
]

Complete Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[gd_resource type="Resource" script_class="PlaceableDefinition" load_steps=4 format=3]

[ext_resource path="res://addons/grid_placement/placement/PlaceableDefinition.cs" type="Script" id="1"]
[ext_resource path="res://scenes/buildings/barn.tscn" type="PackedScene" id="2"]
[ext_resource path="res://addons/grid_placement/placement/tags/building_tag.tres" type="Resource" id="3"]

[resource]
script = ExtResource("1")
display_name = "Barn"
packed_scene = ExtResource("2")
size = Vector2i(2, 2)
rotatable = true
flip_horizontal = true
flip_vertical = false
can_delete = true
tags = [ExtResource("3")]
placement_rules = []
ignore_base_rules = false

Per-Instance Override with ManipulatableNode

If a specific instance of a placed object needs different capabilities than the definition provides:

1
2
3
4
5
6
7
MyScene.tscn
├── Barn (placed via GridPlacement)  ← Uses PlaceableDefinition capabilities
│   ├── Sprite2D
│   ├── CollisionShape2D
│   └── ManipulatableNode             ← Per-instance override
│       rotatable = false             ← Overrides definition
│       flip_horizontal = false

The ManipulatableNode facade on an instance takes precedence over the definition.


Debugging

Verify Definition Loads

1
2
3
4
5
6
if (_context.Catalog.TryGet(hash, out var entry))
{
    GD.Print($"Loaded: {entry.Definition.display_name}");
    GD.Print($"Rotatable: {entry.Definition.rotatable}");
    GD.Print($"CanFlipH: {entry.Definition.flip_horizontal}");
}

Check Entity Capabilities

Query the ECS entity:

1
2
3
4
5
6
if (entity.HasComponent<ManipulatableComponent>())
{
    var mc = entity.GetComponent<ManipulatableComponent>();
    GD.Print($"Entity CanRotate: {mc.CanRotate}");
    GD.Print($"Entity CanFlipHorizontal: {mc.CanFlipHorizontal}");
}

Common Issues

Capabilities Not Flowing to Entity

Cause: packed_scene is null or invalid
Fix: Ensure packed_scene points to a valid PackedScene resource

Entity Has Wrong Capabilities After Placement

Cause: Definition was modified after entity was placed
Fix: Capabilities are baked into ManipulatableComponent at placement time. Re-place the entity.

Tag Not Found

Cause: Tag resource not in tags array or tag script class mismatch
Fix: Ensure tag is PlaceableTagDefinition type and properly assigned