Hi, wanted to share a simple event system for DOTS. You can use it directly or draw inspiration from it.
It’s basically three simple methods in a helper-system, to create a new signal, ask for a specific signal, or ask for the value of a specific signal.
What it does is create an entity of SignalComponent whenever SetSignal is called, reusing an old one if present. The Get’ers use LastSystemVersion of the calling system, to find events that are newer than last run.
All feedback and suggestions welcome. The code is as simple as its writer, I’m still a noob programmer.
*** Update 2021.04.05 ***
- Changed the logic of reusing existing entities when creating a new signal, and added a static variable that can changed based on need; reuseOldSignalsThresholdMultiplier
This multiplier is used on the delta of SignalSystem’s own version, which now runs once per frame. That means the variable is approximately a frame, and thus a value of 2 should cover most situations, and should be increased if there are systems not guaranteed to run every cycle.
- Methods are now static
This gives cleaner and easier usage, and a bonus is that change filter on query is working, which is super useful when using the same technique on my Settings helper-system.
Also, GlobalSystemVersion isn’t any more unnecessary +1’d for every call to a method.
I tried to find a way to access the caller system’s LastSystemVersion, so that parameter could be omitted in the call, but I couldn’t get how the EntityQuery access it when checking the filter. Could possibly do it with reflection, but that would kill the speed. Any input welcome.
NOTE: As this is converted to static methods, this will only work on single world, as noted by @tertle . Visit his Event System for a more thorough solution.
Usage:
In your systems, simply call the methods. Concider adding a using static Vildauget.SignalsSystem;
for easy access.
Create signal:
if (jumpInputAction.triggered) SetSignal(Signal.inputJump);
In another system, get the signal with:
var jumping = GetSignal(Signal.inputJump, LastSystemVersion);
The component and enum - add new signal types here:
public struct SignalComponent : IComponentData
{
public Signal signal;
public uint version;
public float value;
}
public enum Signal
{
save = 0,
inputJump = 1,
inputMoveX = 2,
inputMoveY = 3,
inputLookX = 4,
inputLookY = 5,
inputLookToggle = 6,
}
And the helper system:
using Unity.Entities;
using Unity.Collections;
using static Unity.Mathematics.math;
namespace Vildauget
{
public class SignalsSystem : SystemBase
{
static EntityQuery query;
static Entity signalEntity;
static EntityManager manager;
static SignalsSystem signalsSystem;
static uint reuseOldSignalsThresholdMultiplier = 2;
protected override void OnCreate()
{
manager = EntityManager;
query = GetEntityQuery(ComponentType.ReadOnly<SignalComponent>());
signalEntity = manager.CreateEntity(typeof(SignalComponent)); // used to instantiate new entities
signalsSystem = World.GetExistingSystem<SignalsSystem>(); // static reference to access system's LastSystemVersion
}
protected override void OnUpdate() {}
public static bool GetSignal(Signal signal, uint lastUpdate)
{
if (!query.HasFilter())
query.AddChangedVersionFilter(ComponentType.ReadOnly<SignalComponent>());
var signalComponents = query.ToComponentDataArray<SignalComponent>(Allocator.Temp);
for (var i=0; i<signalComponents.Length;i++)
if (signalComponents[i].signal == signal && asint(signalComponents[i].version - lastUpdate) > 0)
return true;
return false;
}
public static float GetSignalValue(Signal signal, uint lastUpdate)
{
if (!query.HasFilter())
query.AddChangedVersionFilter(ComponentType.ReadOnly<Signal>());
var signalComponents = query.ToComponentDataArray<SignalComponent>(Allocator.Temp);
for (var i=0; i<signalComponents.Length;i++)
if (signalComponents[i].signal == signal && asint(signalComponents[i].version - lastUpdate) > 0)
return signalComponents[i].value;
return 0;
}
public static void SetSignal(Signal signal, float value=0)
{
if (query.HasFilter())
query.ResetFilter();
var signalComponents = query.ToComponentDataArray<SignalComponent>(Allocator.Temp);
var signalEntities = query.ToEntityArray(Allocator.Temp);
var systemVersionDelta = manager.GlobalSystemVersion - signalsSystem.LastSystemVersion;
var oldSignalThreshold = manager.GlobalSystemVersion - systemVersionDelta * reuseOldSignalsThresholdMultiplier;
// reuse outdated signal if present
for (var i=0; i < signalComponents.Length; i++)
{
if (asint(oldSignalThreshold - signalComponents[i].version) > 0)
{
manager.SetComponentData(signalEntities[i], new SignalComponent {signal = signal, version = manager.GlobalSystemVersion, value=value});
SetName(signalEntities[i], "Signal: " + signal.ToString());
return;
}
}
// create new entity with signal if outdated signal wasn't found
var newSignalEntity = manager.Instantiate(signalEntity);
manager.SetComponentData(newSignalEntity, new SignalComponent {signal = signal, version = manager.GlobalSystemVersion, value=value});
SetName(newSignalEntity, "Signal: " + signal.ToString());
}
static void SetName(Entity entity, string name)
{
#if UNITY_EDITOR
manager.SetName(entity, name);
#endif
}
}
}