Gravity Sim Job System - Video 04 - GravitySystem
In this video, we discuss the GravitySystem class and how it completes our mini-ECS
implementation and its responsibility in the overall program. The system manages the
job code, the data pipeline, the memory and JobHandles needed to make this work.
We should note that this Mini-ECS only solves for one component and one system and
the entities are fixed at start-up. A little more work is required to allow entities
to be added or removed and a fair bit more is needed to support multiple component types
and systems.
That said, it achieves what was intended, demonstrate the use of Unity.Mathematics, albeit in a very focused way, and how entites, components and systems could work with the C# Job System as the foundation.
Concepts
We compare this to the GravityController which is a MonoBehaviour class. The GravityController handles
integration with Unity for Start()
, Update()
and OnDestroy()
lifecycle events. It also create the GravitySystem instance.

The GravitySystem sets the jobs up with a daisy-chain of dependencies to form a data pipeline.
The jobs in the pipeline were discussed in Video 2 and Video 3 but it’s the GravitySystem that
makes them work this way and, in the process, makes the computation and movement of the objects
work completely outside the Unity main thread.
The public methods of the GravitySystem are used by the GravityController to manage the lifecycle
of the system with the lifecycle of the game environment.
Code
Here is the code for the GravitySystem class. Note one thing - you will see a constant declaring the number of threads for the TransformAccessArray - best to think of this as a desired level of concurrency. The Unity engine will apply this based on the number of worker threads it creates and that can differ by CPU.
GravitySystem.cs
using UnityEngine;
using UnityEngine.Jobs;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Collections;
public class GravitySystem : System.IDisposable
{
private NativeArray<GravityComponent> _Components;
private NativeArray<float3> _Forces;
private TransformAccessArray _Transforms;
const int NUMBER_THREADS = 10;
private JobHandle
_ForceJob,
_PhysicsJob,
_TranslationJob;
public GravitySystem(GravityBehaviour[] gravityObjects)
{
_Components = new NativeArray<GravityComponent>(
GetGravityComponents(gravityObjects),
Allocator.Persistent
);
_Transforms = new TransformAccessArray(
GetTransforms(gravityObjects), NUMBER_THREADS
);
_Forces = new NativeArray<float3>(gravityObjects.Length, Allocator.Persistent);
}
public void UpdateSystem(float repulsionRadius, float gravityStrength)
{
if ( IsPreviousPipelineRunComplete() )
{
UnlockAllArrays();
UpdateForcesJob(repulsionRadius, gravityStrength);
UpdatePhysicsJob();
UpdateTranslationsJob();
}
}
public void Dispose()
{
JobHandle.ScheduleBatchedJobs(); /// Force pending jobs to begin
UnlockAllArrays();
_Forces.Dispose();
_Components.Dispose();
_Transforms.Dispose();
}
private bool IsPreviousPipelineRunComplete()
=> _ForceJob.IsCompleted
&& _PhysicsJob.IsCompleted
&& _TranslationJob.IsCompleted;
private void UnlockAllArrays()
=> JobHandle.CompleteAll(ref _ForceJob, ref _PhysicsJob, ref _TranslationJob);
private void UpdateForcesJob(float repulsionRadius, float gravityStrength)
=> _ForceJob = GravityForceJob.Begin(
_Components,
_Forces,
repulsionRadius,
gravityStrength
);
private void UpdatePhysicsJob()
=> _PhysicsJob = GravityAccelerationVelocityAndPositionJob.Begin(
_Forces, _Components, _ForceJob
);
private void UpdateTranslationsJob()
=> _TranslationJob = TranslationJob.Begin(
_Components, _Transforms, _PhysicsJob
);
private T[] GetDataFromBehaviour<T>(
GravityBehaviour[] behavioursArray,
System.Func<GravityBehaviour, T> query
)
{
T[] data = new T[behavioursArray.Length];
for (int index = 0; index < behavioursArray.Length; index++)
{
data[index] = query( behavioursArray[index] );
}
return data;
}
private GravityComponent[] GetGravityComponents(GravityBehaviour[] behavioursArray)
=> GetDataFromBehaviour<GravityComponent>(
behavioursArray, (behaviour) => behaviour.GravitySettings
);
private Transform[] GetTransforms(GravityBehaviour[] behavioursArray)
=> GetDataFromBehaviour<Transform>(
behavioursArray, (behaviour) => behaviour.GetComponent<Transform>()
);
}