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/Unioncomposite. Steps narrow (filters), redirect (a filter that yields a related object, orFind 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. |