Lost Lily/Grid ToolkitDocs 1.0
Table of Contents

Place your first structure

Outcome

Create a reusable structure and its visual in Unity, connect them to the board, then preview and place one instance at (1, 1).

The assets define what the structure is and how it looks. The component at the end of this page only asks the board to place that authored definition.

Steps

1. Author the structure

  1. Exit Play Mode and select Assets/FirstBoard in the Project window.
  2. Choose Assets > Create > Lost Lily > Grid Toolkit > Structures > Square Basic Structure. Name the new SquareGridBasicStructureAsset First Board Crate.
  3. Set Structure Id to first-board.crate and Display Name to Crate. Keep the default one-cell footprint, then select Validate Structure.

first-board.crate identifies the reusable structure definition. The placed instance will receive the separate ID first-board.crate-1.

2. Give it a visual

  1. Create a Cube in the Scene, name it First Board Crate Prefab, and scale it so it fits within one cell. Drag it into Assets/FirstBoard to create a prefab, then remove the scene copy.
  2. Choose Assets > Create > Lost Lily > Grid Toolkit > Visualization > Structure Prefab Visual. Name the GridStructurePrefabVisualAsset First Board Crate Visual.
  3. Assign the crate prefab to Prefab, then select Prepare Prefab for Grid Toolkit.
  4. Select First Board Crate and assign First Board Crate Visual to Visual Asset.

The structure asset owns gameplay data such as footprint and layer. The visual asset points to the prefab used by the renderer.

3. Connect the structure to the board and renderer

  1. Choose Assets > Create > Lost Lily > Grid Toolkit > Structures > Structure Library and name the GridStructureLibraryAsset First Board Structure Library.
  2. Add First Board Crate to its Structures list, then select Validate Library.
  3. Select First Board Setup, assign First Board Structure Library to Structure Library, and select Validate Setup.
  4. Select the First Board scene object and add GridStructureLibraryNodeBuilder. Assign First Board Structure Library to it.
  5. On the World 3D renderer, replace Structure Node Builder with the new library node builder.

The setup uses the library to build and resolve structure definitions. The node builder uses the same library to find each structure's authored visual.

4. Preview and place an instance

  1. Create Assets/FirstBoard/FirstBoardActionExample.cs.
  2. Expand Complete tested example, copy the component into that file, and save it.
Complete tested example
using System;
using LostLily.GridToolkit.Actions;
using LostLily.GridToolkit.Authoring;
using LostLily.GridToolkit.Board;
using LostLily.GridToolkit.Occupancy;
using LostLily.GridToolkit.Topology.Square;
using UnityEngine;

namespace GridToolkit.Tutorials
{
    [RequireComponent(typeof(GridBoardAuthoring))]
    public sealed class FirstBoardActionExample : MonoBehaviour
    {
        private const string DefinitionId = "first-board.crate";
        private const string InstanceId = "first-board.crate-1";

        [SerializeField] private GridBoardAuthoring boardAuthoring;

        public (
            bool PreviewSucceeded,
            bool ApplySucceeded,
            long RevisionBeforePreview,
            long RevisionAfterPreview,
            long RevisionAfterApply,
            int BatchesAfterPreview,
            int CommittedBatchCount,
            bool StructureExists) LastReport { get; private set; }

        private void Start()
        {
            LastReport = Run();
        }

        public (
            bool PreviewSucceeded,
            bool ApplySucceeded,
            long RevisionBeforePreview,
            long RevisionAfterPreview,
            long RevisionAfterApply,
            int BatchesAfterPreview,
            int CommittedBatchCount,
            bool StructureExists) Run()
        {
            boardAuthoring ??= GetComponent<GridBoardAuthoring>();
            GridBoardState state = boardAuthoring?.State;

            if (state == null)
            {
                Debug.LogError(
                    "The authored board has no runtime state. Check its setup and Build On Awake.",
                    this);
                return default;
            }

            GridStructureLibraryAsset library =
                boardAuthoring.SetupAsset?.StructureLibrary;
            GridStructureDefinition crate = null;
            bool definitionFound = library != null
                && library.TryGetStructureDefinition(
                    DefinitionId,
                    out crate);

            if (!definitionFound)
            {
                Debug.LogError(
                    $"The board setup must reference a structure library "
                    + $"containing '{DefinitionId}'.",
                    this);
                return default;
            }

            LastReport = PreviewAndApply(state, crate);
            if (!LastReport.PreviewSucceeded || !LastReport.ApplySucceeded)
                return LastReport;

            Debug.Log(
                $"Preview: revision {LastReport.RevisionAfterPreview}, "
                + $"committed batches {LastReport.BatchesAfterPreview}. "
                + $"Apply: revision {LastReport.RevisionAfterApply}, "
                + $"committed batches {LastReport.CommittedBatchCount}, "
                + $"structure {InstanceId} exists: {LastReport.StructureExists}.",
                this);
            return LastReport;
        }

