Low-level API
The components are built on a small set of building blocks that you can use directly: for a one-off visibility check without a Vision Source, for your own jobs, or for AI logic outside of MonoBehaviours. Most games do not need this page.
Building blocks
| Type | Namespace | Description |
|---|---|---|
Segment | Renvix.VisionFog.Core | One obstacle edge: points A and B in plane coordinates, the Owner collider's instance id, and its Transmission (0 = blocks, 1 = see-through) |
VisionShape | Renvix.VisionFog.Core | Where the eye is and what it can see before obstacles: Origin, Radius, Direction and HalfAngle in radians. Create it with VisionShape.Circle(origin, radius) or VisionShape.Cone(origin, radius, forward, fieldOfViewDegrees) |
VisibilitySolver | Renvix.VisionFog.Core | Computes the outline of the visible area |
VisibilityQuery | Renvix.VisionFog.Core | Checks whether a single point is visible, with the same rules as the solver |
VisibilityBuffers | Renvix.VisionFog.Core | The output and scratch memory of the solver. Dispose it when done |
VisibilityJob | Renvix.VisionFog.Core | A Burst job that runs the solver for one shape |
Occluders2D, Occluders3D | Renvix.VisionFog.Occluders | Collect obstacle segments from 2D physics, or cut 3D colliders at a height |
AwarenessModel | Renvix.VisionFog | The awareness meter as pure logic, without Unity objects |
A one-off visibility check
Can a sniper on a tower see the player, without giving the tower a Vision Source?
using Renvix.VisionFog.Core;
using Renvix.VisionFog.Occluders;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
public static class LineOfSight
{
public static bool Check2D(Vector2 eye, Vector2 target, float range, LayerMask walls, Transform ignore = null)
{
var obstacles = new NativeList<Segment>(64, Allocator.Temp);
Occluders2D.BeginTick();
Occluders2D.Gather(eye, range, walls, includeTriggers: false, obstacles, ignore);
VisionShape shape = VisionShape.Circle(eye, range);
bool visible = VisibilityQuery.IsPointVisible(shape, obstacles.AsArray(), target);
obstacles.Dispose();
return visible;
}
}
The 3D version uses Occluders3D.Gather(center, height, radius, layers, includeTriggers, output, ignoreRoot), where center is the eye's X and Z and height is the eye height at which colliders are cut.
Gather reads physics on the main thread and uses the same collider cache as the Vision Sources. Call Occluders2D.BeginTick() (or Occluders3D.BeginTick()) before it, so colliders that moved since the last vision update are read again. If colliders were moved in the same frame and automatic transform sync is off, also call Physics2D.SyncTransforms() or Physics.SyncTransforms() first.
To skip the target's own collider, pass its instance ids to the IsPointVisible overload with ignoredOwners.
Computing an outline
var buffers = new VisibilityBuffers(64, Allocator.Temp);
VisionShape shape = VisionShape.Cone(eye, 8f, forward, 90f);
VisibilitySolver.Compute(shape, obstacles.AsArray(), math.radians(4f), ref buffers);
// buffers.Boundary: the outline, counter-clockwise, in plane coordinates.
// For a cone the area is the fan: eye -> Boundary[0..n-1] -> eye.
for (int i = 0; i < buffers.Boundary.Length; i++)
{
float2 point = buffers.Boundary[i];
// ...
}
buffers.Dispose();
The third argument is the largest angle between rays along the far, rounded edge, in radians: the same as Edge Step on a Vision Source.
To run the solver on a worker thread, fill a VisibilityJob (Shape, Obstacles, MaxArcStep, Buffers) and schedule it. Create the buffers and the obstacle array with Allocator.TempJob or Allocator.Persistent.
Obstacle cache
Occluders2D and Occluders3D cache the segments of each collider and re-read a collider only when its transform or bounds change. That covers moving, rotating, scaling and resizing colliders. If you change a collider's shape in a way that keeps its transform and bounds, for example moving a point of a polygon collider inside its bounds, tell the cache:
Occluders2D.Invalidate(polygonCollider);
ClearCache() drops all cached colliders, for example after loading a new level additively.
The awareness model
AwarenessModel.Step is the meter that Vision Perception uses, without any Unity objects. Use it for observers that do not see with a Vision Source, for example a guard that "hears" footsteps, or in your own systems and tests.
using Renvix.VisionFog;
AwarenessState state = default;
AwarenessRules rules = profile.Rules; // or AwarenessRules.Default
// Every frame:
float gain = heardSomething ? 0.8f : 0f;
AwarenessChange changes = AwarenessModel.Step(ref state, heardSomething, gain, Time.deltaTime, rules);
if ((changes & AwarenessChange.BecameAlerted) != 0)
RaiseAlarm();
AwarenessState holds Value (0 to 1), Level, Visible and TimeSinceSeen. AwarenessChange is a set of flags: Spotted, Lost, BecameSuspicious, BecameAlerted and Calmed, several of which can be raised in one step.