Grid Placement

Save and Load (5.0.2)

Comprehensive guide to persisting placement state using PlaceableInstance and serialization patterns.

This guide covers how to persist placed objects across game sessions in GridBuilding 5.0.2. While the maintenance version uses component-based persistence, it establishes patterns that carry forward into future versions.

Core Concepts

Persistence in 5.0.2 relies on three key elements:

  1. PlaceableInstance: A runtime component attached to every valid placement. It holds the “DNA” of the object (template ID, original resource path).
  2. Serialization: The process of converting the node hierarchy into a flat Dictionary.
  3. Deserialization: Reconstructing the node tree from data, ensuring all dependencies (scripts, resources) are relinked.

The Serialization Contract

A valid save entry in 5.0.2 looks like this:

1
2
3
4
5
6
7
8
9
{
    "instance_name": "Chair_001",
    "transform": Transform2D(...),
    "placeable_id": "**res://placeables/chair.tres**",
    "custom_data": {
        "durability": 50,
        "owner_id": "player_1"
    }
}

PlaceableInstance Component

The PlaceableInstance script (res://addons/grid_building/placeable_instance.gd) exposes two critical methods:

  • save(with_custom_data: bool) -> Dictionary: serializes the node.
  • instance_from_save(data: Dictionary, parent: Node) -> Node: static factory method to rebuild the node.

Implementation Guide

Step 1: Saving the World

To save your level, iterate through the PlaceableInstance group. This group is automatically managed by the system.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func save_level() -> void:
    var save_data: Array[Dictionary] = []
    
    # Iterate all placed objects
    for node in get_tree().get_nodes_in_group("PlaceableInstance"):
        
        # 1. Validate it's a valid placement (not a preview)
        if node.get_parent().has_meta("gb_preview"):
            continue
            
        # 2. Serialize
        # Assumes the node structure: PlaceableRoot -> PlaceableInstance
        var data = node.save(true) 
        
        # 3. Add custom game logic data (optional)
        if node.get_parent().has_method("get_custom_save_data"):
            data["custom_data"] = node.get_parent().get_custom_save_data()
            
        save_data.append(data)
        
    # Write to file
    _write_to_disk(save_data)

Step 2: Loading the World

Loading is a two-step process: Clear the existing world, then rebuild from data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func load_level(data: Array[Dictionary]) -> void:
    # 1. Clear existing
    get_tree().call_group("PlaceableInstance", "queue_free")
    await get_tree().process_frame
    
    # 2. Rebuild
    for entry in data:
        # Static factory method handles resource loading
        var new_node = PlaceableInstance.instance_from_save(entry, world_root)
        
        # Restore custom data if needed
        if "custom_data" in entry and new_node.has_method("restore_custom_data"):
            new_node.restore_custom_data(entry["custom_data"])

Handling Edge Cases

  • Missing Resources: If a placeable_id resource path no longer exists (e.g., you deleted the file), instance_from_save will fail gracefully. Ensure your loading logic handles null returns.
  • Version Migrations: 5.0.2 does not have a built-in schema migration system. If you change your data structure, you must handle version checking in your own code.

Validated By

The save/load architecture and serialization patterns are verified by the following test suites:

  • Serialization Protocol: res://addons/grid_building/test/building/placement/placement_report_unit_test.gd — Validates the serialization of placement metadata and report integrity.
  • Component Implementation: res://addons/grid_building/placeable_instance.gd — Core component that manages instance lifecycle and serialization hooks.
  • Metadata Integrity: res://addons/grid_building/test/utilities/data/composition_container_subresources_test.gd — Ensures that configuration data used during reconstruction is properly persisted.