        public (
            bool PreviewSucceeded,
            bool ApplySucceeded,
            long RevisionBeforePreview,
            long RevisionAfterPreview,
            long RevisionAfterApply,
            int BatchesAfterPreview,
            int CommittedBatchCount,
            bool StructureExists) PreviewAndApply(
            GridBoardState state,
            GridStructureDefinition definition)
        {
            return PreviewAndApply(
                state,
                definition,
                new SquareGridCoordinate(1, 1),
                InstanceId);
        }

        public (
            bool PreviewSucceeded,
            bool ApplySucceeded,
            long RevisionBeforePreview,
            long RevisionAfterPreview,
            long RevisionAfterApply,
            int BatchesAfterPreview,
            int CommittedBatchCount,
            bool StructureExists) PreviewAndApply(
            GridBoardState state,
            GridStructureDefinition definition,
            SquareGridCoordinate coordinate,
            string instanceId)
        {
            if (state == null)
                throw new ArgumentNullException(nameof(state));
            if (definition == null)
                throw new ArgumentNullException(nameof(definition));
            if (string.IsNullOrWhiteSpace(instanceId))
                throw new ArgumentException("An instance ID is required.", nameof(instanceId));

            IGridAction action = GridActions.PlaceStructure(
                definition,
                coordinate,
                instanceId);

            long revisionBeforePreview = state.Revision;
            int committedBatchCount = 0;
            void CountCommittedBatch(GridBoardEventBatch _) => committedBatchCount++;
            state.EventsCommitted += CountCommittedBatch;
            try
            {
                GridActionResult preview = GridActionRunner.Preview(state, action);

                long revisionAfterPreview = state.Revision;
                int batchesAfterPreview = committedBatchCount;
                if (!preview.Success)
                {
                    Debug.LogError(preview.FormatDiagnostics(), this);
                    return (
                        false,
                        false,
                        revisionBeforePreview,
                        revisionAfterPreview,
                        state.Revision,
                        batchesAfterPreview,
                        committedBatchCount,
                        false);
                }

                GridActionResult applied = GridActionRunner.Apply(state, action);

                if (!applied.Success)
                    Debug.LogError(applied.FormatDiagnostics(), this);

                return (
                    true,
                    applied.Success,
                    revisionBeforePreview,
                    revisionAfterPreview,
                    state.Revision,
                    batchesAfterPreview,
                    committedBatchCount,
                    state.TryGetStructure(instanceId, out _));
            }
            finally
            {
                state.EventsCommitted -= CountCommittedBatch;
            }
        }

    }
}
  1. Add First Board Action Example to the First Board GameObject beside GridBoardAuthoring.
  2. Enter Play Mode with the Scene view visible.

How the component works

The component first resolves the authored state on the same GameObject:

Excerpt — resolve the authored board state. Tested using public APIs.

boardAuthoring ??= GetComponent<GridBoardAuthoring>();
GridBoardState state = boardAuthoring?.State;

It resolves first-board.crate from the structure library assigned to the setup:

Excerpt — resolve the authored structure definition. Tested using public APIs.

GridStructureLibraryAsset library =
    boardAuthoring.SetupAsset?.StructureLibrary;
GridStructureDefinition crate = null;
bool definitionFound = library != null
    && library.TryGetStructureDefinition(
        DefinitionId,
        out crate);

It then creates one placement request with that definition:

Excerpt — create the placement request. Tested using public APIs.

IGridAction action = GridActions.PlaceStructure(
    definition,
    coordinate,
    instanceId);

Preview checks the request without changing the board. The complete component reports diagnostics and stops when this result is unsuccessful:

Excerpt — preview the placement. Tested using public APIs.

GridActionResult preview = GridActionRunner.Preview(state, action);

Only an accepted request reaches Apply:

Excerpt — apply the placement. Tested using public APIs.

GridActionResult applied = GridActionRunner.Apply(state, action);

GridActionRunner evaluates Preview and Apply separately. Preview never writes to the board. Apply checks the current board again, then commits the complete placement or leaves the board unchanged. The view refreshes after a commit.

What you should see

The Console reports:

Preview: revision 0, committed batches 0. Apply: revision 1, committed batches 1, structure first-board.crate-1 exists: True.

This confirms that preview left the GridBoardState unchanged, applying the action advanced the revision from 0 to 1, observers received one committed batch, and the expected structure exists.

Scene view of the 8 by 8 World 3D board with one structure and a Console result showing revision 0 to 1 and one committed batch
The structure appears at (1, 1); the Console confirms that preview did not commit and apply committed once.

Troubleshooting

Symptom Check
The authored board has no runtime state Keep the example beside GridBoardAuthoring; assign the setup and enable Build On Awake.
The structure definition cannot be found Add First Board Crate to the library and assign that library to First Board Setup.
Preview reports an invalid coordinate Confirm the board is still 8 by 8 and the request uses (1, 1).
Apply reports a duplicate instance ID Stop and re-enter Play Mode, or remove first-board.crate-1.
The structure exists but is not visible Assign the prefab visual to the structure, assign the library to GridStructureLibraryNodeBuilder, and use that builder in the renderer.
The prefab reports invalid configuration Select Prepare Prefab for Grid Toolkit on the prefab visual asset.