Appearance
Usage Guide
One-shot query (allocating)
csharp
using Carrot.Interaction;
InteractionSettings settings = InteractionSettings.Default;
settings.MaxDistance = 10f;
InteractionResult result = Interaction3D.FromScreen(Camera.main, screenPosition, settings);
if (result.Hit)
{
result.Reactor.OnInteract(/* context */);
}
// Everything the probe touched, near to far — including non-reactive blockers:
foreach (InteractionHit hit in result.All)
{
Debug.Log($"{hit.GameObject.name} @ {hit.Distance} (reactor: {hit.IsReactor})");
}Per-frame query (zero-alloc)
Pre-allocate the buffers once and reuse them:
csharp
private readonly RaycastHit[] raycasts = new RaycastHit[16];
private readonly InteractionHit[] hits = new InteractionHit[16];
void Update()
{
int count = Interaction3D.FromRayNonAlloc(ray, settings, raycasts, hits,
out InteractionHit reactor, out int reactorIndex);
if (reactorIndex >= 0)
{
// reactor.Receiver is the thing that should react
}
}Interaction2D.FromScreen (overlap a sprite) and InteractionUI.FromScreen (EventSystem) share the same InteractionResult shape. UI is allocating only — the EventSystem raycast allocates internally.
Sphere vs ray, blocking, masks
csharp
settings.Radius = 0.25f; // > 0 → sphere cast instead of a thin ray
settings.BlockedByCollision = true; // a nearer non-reactor (a wall) hides the reactor behind it
settings.Layers = LayerMask.GetMask("Interactable", "Default");
settings.Mask = InteractionMask.Channel(0) | InteractionMask.Channel(2);A receiver responds only when settings.Mask overlaps its own InteractionMask. The LayerMask culls the physics cast; the InteractionMask decides which of the hit things actually react.
Components
csharp
// On the interactor (e.g. the player / camera):
var host = gameObject.AddComponent<Interaction3DHost>();
// Wire the gesture layer to it (input stays decoupled):
PointerGestureSource.Gesture.Add(g =>
{
host.ScreenPosition = g.Position; // ScreenPoint mode
if (g.Phase == PointerGesturePhase.Clicked) host.Interact();
});
// React globally:
Interaction3DHost.FocusGained.Add(ctx => /* highlight ctx... */ );Put an InteractionReceiver on anything reactive (it needs a Collider / Collider2D / Graphic to be hit), set its mask, and wire the UnityEvents or subclass and override OnFocus / OnUnfocus / OnInteract.
"Has the camera clearly seen it?"
csharp
var eye = Camera.main.gameObject.AddComponent<InteractionCamera>();
eye.Register(door.transform);
// Later — drive a state machine / sequence off it:
InteractionCamera.TargetSeen.Add(t => Debug.Log($"Player has clearly seen {t.name}"));
// or poll: if (eye.HasSeen(door.transform)) { ... }"Clearly seen" = inside the frustum, within range, optionally unoccluded and large enough on screen, held continuously for the dwell time.