如何試用 TransformAccessArray | 新增/移除 Transform 到 TransformAccessArray中執行
以下是尋找最近目標的示例。
using System.Collections.Generic; using UnityEngine; using UnityEngine.Jobs; using Unity.Mathematics; using Unity.Jobs; using Unity.Collections; using Unity.Profiling; public class FindNearestTargets : 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; } }