Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Stuck on use case

Discussion in 'Entity Component System' started by Shinao, Oct 31, 2019.

  1. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    Hi there,

    I've got a scenario where I can't seem to take advantage of ECS and Jobs.
    There is about 10.000 rigidbody on the floor.
    I've got 100 affectors that fly around and nearby rigidbodies get slowly attracted to them. (let's say 5 rigidbodies attracted on average per affector) while flying around.

    I can't seem to figure out how to leverage ECS to my benefit, my options that I saw thus far are :

    1. Adding a component to every rigidbody affected by a affector seems troublesome : first I would add and remove hundreds of them per frame and one rigidbody can be affected by multiple affector and I can't add multiple component to an entity.

    2. Create an entity for every rigidbody affected and add a component that reference both my affector and rigidbody. I can get the components with ComponentDataFromEntity to the jobs however I have to modify the PhysicsVelocity component in it and that's not allowed since it may be modified by two thread.

    3. Use the idea of 2. but launch a job for each affector that I have but that would mean a hundred of them with an average of 5 entities per job, seems really wasteful.

    I feel like there is a ECS way that I have overlooked or some kind of pattern perhaps...

    Any ideas ?
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Add a trigger collider to each affector and use a trigger job to apply forces to the rigidbodies.
     
    steveeHavok likes this.
  3. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
  4. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    @steveeHavok That is... extremely similar to what I'm trying to do. That will teach me not to look at every folder in the repository, I only saw the bare bone trigger sample that you talked about. It seems that I will only be limited to one affector with this sample but it is a very good starting point. Thank you !

    Edit: It seems that adding other affectors is quite the task which was actually most of my problem actually. I still don't see how I can modify the rigidbodies velocity with multiple affectors in a safe way without slowing everything down.

    Any ideas ?
     
    Last edited: Oct 31, 2019
  5. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    for each attractor iterate over all objects it affects, calculate a force for the object, and add it to the force already applied to that object
     
  6. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    Well of course it's a simple matter if I'm doing everything by iterations, but then I'm losing a ton of performance if I'm not maximizing Jobs/Threads. I feel like this scenario will apply to lot of different things I want to do therefore I'm trying to have the most optimized one from the beginning.
     
  7. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    ok, you can do it this way
    store all attractor positions to the native array
    pass those positions to a job that iterates on all objects
    based on all attractor positions calculate a vector field value at objects position and apply a force based on it
     
  8. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    I'm not sure what exactly you trying to achieve, is it something like interaction with the grass?

    in any case, you need a nested loop as long as you need each attractor position to calculate each object's force to get precise result.

    but if you have both really many attractors and really many objects they affect
    it may be reasonable to split it into two passes
    - calculate attractor forces sum at reasonable resolution and store vector field as a 3d-array
    - iterate on all objects and sample that baked vector field for the force to get approx force value (you can get close enough to real value by interpolation between nearest available values)
    in this case, you sacrifice some precision but save on iterations

    Another way to optimize it is to split world space into sections (based on maximum attractor radius) and to do calculations only inside one section
     
    Last edited: Nov 1, 2019
  9. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    I'm actually trying to do something pretty close to the example but yes with many affectors and many rigidbodies.
    I can't seem to quite understand what you mean by your 3d-array/vector field/interpolation method.
    I guess it does feel like a proper solution is not quite that easy with ECS when you've got multiple objects that can affects the same object.
     
  10. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    I'll try to explain better

    Vector field is a function that takes a position is space as input and returns a vector (force in your case)
    https://www.khanacademy.org/math/mu...valued-functions/v/vector-fields-introduction

    So for your case you can create a function kind of:
    vector3 ClaculateForce( float3 position, attractors[] attractors )

    inside it iterates on attractors, and calculates a sum of their effect in a given point

    Next, instead of applying it to each object, you can apply it to points in space of your scene, with a certain step
    so your scene space is divided into voxels and you know the force in the center of each voxel

    Next, you iterate on all objects, for each object you can get force values from nearest voxels, and by interpolating those values you now get very close to direct calculation result.

    So now instead of invoking high-cost
    ClaculateForce
    function objects-count times, you call it voxels-count times + (read near voxels + interpolate)*objects-count times.
    In case of a huge amount of objects/attractors, this trick may save you a lot of CPU time.
     
  11. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Vector fields are useful for particles on the GPU, but in this case I think @Shinao wants to utilize an (m + n) log n algorithm like the one Unity.Physics provides and get near-perfect accuracy for a lot cheaper than m * n.

    Unless I am misunderstanding the code, ITriggerEventsJob is a single-threaded job, which means you should be able to iterate through all events and directly apply impulses to the entities' rigidbodies using ComponentDataFromEntity<PhysicsVelocity>.

    Of course if you want to apply your impulses in parallel as well, then I suspect Unity.Physics may not meet your needs as it stands today.
     
  12. Shinao

    Shinao

    Joined:
    Mar 7, 2014
    Posts:
    36
    @SubPixelPerfect Thanks for the explanation. Wouldn't I have the same problem storing the forces of the affectors into the voxels when one body can be affected by multiple affectors and therefore the writing in the voxels should be single threaded to avoid overwrite ? It might be faster on this particular use case though.

    @DreamingImLatios I thought ITriggerEventsJob was running in parallel, can't seem to find an answer in the manual/api on that. How do you know that ? But if that is the case then yes there is no point in doing something if my triggers are already iterative...
     
  13. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    In CreateJobReflectionData, the JobType is Single.
    However, what ITriggerEventsJob really does is iterate over a NativeContainer storing all the pairs of triggers that were calculated in the broadphase. That broadphase runs in parallel, so it is very possible that using ITriggerEventsJob may still be significantly faster than a brute force algorithm overall. The reason ITriggerEventsJob is a single-threaded algorithm is because the broadphase can't ensure thread safety between any two pairs of entities. The only way you can do that is by using an islanding algorithm or a multibox (a multibox BVH in Unity's case).
     
    Shinao likes this.