Last active
March 26, 2024 21:51
-
-
Save andrew-raphael-lukasik/d65b2d97d88767b938cf226c15216480 to your computer and use it in GitHub Desktop.
job system for basic steering behavior at scale
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src*: https://gist.github.com/andrew-raphael-lukasik/d65b2d97d88767b938cf226c15216480 | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Jobs; | |
using Unity.Mathematics; | |
using Unity.Jobs; | |
using Unity.Collections; | |
using Unity.Transforms; | |
public class CarsAISystem : MonoBehaviour | |
{ | |
[SerializeField] GameObject _carPrefab = null; | |
[SerializeField] float _topSpeed = 4; | |
[SerializeField] float _acceleration = 2; | |
[SerializeField] float _turningRate = 1; | |
[SerializeField][Range(0,1)] float _performanceVariation = 0.2f; | |
[SerializeField] int _numberOfCarsToSpawn = 10; | |
Transform[] _cars = null; | |
TransformAccessArray _carsTransformAccess; | |
NativeArray<int> _carWaypointData; | |
NativeArray<float> _carSpeedData; | |
NativeArray<float> _carPerformanceData; | |
[SerializeField] Transform[] _waypoints = new Transform[2]; | |
NativeArray<float3> _pathVertices; | |
JobHandle Dependency = default(JobHandle); | |
void Awake () | |
{ | |
_cars = new Transform[ _numberOfCarsToSpawn ]; | |
for( int i=0 ; i<_numberOfCarsToSpawn ; i++ ) | |
{ | |
_cars[i] = GameObject.Instantiate( | |
original: _carPrefab , | |
position: (float3)UnityEngine.Random.insideUnitSphere * new float3{ x=30 , y=0 , z=30 } , | |
rotation: Quaternion.identity | |
).transform; | |
} | |
_carsTransformAccess = new TransformAccessArray( _cars ); | |
_carSpeedData = new NativeArray<float>( _cars.Length , Allocator.Persistent ); | |
_carPerformanceData = new NativeArray<float>( _cars.Length , Allocator.Persistent ); | |
for( int i=0 ; i<_carPerformanceData.Length ; i++ ) | |
_carPerformanceData[i] = UnityEngine.Random.Range( 1f-_performanceVariation , 1f+_performanceVariation ); | |
_pathVertices = new NativeArray<float3>( _waypoints.Length , Allocator.Persistent ); | |
for( int i=0 ; i<_pathVertices.Length ; i++ ) | |
_pathVertices[i] = _waypoints[i].position; | |
_carWaypointData = new NativeArray<int>( _cars.Length , Allocator.Persistent ); | |
for( int i=0 ; i<_carWaypointData.Length ; i++ ) | |
_carWaypointData[i] = UnityEngine.Random.Range( 0 , _pathVertices.Length ); | |
} | |
void OnDestroy () | |
{ | |
Dependency.Complete(); | |
if( _pathVertices.IsCreated ) _pathVertices.Dispose(); | |
if( _carsTransformAccess.isCreated ) _carsTransformAccess.Dispose(); | |
if( _carWaypointData.IsCreated ) _carWaypointData.Dispose(); | |
if( _carSpeedData.IsCreated ) _carSpeedData.Dispose(); | |
if( _carPerformanceData.IsCreated ) _carPerformanceData.Dispose(); | |
} | |
void Update () | |
{ | |
Dependency.Complete(); | |
var job = new FollowWaypoints{ | |
DeltaTime = Time.deltaTime , | |
TopSpeed = _topSpeed , | |
Acceleration = _acceleration , | |
TurningRate = _turningRate , | |
Path = _pathVertices , | |
Waypoint = _carWaypointData , | |
Speed = _carSpeedData , | |
Performance = _carPerformanceData | |
}; | |
Dependency = job.Schedule( _carsTransformAccess , Dependency ); | |
} | |
#if UNITY_EDITOR | |
void OnDrawGizmos () | |
{ | |
int length = _waypoints.Length; | |
for( int i=0 ; i<length ; i++ ) | |
{ | |
var a = _waypoints[i]; | |
var b = _waypoints[(i+1)%length]; | |
if( a && b ) Gizmos.DrawLine( a.position , b.position ); | |
} | |
} | |
#endif | |
[Unity.Burst.BurstCompile] | |
public struct FollowWaypoints : IJobParallelForTransform | |
{ | |
public float DeltaTime, TopSpeed, Acceleration, TurningRate; | |
[NativeDisableParallelForRestriction] public NativeArray<float3> Path; | |
public NativeArray<int> Waypoint; | |
public NativeArray<float> Speed; | |
public NativeArray<float> Performance; | |
void IJobParallelForTransform.Execute ( int index , TransformAccess transform ) | |
{ | |
float3 position = transform.position; | |
float3 forward = math.mul( transform.rotation , new float3{ z=1 } ); | |
float performance = Performance[index]; | |
float3 waypointPos = Path[ Waypoint[index] ]; | |
// switch waypoints: | |
if( math.lengthsq(waypointPos-position)<math.pow(Speed[index],2f) ) | |
{ | |
// will be there in about a second, lets select next waypoint | |
int next = ( Waypoint[index] +1 )%Path.Length; | |
Waypoint[index] = next; | |
waypointPos = Path[next]; | |
} | |
// steering control: | |
float3 desiredForward = math.normalize( waypointPos - position ); | |
float makeSteeringDecreseWithSpeed = math.lerp( 2f , 1f , math.saturate(math.pow(Speed[index]/TopSpeed,2f)) ); | |
transform.rotation = math.slerp( | |
transform.rotation , quaternion.LookRotation( desiredForward , new float3{y=1} ) , | |
math.saturate( makeSteeringDecreseWithSpeed * TurningRate*performance * DeltaTime ) | |
); | |
// speed control: | |
float makeDesiredSpeedDependOnAngleToTarget = math.dot( forward , desiredForward ); | |
float desiredSpeed = TopSpeed*performance * makeDesiredSpeedDependOnAngleToTarget; | |
float desiredSpeedDiff = desiredSpeed - Speed[index]; | |
Speed[index] += math.min( math.abs(desiredSpeedDiff) , Acceleration*performance ) * math.sign(desiredSpeedDiff) * DeltaTime; | |
transform.position += (Vector3)( forward * Speed[index] * DeltaTime ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment