Skip to content

Detector

GameObject-only detector with a pluggable detection strategy, selection step, and reactive lifecycle.

Namespace: Jungle.Core Inherits: ControllableComponent → MonoBehaviour Implements: IDetectorHost, IItemSource<GameObject>

Overview

Detector is the unified, domain-neutral way to find GameObjects in the scene. It combines:

  • A pluggable [IDetectionStrategy] (trigger volume, raycast, proxy, …) that decides how to discover candidates.
  • A single IObjectSelection that transforms each raw candidate into 0..N tracked GameObjects — one step, or a Sequence/Union composite. Steps narrow (filters), redirect (a filter that yields a related object, or Find Related), or broaden (hierarchy walks) — compose them to express patterns like "parents with a Point of Interest" or "children with a Rigidbody". Leave it empty to track every raw detection.
  • Lifecycle processes that begin/end with detection edges (any-available, none-available) and on each add/remove.
  • UnityEvents for designer-friendly hooks on detection edges.

Detectors are pure read-only sources of GameObjects (IItemSource<GameObject>). They never mutate external containers as a side effect — to mirror detection into a container, put a Reactor next to it with a Detector Membership cycle reaction: its On Begin process runs a ContainerCommand with a StoreValueOperation, its On End process a RemoveValueOperation. The detector stays pure; the reaction does the side effect.

For typed component resolution (e.g. "detect every AttachableObject in range"), combine a SmartX value in Dynamic mode with an IComponentLookupStrategy on the consumer side. The detector itself stays domain-neutral.

Selection vs filter semantics

An object that is destroyed or switched off while inside range is dropped automatically, with the same removal events as one that walked out: nothing leaves when a pooled item is returned to its pool where it stands, so the detector checks for that itself. The strategy forgets the object too, so a pooled item that comes back somewhere else in the scene is not picked up from here.

A detector decides on arrival, once, and never changes its mind. A detection strategy presents an object when it enters range; a selection that could later answer differently — Held / Free on an object a claw just grabbed, a tag added at runtime — is not consulted again while the object sits there. This is deliberate: a detector detects, it does not re-detect.

When a verdict has to follow a state change rather than a movement, there are two ways to say so, and which one you want depends on whether you are narrowing a reader or changing availability.

Narrowing a reader: put the condition in the filters list on From Detector, where it runs at read time. The detector's own set and its events are untouched, so only that reader is narrowed. Turn on Keep The Same Pick when the reader is a destination something is already travelling towards.

Changing availability: when the object really should stop being offered by everything, including the add/remove events and any Has Any Detection gate, take it out of the set. Put a Reactor on the object, give it the cycle that fires when the state flips (grabbed, released, attached, detached), and have it run Ignore GameObject on the detector. The object leaves the set at once and comes back on its own when the strategy reports it has left range.

Filtering a reader moves only half the job. Every other consumer keeps reading the raw set, so a detector that used to go empty when nothing qualified never goes empty again. If a condition on the detector was doing double duty as an availability signal, move the gates too — swap Has Any Detection for Any Detected Matches with the same filter.

A single raw detection (one trigger enter, one raycast hit) can produce more than one tracked GameObject when the selection chain includes an expansion step. Example: a raw collider hit + Sequence(HierarchyExpand{Parents}, JungleTagFilter{Grip}) yields every ancestor of the hit object tagged Grip. Each yielded GameObject ref-counts independently — onNewAvailable fires once per unique object, onNotAvailableAnymore fires when the raw hit eventually leaves range and its contribution to each is unwound.

Properties

Detection

Property Type Description
detectionStrategy IDetectionStrategy How this detector discovers candidates (triggers, raycasts, proxy, …).
selectionStrategy IObjectSelection Single selection step applied to each raw detection. Filters, redirects, or expands the candidate. Use a Sequence or Union composite to chain multiple steps. Leave empty to track every raw detection.

Processes

Property Type Description
onAddedProcess IProcess Triggered each time an object is added to this source.
onRemovedProcess IProcess Triggered each time a previously added object is lost.
anyAvailableProcess IProcess Begun when transitioning from none → at least one available. Completed when all are lost.
noneAvailableProcess IProcess Begun when all detected objects have been lost. Completed when a new object is detected.

Events

Property Type Description
onNewAvailable UnityEvent<GameObject> Invoked when an object becomes available.
onNotAvailableAnymore UnityEvent<GameObject> Invoked when a previously available object is no longer valid.
onNoMoreAvailable UnityEvent Invoked when the source has no objects left.

Runtime State

Member Description
Available Iterable of currently-detected objects.
First / Last First / last entry in the available list.
LastAdded / LastRemoved Most recent add / remove.
IsDetecting True when at least one object is available.
DetectedCount Number of currently-detected objects.

Methods

Method Description
IgnoreUntilLost(GameObject) Marks an object as ignored until the strategy reports it has physically left detection range.
IgnoreAllCurrentlyAvailable() Marks every currently-available GameObject as ignored.
ReleaseIgnored(GameObject) Releases the ignore flag.
ReleaseAllIgnored() Releases all ignore flags.
ClearDetected() Clears every detected object and raises matching lost events.

See Also