#Most efficient way to determine if entity is on the ground/supported?

1 messages · Page 1 of 1 (latest)

eternal marsh
#

I have a entities with physicsColliders (simple capsules), and I need to figure out if they are currently on the ground or not.

Currently I've tried 2 approaches in the hopes of getting one that can run efficiently.

  1. Have a job that on every run, casts a sphere collider down by the character's feet and iterates through all the entities that overlap to find if that array is larger than 1 (min of array 1 because this collider will interact with the current character, which we want to ignore)

  2. Have a job that on every run, sets an enableableComponent for "isGrounded" to false - then turn on collider events for all character physicsColliders - and use the CollisionEventsJob to then watch for collisions and set those entities to have an IsGrounded value of true.

#2 is faster - but I've only tested it for about 50 entities before it starts bringing my system down to about 10fps.

What else do you guys do for efficient ground checks?

olive tendon
#

50 entities for 10 fps seems wrong

#

What is even taking up your frame

eternal marsh
#

that's fair. my system isn't that fast, and I do have a few other heavy jobs that could be optimized to help speed things up (one scans for nearby entities) - and after removing them, I can get upwards of up to 100 entities at 20-30fps. However, after removing them, the next area that is heavy is this "isGround" calculation, so I'm seeking some advice on what others do in this area to help speed things up.

idle salmon
#

You can read sources of character controller how it is finding grounding hit
Almost zero performance impact

lapis finch
#

"100 entities at 20-30fps" still feels wrong. Did you inspect the profiler in timeline view?

#

what are these "heavy jobs that could be optimized"? how could they be such heavy that your fps became so sluggy?

tawny dagger
#

is burst not enabled?

eternal marsh
#

alright, I hear you guys - seems like my baseline for what it should be is very low. Let me optimize a bit and i'll come back with a profiler snapshot. Yes, I've attempted to burstcompile all jobs.

eternal marsh
#

Alright, I need some help with this, here is my job that takes a collider and checks to see if any other colliders are colliding:

` [BurstCompile]
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(MovementSystem))]
public partial struct InAirCalculationSystem : ISystem
{
private EntityQuery entitiesThatNeedMovementQuery;

    public void OnCreate(ref SystemState state)
    {
        entitiesThatNeedMovementQuery = state.GetEntityQuery(
            ComponentType.ReadOnly<EntityInputControls>(),
            ComponentType.ReadOnly<PhysicsCollider>(),
            ComponentType.ReadWrite<LocalTransform>());
    }

    public void OnUpdate(ref SystemState state)
    {
        var entityManager = state.World.EntityManager;
        var ecb = new EntityCommandBuffer(Allocator.TempJob);

        var entityType = entitiesThatNeedMovementQuery.ToEntityArray(Allocator.TempJob);
        var isGroundedArray = new NativeArray<bool>(entityType.Length, Allocator.TempJob);

        var job = new InAirCalculationJob
        {
            Ecb = ecb.AsParallelWriter(),
            Entities = entitiesThatNeedMovementQuery.ToEntityArray(Allocator.TempJob),
            PhysicsWorld = SystemAPI.GetSingleton<PhysicsWorldSingleton>().PhysicsWorld,
            Colliders = SystemAPI.GetComponentLookup<PhysicsCollider>(true),
            Transforms = SystemAPI.GetComponentLookup<LocalTransform>(true),
            IsGroundedArray = isGroundedArray,
        }.ScheduleParallel(entitiesThatNeedMovementQuery, state.Dependency);

        job.Complete();
  
        // ecb.Playback(entityManager);
        // ecb.Dispose();
    }
}`
#

Continued:

`[BurstCompile]
partial struct InAirCalculationJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter Ecb;
[ReadOnly] public PhysicsWorld PhysicsWorld;
public NativeArray<bool> IsGroundedArray;
[ReadOnly] public NativeArray<Entity> Entities;
[ReadOnly] public ComponentLookup<PhysicsCollider> Colliders;
[ReadOnly] public ComponentLookup<LocalTransform> Transforms;

    public void Execute(
        in Entity entity,
        in PhysicsCollider collider,
        in LocalTransform transform,
        [EntityIndexInQuery] int entityInQueryIndex
    )
    {
        // var entity = Entities[index];
        // var collider = Colliders[entity];
        // var transform = Transforms[entity];

        int rigidBodyIndex = PhysicsWorld.GetRigidBodyIndex(entity);
        var castHits = new NativeList<ColliderCastHit>(8, Allocator.Temp);
        var castHitsCollector = new AllHitsCollector<ColliderCastHit>(rigidBodyIndex, 1.0f, ref castHits);

        var perceivedUp = new float3(0, 1, 0);
        var contactTolerance = 1.15f;

        var maxDisplacement = -contactTolerance * perceivedUp;
        ColliderCastInput input = new ColliderCastInput(collider.Value, transform.Position, transform.Position + maxDisplacement);

        PhysicsWorld.CastCollider(input, ref castHitsCollector);

        var isGrounded = castHitsCollector.NumHits == 0;
        Ecb.SetComponentEnabled<IsGrounded>(entityInQueryIndex, entity, true);
    }
}`
#

Here is a snapshot of the profiler - My job does have the burstCompile attributes, and it does appear in the burst inspector - so I'm assuming it's burst. It doesn't show up as green on the timeline, then again, none of my other jobs show as green, they're all blue.

#

What recommendations can you give me that I can look into, to speed this up?

olive tendon
#

Turn burst on to start with

lapis finch
#

this menu must be ✅

idle salmon
eternal marsh
eternal marsh
idle salmon
lapis finch
# eternal marsh Alright, I need some help with this, here is my job that takes a collider and ch...

