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
- Exit Play Mode and select
Assets/FirstBoardin the Project window. - Choose Assets > Create > Lost Lily > Grid Toolkit > Structures > Square Basic Structure.
Name the new SquareGridBasicStructureAsset
First Board Crate. - Set Structure Id to
first-board.crateand Display Name toCrate. 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
- Create a Cube in the Scene, name it
First Board Crate Prefab, and scale it so it fits within one cell. Drag it intoAssets/FirstBoardto create a prefab, then remove the scene copy. - Choose Assets > Create > Lost Lily > Grid Toolkit > Visualization > Structure Prefab Visual.
Name the GridStructurePrefabVisualAsset
First Board Crate Visual. - Assign the crate prefab to Prefab, then select Prepare Prefab for Grid Toolkit.
- Select
First Board Crateand assignFirst Board Crate Visualto 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
- Choose Assets > Create > Lost Lily > Grid Toolkit > Structures > Structure Library and name
the GridStructureLibraryAsset
First Board Structure Library. - Add
First Board Crateto its Structures list, then select Validate Library. - Select
First Board Setup, assignFirst Board Structure Libraryto Structure Library, and select Validate Setup. - Select the
First Boardscene object and add GridStructureLibraryNodeBuilder. AssignFirst Board Structure Libraryto it. - 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
- Create
Assets/FirstBoard/FirstBoardActionExample.cs. - 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;
}
}
}
}
- Add First Board Action Example to the
First BoardGameObject beside GridBoardAuthoring. - 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.
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. |