Gravity Sim Job System - Video 05 - GravityController and Using the Code

YouTube Video #5 - Gravity Series

Overview

The GravityController is to be attached to an empty GameObject and is intended to manage the GravitySystem in terms of the Unity game engine lifecycle using the MonoBehaviour signalling methods that are standard for Unity:

  • at Start(), the system is created
  • at Update(), the system is told to update which means to start a new data pipeline run if the prior one had completed
  • at OnDestry(), the system is told to dispose of itself and all resources.

The other thing the controller does is work with the Unity Editor. This means that prefabs with a GravityBehaviour can be registered to be created at start. The controller finds the prefabs and any other objects in the scene with the GravityBehaviour and adds them to the simulation. It also makes sure their gravity settings are sufficiently valid to start the simulation (mass > 0, position matches position in scene).

Code

GravityController.cs

using Unity.Mathematics;
using UnityEngine;

[AddComponentMenu("DesignerTech/Gravity Controller")]
public class GravityController : MonoBehaviour
{
    [Tooltip("How many and what prefabs to use")]
    [SerializeField] GravityBehaviour[] _GravityPrefabs = null;

    [Tooltip("The same number applied to each prefab")]
    [SerializeField] int _NumberInstancesPerPrefab = 3;

    [SerializeField] float _RepulsionDistance = 0.5f;
    [SerializeField] float _GravityStrength = 100f;
    [SerializeField] float _MinPos = -50f;
    [SerializeField] float _MaxPos = 50f;

    private GravitySystem _System;

    void Start()
    {
        if (IsPrefabsReady())
        {
            CreatePrefabInstances();
        }
        var objects = CollectAllObjectsInSceneIncludingPrefabs();
        EnsureGravityComponentHasValidInitValues(objects);
        if (objects != null && objects.Length > 0)
        {
            _System = new GravitySystem(objects);
        }
        else
        {
            _System = null;
            Debug.LogWarning("No GravityBehaviours found in scene at all");
        }
    }

    void Update()
    {
        _System?.UpdateSystem(_RepulsionDistance, _GravityStrength);
    }

    private void OnDestroy()
    {
        _System.Dispose();
    }

    private bool IsPrefabsReady() => _GravityPrefabs != null && _GravityPrefabs.Length > 0;

    private GravityBehaviour[] CollectAllObjectsInSceneIncludingPrefabs()
        => GameObject.FindObjectsOfType<GravityBehaviour>();

    private void EnsureGravityComponentHasValidInitValues(GravityBehaviour[] entities)
    {
        for(int index = 0; index < entities.Length; index++)
        {
            var _transform = entities[index].GetComponent<Transform>();
            var _settings = entities[index].GravitySettings;
            if (_settings.Mass == 0f)
            {
                _settings.Mass = 1f;
            }
            _settings.Position = _transform.position;
            entities[index].GravitySettings = _settings;
        }
    }

    private void CreatePrefabInstances()
    {
        for (int prefabIndex = 0; prefabIndex < _GravityPrefabs.Length; prefabIndex++)
        {
            CreateNumberOfPrefabs(_GravityPrefabs[prefabIndex]);
        }
    }

    private void CreateNumberOfPrefabs(GravityBehaviour prefab)
    {
        for (int count = 0; count < _NumberInstancesPerPrefab; count++)
        {
            var settings = GameObject.Instantiate(prefab);
            settings.transform.position = RandomPos();
            if (settings.GravitySettings.Mass == 0)
            {
                settings.GravitySettings.Mass = 1f;
            }
        }
    }

    private float3 RandomPos()
        => new float3(
            UnityEngine.Random.Range(_MinPos, _MaxPos),
            UnityEngine.Random.Range(_MinPos, _MaxPos),
            UnityEngine.Random.Range(_MinPos, _MaxPos)
        );
}