Take another look at the code, there are some questionable things about it:

  1. state.World is a managed object, can't use it in Burst context. It will prevent Burst from compiling this method. If you want to access entity manager in Burst context, use state.EntityManager.

  2. Use state.WorldUpdateAllocator instead of Allocator.TempJob so you don't have to dispose them manually.

  3. InAirCalculationJob is scheduled against entitiesThatNeedMovementQuery. But why do you also do entitiesThatNeedMovementQuery.ToEntityArray(...)? FYI, this method creates a sync point.

  4. Instead of making your own ECB like this new EntityCommandBuffer(Allocator.TempJob), you should create it via an ECB system.
    https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/systems-entity-command-buffer-automatic-playback.html

  5. With point 3 and 4 fixed, instead of job.Complete();, you can do this:

state.Dependency = new InAirCalculationJob
{
    ...
}.ScheduleParallel(entitiesThatNeedMovementQuery, state.Dependency);
idle salmon
#

Also it’s better to access isGrounded component as enablableRW and remove ecb completely (and array too)

#

Plus for performance you can allocate list for hits only once per chunk (via implementing interface), not per entity
There are tertile’s (I think) extension to do the same per thread which is more better if you want

eternal marsh
eternal marsh
eternal marsh
eternal marsh
# lapis finch Take another look at the code, there are some questionable things about it: 1. ...

Here is my updated code:

`
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(MovementSystem))]
public partial struct InAirCalculationSystem : ISystem
{
private EntityQuery entitiesThatNeedMovementQuery;

    public void OnCreate(ref SystemState state)
    {
        entitiesThatNeedMovementQuery = state.GetEntityQuery(
            ComponentType.ReadOnly<EntityInputControls>(),
            ComponentType.ReadOnly<PhysicsCollider>(),
            ComponentType.ReadWrite<LocalTransform>());
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var singleton = SystemAPI.GetSingleton<BeginInitializationEntityCommandBufferSystem.Singleton>();
        EntityCommandBuffer ecb = singleton.CreateCommandBuffer(state.WorldUnmanaged);

        state.Dependency = new InAirCalculationJob
        {
            Ecb = ecb.AsParallelWriter(),
            PhysicsWorld = SystemAPI.GetSingleton<PhysicsWorldSingleton>().PhysicsWorld,
            Colliders = SystemAPI.GetComponentLookup<PhysicsCollider>(true),
            Transforms = SystemAPI.GetComponentLookup<LocalTransform>(true),
        }.ScheduleParallel(entitiesThatNeedMovementQuery, state.Dependency);
    }
}

`

#

`
[BurstCompile]
partial struct InAirCalculationJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter Ecb;
[ReadOnly] public PhysicsWorld PhysicsWorld;
[ReadOnly] public ComponentLookup<PhysicsCollider> Colliders;
[ReadOnly] public ComponentLookup<LocalTransform> Transforms;

    public void Execute(
        in Entity entity,
        in PhysicsCollider collider,
        in LocalTransform transform,
        [EntityIndexInQuery] int entityInQueryIndex
    )
    {
        int rigidBodyIndex = PhysicsWorld.GetRigidBodyIndex(entity);
        var castHits = new NativeList<ColliderCastHit>(8, Allocator.Temp);
        var castHitsCollector = new AllHitsCollector<ColliderCastHit>(rigidBodyIndex, 1.0f, ref castHits);

        var perceivedUp = new float3(0, 1, 0);
        var contactTolerance = 1.15f;

        var maxDisplacement = -contactTolerance * perceivedUp;
        ColliderCastInput input = new ColliderCastInput(collider.Value, transform.Position, transform.Position + maxDisplacement);

        PhysicsWorld.CastCollider(input, ref castHitsCollector);

        var isGrounded = castHitsCollector.NumHits == 0;
        Ecb.SetComponentEnabled<IsGrounded>(entityInQueryIndex, entity, isGrounded);
    }
}

`

#

Unfortunately, I may be missing a crucial piece somewhere - as I'm not getting any improvement in fps:

lapis finch
#

As displayed in the profiler, your InAirCalculationJob is not bursted. Bursted code will be displayed in bright green color.

olive tendon
#

Your jobs still aren't running bursted for some reason

#

Can you check burst debugger and find your job

#

Does it have errors

lapis finch
#

I'm curious, what could it be?

eternal marsh
eternal marsh
lapis finch
#

that's where you find out if your code is bursted, if you find nothing, your code has failed Burst

eternal marsh
lapis finch
#

did you enter play mode after burst finished compiling?

eternal marsh
#

how do I know when burst finishes compiling?

#

well, either way, I've entered play mode several times, so i presume it's finished compiling. Those profiler screenshots are from when the game was in play mode.

lapis finch
#

See if there is a progress bar that the bottom right of Unity editor?

#

If burst compilation is finished but your job is still blue in the profiler then it's really weird.

#

Have you tried deleting your Library folder to force Unity generates things again?

olive tendon
#

Make sure you don't have a debugger attached with a break point in the job

eternal marsh
eternal marsh
lapis finch
#

You should turn that little bug off

#

Means switch to release mode

eternal marsh
lapis finch
#

I've just done a test with that bug icon in yellow (debug mode) and most of my jobs are not bursted!

#

in debug mode

#

in release mode

eternal marsh
#

this is it! thanks so much! It runs really well now, even with 200 entities, something I wouldn't dare try. I can see that the jobs are indeed coloured green in the profiler too. thanks for all the help getting here guys, the code recommendations and this was really good learnings!

lapis finch
#

You might want to always open Unity in release mode, you can set it in Preferences window > General > Code Optimization On Startup

#

Aside from this I suggest you read a bit about markdown text formatting. The next time you post a multi-line code snippet you should use code block formatting

idle salmon