Fundamentals
Core Concepts
All sensors share some concepts in common:
- A
Sensor
is a Component that has a list of Signals. - Each
Signal
stores one detected GameObject and some data about the objects shape and visibility. - A Sensor can
Pulse
, causing it to run its sensing routine and update its list of Signals. - Some sensors are Compound, they read the Signals from another sensor and run additional detection logic.
There are edge cases, but this is a good framework for understanding the kit. If a sensor deviates then it will be explained on its manual page.
Sensor
Almost all of the sensors derive from a common base class Sensor
. It provides a standard interface for querying what a Sensor
can detect and hooking into its events. You can read its API page here. Only the NavMeshSensor
and SteeringSensor
do not derive from Sensor
.
Signal
A Signal
is a small data structure that references a detected GameObject and holds a bit of information about the detection. The data it holds is as follows:
- Object - The GameObject that is detected.
- Strength - A value representing how visible the signal is.
- Shape - A bounding box approximating the shape that the object appears to be.
A sensor will have at most one Signal
for any GameObject.
Pulse
Most sensors have a Pulse
action that runs its detection routines. A list of signals is generated and compared to the previous list. Events are fired for new detections and for lost detections. You can configure when a sensor will pulse by setting its PulseMode
. The modes available are:
- Each Frame - The sensor will pulse each frame in the Update stage.
- Fixed Interval - The sensor pulses at a regular time interval in seconds. This is recommended where possible for optimising performance. The intervals are staggered with a random offset so sensors with equal intervals are spread across frames.
- Manual - The sensor will only pulse if you invoke its
Pulse()
method via script or PlayMaker.
Scripting API
All classes are in the Micosmo.SensorToolkit
namespace. A simple script that reads a sensor might look like this:
using UnityEngine;
using Micosmo.SensorToolkit;
public class AIBehaviour : MonoBehaviour {
public Sensor sensor;
void Update() {
var pickup = sensor.GetNearestDetection("pickup");
if (pickup != null) {
// Collect it...
}
}
}
Of course it's also possible to subscribe to the OnDetected
and OnLostDetection
events instead so it's not necessary to check the sensor each frame.
Garbage Collector Optimisation
Special care was taken to design SensorToolkit so that it never allocates GC. If you call a function that returns a List
I will have implemented it so the same List
instance is returned each time. The goal being that it's super-convenient to return a list of detections and iterate over them.
List<Signal> enemies = sensor.GetSignals("enemy"); // Get all signals with tag "enemy"
List<Signal> friends = sensor.GetSignals("friend"); // ⚠️ 'enemies' will be overwritten
enemies == friends; // True
List<Signal> items = sensor.GetSignalsByDistance("item"); // ⚠️ different methods will also overwrite the List
enemies == items; // True
List<Signal> coverLocations = coverSensor.GetSignalsByDistance(); // ✔️ no issue here. Each sensor allocates its own reusable list.
To fix this you can write:
// An optional last parameter let's you provide your own instance of List
List<Signal> enemies = sensor.GetSignals("enemy", new List<Signal>());
List<Signal> friends = sensor.GetSignals("friend", new List<Signal>());
enemies == friends; // False