Last active
April 23, 2024 21:39
-
-
Save andrew-raphael-lukasik/e9c3c1c85a149d2c041532e3632fe4e3 to your computer and use it in GitHub Desktop.
| How to use TransformAccessArray | How to add Transform to TransformAccessArray mid execution | How to remove Transform from TransformAccessArray mid execution |
This file contains hidden or 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
| // web* src = https://gist.github.com/andrew-raphael-lukasik/e9c3c1c85a149d2c041532e3632fe4e3 | |
| using System.Collections.Generic; | |
| using UnityEngine; | |
| using UnityEngine.Jobs; | |
| using Unity.Mathematics; | |
| using Unity.Jobs; | |
| using Unity.Collections; | |
| using Unity.Profiling; | |
| public class LetFindNearestTargets : MonoBehaviour | |
| { | |
| public List<Transform> | |
| troopsToAdd = new List<Transform>() , | |
| troopsToRemove = new List<Transform>() , | |
| targetsToAdd = new List<Transform>() , | |
| targetsToRemove = new List<Transform>(); | |
| TransformAccessArray _troopsDataAccess, _targetsDataAccess; | |
| NativeList<float3> _troopPositions, _targetPositions; | |
| NativeList<int> _nearestTargetIndices; | |
| public JobHandle Dependency; | |
| ProfilerMarker | |
| ___proc_results = new ProfilerMarker("process job results") , | |
| ___maintain_data_access = new ProfilerMarker("maintan data access") , | |
| ___schedule_new = new ProfilerMarker("schedule jobs") , | |
| ___schedule = new ProfilerMarker("schedule calls"); | |
| const float k_tick_rate = 0.4f;// tick every 0.4s | |
| void OnEnable () | |
| { | |
| InvokeRepeating( nameof(Tick) , k_tick_rate , k_tick_rate ); | |
| _troopsDataAccess = new TransformAccessArray( 32 ); | |
| _targetsDataAccess = new TransformAccessArray( 32 ); | |
| _troopPositions = new NativeList<float3>( 32 , Allocator.Persistent ); | |
| _targetPositions = new NativeList<float3>( 32 , Allocator.Persistent ); | |
| _nearestTargetIndices = new NativeList<int>( 32 , Allocator.Persistent ); | |
| } | |
| void OnDisable () | |
| { | |
| CancelInvoke(); | |
| Dependency.Complete(); | |
| if( _troopsDataAccess.isCreated ) _troopsDataAccess.Dispose(); | |
| if( _targetsDataAccess.isCreated ) _targetsDataAccess.Dispose(); | |
| if( _troopPositions.IsCreated ) _troopPositions.Dispose(); | |
| if( _targetPositions.IsCreated ) _targetPositions.Dispose(); | |
| if( _nearestTargetIndices.IsCreated ) _nearestTargetIndices.Dispose(); | |
| } | |
| #if UNITY_EDITOR | |
| void OnDrawGizmos () | |
| { | |
| if( !Application.isPlaying ) return; | |
| Gizmos.color = Color.blue; | |
| for( int i=_troopsDataAccess.length-1 ; i!=-1 ; i-- ) | |
| Gizmos.DrawWireCube( _troopsDataAccess[i].position , Vector3.one ); | |
| Gizmos.color = Color.red; | |
| for( int i=_targetsDataAccess.length-1 ; i!=-1 ; i-- ) | |
| Gizmos.DrawWireSphere( _targetsDataAccess[i].position , 1f ); | |
| } | |
| #endif | |
| void Tick () | |
| { | |
| Dependency.Complete(); | |
| // process results | |
| ___proc_results.Begin(); | |
| { | |
| // don't do this like I'm doing here, it's just for quick & dirty viz | |
| // i.e. avoid reading/writing from native collection outside jobs, it very slow (especially in big loops) | |
| int numResults = _nearestTargetIndices.Length; | |
| for( int i=0 ; i<numResults ; i++ ) | |
| { | |
| int nearestTargetIndex = _nearestTargetIndices[i]; | |
| Debug.DrawLine( _troopPositions[i] , _targetPositions[nearestTargetIndex] , Color.yellow , k_tick_rate ); | |
| // Transform troop = _troopsDataAccess[i]; | |
| // Transform target = _targetsDataAccess[nearestTargetIndex]; | |
| } | |
| } | |
| ___proc_results.End(); | |
| // TransformAccessArray maintainance | |
| ___maintain_data_access.Begin(); | |
| { | |
| int numTroopsToRemove = troopsToRemove.Count; | |
| if( numTroopsToRemove!=0 ) | |
| { | |
| for( int i=0 ; i<numTroopsToRemove ; i++ ) | |
| { | |
| Transform next = troopsToRemove[i]; | |
| if( next!=null ) | |
| for( int k=_troopsDataAccess.length-1 ; k!=-1 ; k-- ) | |
| { | |
| if( _troopsDataAccess[k]==next ) | |
| { | |
| _troopsDataAccess.RemoveAtSwapBack( k ); | |
| break; | |
| } | |
| } | |
| } | |
| troopsToRemove.Clear(); | |
| } | |
| int numTroopsToAdd = troopsToAdd.Count; | |
| if( numTroopsToAdd!=0 ) | |
| { | |
| for( int i=0 ; i<numTroopsToAdd ; i++ ) | |
| if( troopsToAdd[i]!=null ) | |
| _troopsDataAccess.Add( troopsToAdd[i] ); | |
| troopsToAdd.Clear(); | |
| } | |
| int numTargetsToRemove = targetsToRemove.Count; | |
| if( numTargetsToRemove!=0 ) | |
| { | |
| for( int i=0 ; i<numTargetsToRemove ; i++ ) | |
| { | |
| Transform next = targetsToRemove[i]; | |
| if( next!=null ) | |
| for( int k=_targetsDataAccess.length-1 ; k!=-1 ; k-- ) | |
| { | |
| if( _targetsDataAccess[k]==next ) | |
| { | |
| _targetsDataAccess.RemoveAtSwapBack( k ); | |
| break; | |
| } | |
| } | |
| } | |
| targetsToRemove.Clear(); | |
| } | |
| int numTargetsToAdd = targetsToAdd.Count; | |
| if( numTargetsToAdd!=0 ) | |
| { | |
| for( int i=0 ; i<numTargetsToAdd ; i++ ) | |
| if( targetsToAdd[i]!=null ) | |
| _targetsDataAccess.Add( targetsToAdd[i] ); | |
| targetsToAdd.Clear(); | |
| } | |
| } | |
| ___maintain_data_access.End(); | |
| // schedule new jobs: | |
| ___schedule_new.Begin(); | |
| int numTroops = _troopsDataAccess.length; | |
| int numTargets = _targetsDataAccess.length; | |
| if( numTroops!=0 && numTargets!=0 ) | |
| { | |
| ___schedule.Begin(); | |
| _troopPositions.Length = numTroops; | |
| _nearestTargetIndices.Length = numTroops; | |
| _targetPositions.Length = numTargets; | |
| JobHandle troopPositionsJobHandle = new ReadPositionsJob{ Results = _troopPositions } | |
| .Schedule( _troopsDataAccess ); | |
| JobHandle targetPositionsJobHandle = new ReadPositionsJob{ Results = _targetPositions } | |
| .Schedule( _targetsDataAccess ); | |
| var findNearestTargetJob = new FindNearestTargetJob | |
| { | |
| Origins = _troopPositions , | |
| Targets = _targetPositions , | |
| Results = _nearestTargetIndices , | |
| }; | |
| Dependency = JobHandle.CombineDependencies( troopPositionsJobHandle , targetPositionsJobHandle , Dependency ); | |
| Dependency = findNearestTargetJob.Schedule( | |
| arrayLength: numTroops , | |
| innerloopBatchCount: math.max( numTroops/SystemInfo.processorCount/4 , 1 ) , | |
| dependsOn: Dependency | |
| ); | |
| ___schedule.End(); | |
| } | |
| ___schedule_new.End(); | |
| } | |
| } | |
| [Unity.Burst.BurstCompile] | |
| public struct ReadPositionsJob : IJobParallelForTransform | |
| { | |
| [WriteOnly] public NativeArray<float3> Results; | |
| void IJobParallelForTransform.Execute ( int index , TransformAccess transform ) | |
| => Results[index] = transform.position; | |
| } | |
| [Unity.Burst.BurstCompile] | |
| public struct FindNearestTargetJob : IJobParallelFor | |
| { | |
| [ReadOnly] public NativeList<float3> Origins;// reads one per jobIndex | |
| [ReadOnly][NativeDisableParallelForRestriction] public NativeList<float3> Targets;// reads all per jobIndex | |
| [WriteOnly] public NativeArray<int> Results; | |
| void IJobParallelFor.Execute ( int jobIndex ) | |
| { | |
| float3 origin = Origins[jobIndex]; | |
| int candidate = -1; | |
| float candidateDistSq = float.PositiveInfinity; | |
| for( int i=0 ; i<Targets.Length ; i++ ) | |
| { | |
| float distSq = math.distancesq( origin , Targets[i] ); | |
| if( distSq<candidateDistSq ) | |
| { | |
| candidateDistSq = distSq; | |
| candidate = i; | |
| } | |
| } | |
| Results[jobIndex] = candidate; | |
| } | |
| } |
Author
andrew-raphael-lukasik
commented
Apr 30, 2022

Author
TODO:
- refactor
FindNearestTargetJobto use a quadtree https://gist.github.com/andrew-raphael-lukasik/02cd430757421f15a21ef23aa7368fb3
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment