Run an action on a code-owned board
When to use
After creating and owning a board in code, run the action component below against that retained state. Use Place your first structure when GridBoardAuthoring owns the state instead.
Before you start
Exit Play Mode. Keep ProgrammaticBoardExample on the Programmatic Board GameObject.
Procedure
- Create
Assets/ProgrammaticBoard/ProgrammaticFirstActionExample.cs. - Copy the complete component below into the file.
Complete tested example
using LostLily.GridToolkit.Actions;
using LostLily.GridToolkit.Board;
using LostLily.GridToolkit.Occupancy;
using LostLily.GridToolkit.Topology.Square;
using UnityEngine;
namespace GridToolkit.Tutorials
{
[DisallowMultipleComponent]
public sealed class ProgrammaticFirstActionExample : MonoBehaviour
{
// Moving this structure later uses the same instance ID.
private const string DefinitionId = "programmatic-board.crate";
private const string InstanceId = "programmatic-board.crate-1";
[SerializeField] private MonoBehaviour stateSourceComponent;
[SerializeField] private Vector2Int placementCell = new(1, 1);
private void Start()
{
Place();
}
public GridActionResult Place()
{
IGridBoardStateSource stateSource = FindStateSource();
GridBoardState state = stateSource?.State;
if (state == null || state.IsDisposed)
{
Debug.LogError("No ready IGridBoardStateSource was found on this object.", this);
return null;
}
GridStructureDefinition definition = new GridStructureDefinitionBuilder(
DefinitionId,
GridStructureFootprint.CreateSingleCell(SquareGridTopology.Instance),
topology: SquareGridTopology.Instance).Build();
SquareGridCoordinate coordinate = new(placementCell.x, placementCell.y);
IGridAction action = GridActions.PlaceStructure(definition, coordinate, InstanceId);
// Use the owner's state; preview leaves that state unchanged.
GridActionResult preview = GridActionRunner.Preview(state, action);
if (!preview.Success)
{
Debug.LogError(preview.FormatDiagnostics(), this);
return preview;
}
GridActionResult applied = GridActionRunner.Apply(state, action);
if (!applied.Success)
{
Debug.LogError(applied.FormatDiagnostics(), this);
return applied;
}
Debug.Log($"Placed {InstanceId}. Board revision {state.Revision}.", this);
return applied;
}
private IGridBoardStateSource FindStateSource()
{
if (stateSourceComponent is IGridBoardStateSource assignedSource)
return assignedSource;
foreach (MonoBehaviour component in GetComponents<MonoBehaviour>())
{
if (component is IGridBoardStateSource source)
return source;
}
return null;
}
}
}
- Add Programmatic First Action Example beside Programmatic Board Example on the same GameObject.
- Enter Play Mode.
Place() resolves the owner's current state, creates a definition and placement request, previews
it, and applies an accepted request. Rejected results log diagnostics and stop. Start calls
Place() once; remove Start when your own input should trigger the request.
Resolve the retained state
Excerpt — resolve the retained state. Tested using public APIs.
IGridBoardStateSource stateSource = FindStateSource();
GridBoardState state = stateSource?.State;
Create the placement
Excerpt — create the placement. Tested using public APIs.
GridStructureDefinition definition = new GridStructureDefinitionBuilder(
DefinitionId,
GridStructureFootprint.CreateSingleCell(SquareGridTopology.Instance),
topology: SquareGridTopology.Instance).Build();
SquareGridCoordinate coordinate = new(placementCell.x, placementCell.y);
IGridAction action = GridActions.PlaceStructure(definition, coordinate, InstanceId);
Preview without changing the board
Excerpt — preview and stop on rejection. Tested using public APIs.
// Use the owner's state; preview leaves that state unchanged.
GridActionResult preview = GridActionRunner.Preview(state, action);
if (!preview.Success)
{
Debug.LogError(preview.FormatDiagnostics(), this);
return preview;
}
Apply the accepted request
Excerpt — apply the accepted request. Tested using public APIs.
GridActionResult applied = GridActionRunner.Apply(state, action);
if (!applied.Success)
{
Debug.LogError(applied.FormatDiagnostics(), this);
return applied;
}
Result
The Console first reports the 12-cell board and then reports:
Placed programmatic-board.crate-1. Board revision 1.
When you stop Play Mode or destroy the GameObject, the Console also reports:
Programmatic board disposed.
IGridBoardStateSource provides the exact GridBoardState retained by the neighboring owner. The action component neither creates nor disposes that state.
GridActionRunner plans the placement against
the owner's current state. Preview leaves revision 0 with no committed batch. Apply builds a
fresh plan, advances revision to 1, publishes one batch, and creates the expected instance. The
owner later disposes the same state from OnDestroy.
Troubleshooting
| Symptom | Check |
|---|---|
| “No ready IGridBoardStateSource was found” | Put both workflow components on the same enabled GameObject. |
| Preview reports an invalid coordinate | Keep the 4 × 3 square board and coordinate (1, 1). |
| Placement reports a duplicate instance ID | Stop and re-enter Play Mode, or remove the existing instance. |
| A renderer stays empty | Connect a board view to state and configure a renderer. |