Hey all,
TLDR; If I have an aspect that gets an objects Entity ref, LocalTransform and empty TagComponent, how can I get the non-uniform transform for it’s child object for updating the position, rotation and scale?
First off, I’m find it very hard to absorb info through reading alone - I have to get hands on to get the intuition of something.
So, expanding Unity’s Jobs example from their GitHub, I’m replicating the functionality to work through ECS as I learn more about the overall paradigm.
My latest problem is ‘drawing’ the lines between the blue and red cubes. Line Renderers as, AFAIK, not in ECS yet, so I’m going to fudge it with a stretched quad.
I’m currently using an Aspect (to learn about those) with a passed sorted NativeArray for the “FindNearestRedCube” system.
Current Performance: So far I have 5k red and 5k blue cubes (with ‘line’ quads) running at approx. 60fps with distance checks
Current Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Burst;
using Unity.Collections;
using Unity.Transforms;
using Unity.Mathematics;
using System.Linq;
using Unity.Jobs;
using System;
public partial struct FindNearestSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState _state)
{
_state.RequireForUpdate<BlueCubeComponentTag>();
_state.RequireForUpdate<RedCubeComponentTag>();
}
//[BurstCompile] - Can't Burst this as errors: "Reflection data was not set up by an Initialize() call. Support for
// burst compiled calls to Schedule depends on the Collections package."
public void OnUpdate(ref SystemState _state)
{
//1......................................
// Get the positions of all the red tags.
int _queryLength = 0;
foreach (var _lt in SystemAPI.Query<RefRO<LocalTransform>>().WithAll<RedCubeComponentTag>())
_queryLength++;
NativeArray<float3> _positions = new NativeArray<float3>(_queryLength, Allocator.TempJob);
int _naIndex = 0;
foreach (var _lt in SystemAPI.Query<RefRO<LocalTransform>>().WithAll<RedCubeComponentTag>())
_positions[_naIndex++] = _lt.ValueRO.Position;
//2......................................
// Sort and merge the list
SortJob<float3, AxisXComparer> _sortJob = _positions.SortJob(new AxisXComparer { });
JobHandle _sortJobHandle = _sortJob.Schedule();
// Run an entity aspect on all blue cubes with the positions
JobHandle _findJobHandle = new FindAndDrawToNearestJob
{
Positions = _positions
}.ScheduleParallel<FindAndDrawToNearestJob>(_sortJobHandle);
_findJobHandle.Complete();
// Dispose of all that was needed
_positions.Dispose();
}
[BurstCompile]
partial struct FindAndDrawToNearestJob : IJobEntity
{
[ReadOnly] public NativeArray<float3> Positions;
public void Execute(BlueCubeFindNearestAspect _aspect)
{
_aspect.FindNearest(Positions);
}
}
[BurstCompile]
readonly public partial struct BlueCubeFindNearestAspect : IAspect
{
public readonly Entity Self;
public readonly RefRO<LocalTransform> Transform;
public readonly RefRO<BlueCubeComponentTag> BlueTag;
public void FindNearest(in NativeArray<float3> _redPositions)
{
// Get closest target pos...
// Try to get exact position;
int _startIndex = _redPositions.BinarySearch(Transform.ValueRO.Position, new AxisXComparer { });
// if <1, then exact not found. Therefore, flip bits to reveal closest ;)
if (_startIndex < 0) _startIndex = ~_startIndex;
if (_startIndex >= _redPositions.Length) _startIndex = _redPositions.Length - 1; // Clamp to stop OOB
if (_startIndex < 0)
{
Debug.LogError("The index is -1");
return;
}
float3 _nearestPos = _redPositions[_startIndex];
float _nearestDistSqrd = math.distancesq(_nearestPos, Transform.ValueRO.Position);
// Search up, then down for the nearest position;
// Upwards:
for (int i = _startIndex; i < _redPositions.Length; i++)
{
float _xDiff = Transform.ValueRO.Position.x - _redPositions[i].x;
if ((_xDiff * _xDiff) > _nearestDistSqrd) break; // We're going beyond our current min range, so bug out
float _newDistSq = math.distancesq(_redPositions[i], Transform.ValueRO.Position);
if (_newDistSq < _nearestDistSqrd)
{
_nearestDistSqrd = _newDistSq;
_nearestPos = _redPositions[i];
}
}
// Now downwards
for (int i = _startIndex; i >= 0; i--)
{
float _xDiff = Transform.ValueRO.Position.x - _redPositions[i].x;
if ((_xDiff * _xDiff) > _nearestDistSqrd) break; // We're going beyond our current min range, so bug out
float _newDistSq = math.distancesq(_redPositions[i], Transform.ValueRO.Position);
if (_newDistSq < _nearestDistSqrd)
{
_nearestDistSqrd = _newDistSq;
_nearestPos = _redPositions[i];
}
}
// Move child objects and scale accordingly.
// Get Child Ref
// Move the child to halways between this cube and nearest position.
// Rotate to 'point' to nearest position
// Stretch to void the void in my soul...
}
}
[BurstCompile]
public struct AxisXComparer : IComparer<float3>
{
public int Compare(float3 _x, float3 _y)
{
return _x.x.CompareTo(_y.x);
}
}
}
In the above code, I was previously thinking that I could get the Child entity through Aspect code gen by adding it as a readonly public RefRW<>, however this produced a boxing error (Child is a buffer, not a component as per normal?).
I then decided to try taking my clue cube ref and use GetDynamicBuffer, but you can’t use SystemAPI outside of a system(?!?).
So, I’m a little stuck in the mud. Any pointers or help in clearing the fog would be greatly appreciated.
Please note, I’m going through the documentation (I understand it’s a WIP as DOTS development continues), as well as example code… but as mentioned, it can only penetrate my dense brain so far before I need the gentle lubricant of assistance/patient intuition training. That being said, if there are good links/articles that can be sent over I will read those ![]()
Regards,
Mike
P.S. Any insight on the error I get when I try to Burst Compile the OnUpdate method would be a bonus, but no worries if not ![]()
