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 Stable-tier in 1.0.0.
Temporary drag presentation
The drag presenter owns its temporary Canvas content. Cancel or Hide clears it. Disabling the
component or its GameObject hides and clears the content; re-enabling does not restore the old
drag. Destroying the presenter destroys its Canvas child even when that child is parented outside
the presenter's GameObject. Source disposal, view rebinding or loss of a ready view cancels the
presentation; source deactivation is also checked each frame. A hidden or disabled presenter does
not resume a logical drag session automatically.
Keep one event camera appropriate to the Canvas render mode and let logical hit resolution select the destination view. Overlapping board views require deliberate priority and raycast settings. Decorative drag graphics do not capture pointer hits. Assign a label font when structure visuals contain text; the Canvas setup recipe provides Unity's built-in font.
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. |