Value Providers¶
The Core Idea¶
Instead of hardcoding a speed, a position, or a color, make it configurable - the user picks the source in the Inspector.
A speed can be a constant number for prototyping, an animation curve that ramps up over time, an Animator parameter driven by state machines, or a shared asset that multiple objects read from. The component using the speed does not care which - it just asks for the current value and gets a number back.
This is what value providers do: they separate what a value is from where it comes from. You configure the source in the Inspector by picking from a popup menu, and the component works with any source you choose.
Example use cases:
- A movement speed that starts constant but later switches to an animation curve for polish
- A target position that can come from a Transform, a spline point, or a raycast hit
- A color that reads from a gradient, a material property, or a shared palette asset
How It Looks in the Inspector¶
A value provider field shows the currently selected source (e.g., "Constant Float"). Click it to switch to a different source. Expand the foldout to configure that source's settings.
For a float value, you might see options like:
- Constant Float - A single number field
- Animation Curve Float - A curve editor and time source
- Transform Distance - Two Transform references and a distance mode
- Random Range - Min and max fields
Practical Example¶
Consider a modifier that moves an object at a configurable speed. Without value providers:
With value providers:
[SerializeReference] [JungleClassSelection]
private IValue<float> speed;
void Update()
{
transform.position += transform.forward * speed.Value() * Time.deltaTime;
}
Now the user can configure speed in the Inspector to come from:
- A constant (5.0) for simple cases
- An AnimationCurve that ramps up over time
- An Animator float parameter driven by animation state
- A ScriptableObject shared across multiple objects
- A Random range for variety
The code does not change. Only the Inspector configuration does.
Data Flow¶
flowchart LR
subgraph Sources
A[Constant]
B[Transform]
C[ScriptableObject]
D[AnimationCurve]
end
E["IValue<float>"]
F[Consumer Component]
A --> E
B --> E
C --> E
D --> E
E -->|"Value()"| F
The consumer only knows about the value interface. The concrete source is selected by the user in the Inspector through the class selection system.
Technical Details¶
IValue<T>¶
The read-only value provider. This is the interface you will encounter most often.
| Member | Description |
|---|---|
Value() |
Returns the current value |
V |
Shorthand property — equivalent to Value() |
Initialize() |
Called once when the value source is first used in a pipeline |
HasMultipleValues |
Whether this provider can return more than one value |
GetValueAt(int index) |
Access individual values for multi-value providers |
ISettableValue<T>¶
Extends IValue<T> with write access. Used for two-way bindings where the consumer needs to push values back.
IValueApplier<T>¶
Applies a value to a target. Used by pipelines to write the final computed value to its destination (e.g., setting a Transform's position).
public interface IValueApplier<T>
{
T CurrentValue { get; }
void Apply(T value);
void Initialize();
void Complete();
}
Common Value Types¶
Value providers exist for every common Unity type, spread across Jungle packages:
| Type | Package | Example Sources |
|---|---|---|
float |
jungle.math | Constant, AnimationCurve, Animator parameter, Random range |
int |
jungle.math | Constant, counter, database lookup |
double |
jungle.math | Constant, high-precision calculations |
Vector3 |
jungle.spatial | Constant, Transform position, spline point |
Quaternion |
jungle.spatial | Constant, Transform rotation |
Color |
jungle.graphics | Constant, material property, gradient sample |
bool |
jungle.core | Constant, toggle state |
string |
jungle.core | Constant, localization key |
GameObject |
jungle.objects | Direct reference, find by tag/name |
Component |
jungle.objects | Direct reference, GetComponent lookup |
Rigidbody |
jungle.physics | Direct reference, velocity accessor |
Value Sources¶
For any given type, multiple source implementations exist. Here are the common patterns:
Constant¶
The simplest provider. Returns a serialized value directly. Good for prototyping or when the value truly is fixed.
From Transform¶
Reads a value from a Transform component — position, rotation, scale, or a derived quantity like forward direction.
From Component Property¶
Reads a property from any component. For example, a Rigidbody's velocity magnitude as a float value.
From ScriptableObject¶
Reads from a shared ScriptableObject asset. Useful when multiple objects need to reference the same configurable value.
From Function¶
Computes the value dynamically. Wraps a delegate or expression — useful for math operations, remapping, or aggregating other values.
Contextual Values¶
Some places in Jungle evaluate a value chain with an implicit target — for example, a GameObjectFilter is asked "is this object valid?" and runs its serialized chain to decide. The object being checked isn't a serialized field anywhere; it's passed at runtime.
Contextual values let designers reach that runtime target from inside the chain.
The pattern¶
The classic example is Filtered Object: an IGameObjectValue that resolves to whichever object the surrounding filter is currently testing.
PhysicsOverlapFilter
└─ sphereRadius (SmartFloat)
└─ Bounds Sphere Radius
└─ Local GameObject Bounds
└─ gameObject ← Filtered Object
When the filter is asked about an object, every node in that chain ultimately reads from "the object being filtered" — so the sphere overlap is sized to that specific object's bounds, automatically. The same filter can be reused on objects of any size without per-object tuning.
When to use it¶
Use a contextual value when:
- The same filter (or condition, or action) is reused across many different runtime targets, and
- A node deep in the chain needs to read a property of whichever target is currently being evaluated.
If the target is fixed at edit time, use a normal serialized field instead — Local GameObject Bounds already takes a regular IGameObjectValue field, so for a static target you wire it to a literal reference.
How the editor surfaces them¶
The picker is context-aware: contextual values only appear in slots whose surrounding chain provides matching context. Inside a GameObjectFilter, the picker for any IGameObjectValue slot offers Filtered Object; outside one, it doesn't appear at all. This means most mis-wirings can't be authored in the first place.
ScriptableObject assets are an exception — values authored inside an SO can be referenced from anywhere, so the picker is permissive there. Inside an SO, contextual values are offered without a guaranteed provider; they resolve at the consuming site at runtime.
For plugin authors¶
An evaluation root declares what context it provides via an attribute on its base class:
[ProvidesEvaluationContext(typeof(FilteredObjectContext),
"the object currently being evaluated by this filter")]
public abstract class GameObjectFilter : IGameObjectFilter { ... }
A contextual value declares what context it reads:
[ConsumesEvaluationContext(typeof(FilteredObjectContext))]
public sealed class FilteredObject : IGameObjectValue
{
public GameObject Value()
=> JungleEvaluationContext.Get<FilteredObjectContext, GameObject>();
}
The runtime push/pop is on the provider — typically a one-line using block in the entry method:
public bool IsGameObjectValid(GameObject go)
{
if (!go) return false;
using (JungleEvaluationContext.Scope<FilteredObjectContext>(go))
return Evaluate(go);
}
Once both attributes and the push are in place, the editor picker filters the value into matching slots automatically — no further wiring needed.
Value References¶
Value references are reactive, scene-side value holders. Where a value source provides a value for a consumer to read, a value reference holds a settable value and fires configured processes whenever it changes. This makes them useful as binding points in a scene: other code writes to the reference, and downstream logic reacts automatically.
ValueReference<T, TValue>¶
The abstract base class. It is a MonoBehaviour that wraps an ISettableValue<T> backing field and a list of IProcess instances.
public abstract class ValueReference<T, TValue> : MonoBehaviour
where TValue : class, ISettableValue<T>
{
public T Value { get; }
public void SetValue(T value);
public void Clear();
}
| Member | Description |
|---|---|
Value |
Returns the current value from the backing settable value |
SetValue(T value) |
Writes a new value. If the value changed, all configured processes are fired |
Clear() |
Resets the held value to its default |
The backing ISettableValue<T> and the process list are both configured in the Inspector through the standard class selection system.
GameObjectValueReference¶
A concrete implementation: ValueReference<GameObject, ISettableGameObjectValue>. Attach it to a GameObject in your scene to create a reactive binding point — other components or commands write a GameObject into it, and the configured processes fire in response.
ComponentReference<TComponent, TValue>¶
A generic base for referencing a Component that directly implements a value interface. It implements ISettableValue<TValue>, bridging a live component reference into the value system so it can be used anywhere a settable value is expected.