Display action feedback
When to use
Display action feedback when a preview or committed result should highlight affected or invalid cells. Visual feedback is presentation state: it does not mutate the board or advance its revision.
Before you start
Choose the logical request in Choose a built-in action. Connect a GridBoardView to a ready renderer with a compatible intent renderer.
Procedure
Publish preview feedback
Preview the action, then give the same action and result to the view's GridVisualState.
Excerpt — publish a preview result. Tested using public APIs.
GridActionResult preview = GridActionRunner.Preview(state, action);
boardView.VisualState.SetActionPreview(action, preview);
Apply and replace the channel
Apply only after a successful preview. SetActionResult replaces the action feedback channel with
the committed result.
Excerpt — apply and publish the result. Tested using public APIs.
GridActionResult applied = GridActionRunner.Apply(state, action);
boardView.VisualState.SetActionResult(action, applied);
Choose the feedback route
| Feedback | Use |
|---|---|
| Generic affected or invalid cells | SetActionPreview or SetActionResult |
| Placement footprint | SetPlacementPreview |
| Inventory move, rotate, sort, transfer, or merge | Inventory Visualization bridge |
| Project-specific semantics | IGridActionVisualIntentContributor |
A custom contributor that returns no valid intents falls back to generic action feedback. Inventory quantity changes also use generic action feedback because Inventory has no quantity-specific intent ID.
GridVisualIntentIds defines the Stable built-in intent and channel IDs:
| Purpose | Intent ID | Channel ID |
|---|---|---|
| Hover | grid.hover |
grid.visual.channel.hover |
| Selection | grid.selection |
grid.visual.channel.selection |
| Valid placement | grid.placement.valid |
grid.visual.channel.placement |
| Invalid placement | grid.placement.invalid |
grid.visual.channel.placement |
| Affected action cells | grid.action.affected |
grid.visual.channel.action-affected |
| Invalid action cells | grid.action.invalid |
grid.visual.channel.action-invalid |
| Project overlay | grid.overlay |
Project-owned channel |
GridInventoryVisualIntentIds defines these Preview intent IDs:
| Operation | Accepted intent | Rejected intent |
|---|---|---|
| Move | grid.inventory.move |
grid.inventory.move.invalid |
| Transfer | grid.inventory.transfer |
grid.inventory.transfer.invalid |
| Merge | grid.inventory.merge |
grid.inventory.merge.invalid |
| Rotate | grid.inventory.rotate |
grid.inventory.rotate.invalid |
| Sort | grid.inventory.sort |
grid.inventory.sort.invalid |
The current Inventory bridge emits grid.inventory.move, grid.inventory.rotate, and
grid.inventory.sort channels for same-board actions.
Cross-board operations use one channel per participant:
- transfers use
grid.inventory.transfer.sourceandgrid.inventory.transfer.destination; - merges use
grid.inventory.merge.sourceandgrid.inventory.merge.destination.
These channel strings are current Preview-tier behavior rather than Stable built-in constants.
Clear temporary feedback
Excerpt — clear the action-feedback channels. Tested using public APIs.
if (boardView == null)
boardView = GetComponent<GridBoardView>();
if (boardView == null)
return;
boardView.VisualState.ClearIntentChannel(GridVisualIntentIds.ActionAffectedChannel);
boardView.VisualState.ClearIntentChannel(GridVisualIntentIds.ActionInvalidChannel);
Complete tested example
using LostLily.GridToolkit.Actions;
using LostLily.GridToolkit.Authoring;
using LostLily.GridToolkit.Board;
using LostLily.GridToolkit.Topology.Square;
using LostLily.GridToolkit.Visualization;
using UnityEngine;
namespace GridToolkit.Tutorials
{
[RequireComponent(typeof(GridBoardAuthoring), typeof(GridBoardView))]
public sealed class ActionFeedbackExample : MonoBehaviour
{
[SerializeField] private GridBoardAuthoring boardAuthoring;
[SerializeField] private GridBoardView boardView;
private void Start()
{
AddCell();
}
public GridActionResult AddCell()
{
return AddCell(new SquareGridCoordinate(8, 0));
}
public GridActionResult AddCell(SquareGridCoordinate coordinate)
{
if (boardAuthoring == null)
boardAuthoring = GetComponent<GridBoardAuthoring>();
if (boardView == null)
boardView = GetComponent<GridBoardView>();
GridBoardState state = boardAuthoring.State;
if (state == null || state.IsDisposed || boardView == null)
{
Debug.LogError("Action feedback needs a ready board owner and view.", this);
return null;
}
IGridAction action = GridActions.AddCell(coordinate);
GridActionResult preview = GridActionRunner.Preview(state, action);
boardView.VisualState.SetActionPreview(action, preview);
if (!preview.Success)
{
Debug.LogError(preview.FormatDiagnostics(), this);
return preview;
}
GridActionResult applied = GridActionRunner.Apply(state, action);
boardView.VisualState.SetActionResult(action, applied);
if (!applied.Success)
{
Debug.LogError(applied.FormatDiagnostics(), this);
return applied;
}
Debug.Log($"Added cell {coordinate}. Board revision {state.Revision}.", this);
return applied;
}
public void ClearFeedback()
{
if (boardView == null)
boardView = GetComponent<GridBoardView>();
if (boardView == null)
return;
boardView.VisualState.ClearIntentChannel(GridVisualIntentIds.ActionAffectedChannel);
boardView.VisualState.ClearIntentChannel(GridVisualIntentIds.ActionInvalidChannel);
}
}
}
The complete component calls AddCell() from Start to add (8, 0) beside the tutorial's 8 by 8
board. AddCell(coordinate) uses the same workflow for another target. Remove Start when your
own input should supply the coordinate. It returns the rejected preview or committed action result;
the view receives that result for feedback. ClearFeedback() removes the two action channels.
Result
Preview feedback appears without a commit. Apply publishes the committed result, and the renderer keeps each feedback node under a stable key that includes its owning channel. Replacing or clearing that channel updates or removes only its own retained nodes.
Troubleshooting
| Symptom | Check |
|---|---|
| The action succeeds but nothing appears | Assign an intent renderer whose required capabilities match the active backend. |
| Old feedback remains visible | Clear the exact owner channel or the relevant transient visual category. |
| Custom feedback disappears | Return at least one valid intent and keep its channel ID stable. |