Interact with a uGUI board
When to use
Add pointer interaction with uGUI after a board already renders on a Canvas and input should hover, select, or drag logical cells or structures. These uGUI interaction contracts are Preview-tier in 1.0.0.
Before you start
Complete Display a board with uGUI, then choose the input path:
| Input path | Additional scene requirements |
|---|---|
| Project code supplies a screen position directly | None beyond a ready view and renderer. |
| Unity pointer callbacks supply the position | EventSystem, compatible input module, and the project's event-routing or raycast components. |
Neither path changes what is required merely to render retained uGUI content.
Procedure
1. Resolve the pointer
Use GridUiBoardHitUtility. Its multi-view overload checks views in the supplied order and returns the first view that produces a hit. Treat unresolved input as no target. If overlapping views should be rejected, compare their results in project code before accepting a target.
Excerpt — resolve and publish one hover target. Tested using public APIs.
public bool TrySetHover(Vector2 screenPosition, out GridHit hit)
{
boardView ??= GetComponent<GridBoardView>();
if (boardView == null
|| !GridUiBoardHitUtility.TryResolveScreenHit(
boardView,
screenPosition,
out hit))
{
hit = GridHit.None(Vector3.zero);
boardView?.VisualState.ClearHover();
return false;
}
boardView.VisualState.SetHover(hit.Coordinate);
return true;
}
2. Decide what the hit means
Game code decides whether the returned cell or structure can be hovered, selected, moved, or acted on. Hit resolution does not make that gameplay decision.
Publish temporary hover or selection feedback without changing the board. Add GridUiInteractionIntentRenderer with a visual profile when the Canvas needs specialized fills or outlines.
After game code accepts the target, preview the logical action and apply only a successful result:
Excerpt — preview, publish feedback, and apply. Tested using public APIs.
public (GridActionResult Preview, GridActionResult Applied) PreviewAndApply(
IGridAction action)
{
boardView ??= GetComponent<GridBoardView>();
if (boardView?.State == null)
throw new InvalidOperationException("Bind a ready board before interacting.");
GridActionResult preview = GridActionRunner.Preview(boardView.State, action);
boardView.VisualState.SetActionPreview(action, preview);
if (!preview.Success)
return (preview, null);
GridActionResult applied = GridActionRunner.Apply(boardView.State, action);
boardView.VisualState.SetActionResult(action, applied);
return (preview, applied);
}
3. Clear temporary feedback
Use GridUiStructureDragVisualPresenter only for temporary drag presentation. The accepted action or multi-board operation remains the authority for gameplay state.
Clear temporary channels when the pointer leaves, the interaction is cancelled, or the owning component is disabled.
Excerpt — clear the interaction state. Tested using public APIs.
public void ClearInteractionFeedback()
{
boardView ??= GetComponent<GridBoardView>();
boardView?.ClearTransientVisuals(GridTransientVisualClearOptions.All);
}
private void OnDisable()
{
ClearInteractionFeedback();
}
Complete tested example
using System;
using LostLily.GridToolkit.Actions;
using LostLily.GridToolkit.Board;
using LostLily.GridToolkit.Visualization;
using LostLily.GridToolkit.Visualization.UI;
using UnityEngine;
namespace GridToolkit.Tutorials
{
[RequireComponent(typeof(GridBoardView))]
public sealed class UiInteractionWorkflowExample : MonoBehaviour
{
[SerializeField] private GridBoardView boardView;
public void Bind(IGridBoardStateSource stateSource)
{
boardView ??= GetComponent<GridBoardView>();
if (boardView == null)
throw new InvalidOperationException("A GridBoardView is required.");
boardView.SetStateSource(stateSource);
}
public bool TrySetHover(Vector2 screenPosition, out GridHit hit)
{
boardView ??= GetComponent<GridBoardView>();
if (boardView == null
|| !GridUiBoardHitUtility.TryResolveScreenHit(
boardView,
screenPosition,
out hit))
{
hit = GridHit.None(Vector3.zero);
boardView?.VisualState.ClearHover();
return false;
}
boardView.VisualState.SetHover(hit.Coordinate);
return true;
}
public (GridActionResult Preview, GridActionResult Applied) PreviewAndApply(
IGridAction action)
{
boardView ??= GetComponent<GridBoardView>();
if (boardView?.State == null)
throw new InvalidOperationException("Bind a ready board before interacting.");
GridActionResult preview = GridActionRunner.Preview(boardView.State, action);
boardView.VisualState.SetActionPreview(action, preview);
if (!preview.Success)
return (preview, null);
GridActionResult applied = GridActionRunner.Apply(boardView.State, action);
boardView.VisualState.SetActionResult(action, applied);
return (preview, applied);
}
public void ClearInteractionFeedback()
{
boardView ??= GetComponent<GridBoardView>();
boardView?.ClearTransientVisuals(GridTransientVisualClearOptions.All);
}
private void OnDisable()
{
ClearInteractionFeedback();
}
}
}
Result
Canvas input resolves to a logical hit, temporary visual channels update without changing revision, and cleanup removes the feedback owned by the interaction. A gameplay action is applied only after the project accepts the target.
Troubleshooting
| Symptom | Check |
|---|---|
| A direct screen-position query misses | Check the view, renderer readiness, screen coordinates, and Canvas camera mapping. |
| Unity pointer callbacks never arrive | Check the EventSystem, input module, and the project's event-routing or raycast components. |
| Decorative content receives the hit | Disable unnecessary raycast targets and use the renderer's logical mapping. |
| Dragging changes state before drop | Keep the drag presenter temporary and apply only the accepted action or operation. |
| Interaction visuals remain after cancel | Clear hover, selection, placement, and project-owned channels from the same owner lifecycle. |