Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by guerman2008 · Nov 18, 2020 at 03:32 PM · dotsthreading

ECS And the Job System

I'm having issues understanding how I should be using the Jobs System to interact with the ECS system.

My current goal:

  • Create 10,000 physics objects and add a random velocity

  • Add a random velocity if their velocity drops below a threshold

Creating the physics objects doesn't have to be in a Job, since it's a one-time action. However, iterating over 10k objects and adding a velocity when needed seems like something that could benefit from the Jobs System.

My questions:

  • What is the difference between creating and defining a job within a monobehavior, and creating a script that derives from JobComponentSystem? Which should I use for this use-case?

  • How do I access entity commands such as getting the components I need to compare against? Getting PhysicsVelocity from entities is important for this task as I'm comparing it to a minimum velocity before adding a new velocity. However, EntityManager doesn't seem to be something I can access within a job.

Comment
Add comment · Show 5
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image myzzie · Nov 18, 2020 at 04:21 PM 0
Share

Have you read through the manual? It'll tell you why to use the JobComponentSystem and how to access entity data.

avatar image guerman2008 myzzie · Nov 18, 2020 at 06:15 PM 0
Share

From what I'm understanding, JobComponentSystem is used to resolve dependency issues with different Jobs writing and using the same component types. But specifically, it looks like EntityQuery is what I want to use. Though, I'm still unsure, as it looks like it gets all components of one type, and I already have a NativeArray of the specific entities I want to use.

avatar image myzzie guerman2008 · Nov 18, 2020 at 07:08 PM 0
Share

You still use EntityQuery in the JobComponentSystem An example:

 public class RotationSpeedSystem : JobComponentSystem
 {
    private EntityQuery m_Query;
    protected override void OnCreate()
    {
        m_Query = GetEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());
    }
    //…
 }

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
1

Answer by andrew-lukasik · Nov 18, 2020 at 08:32 PM

What is the difference between creating and defining a job within a monobehavior, and creating a script that derives from JobComponentSystem? Which should I use for this use-case?

You can schedule jobs from monobehaviours successfully whenever you need that, that's ok use case. Instantiate entities too. But processing entities from an gameObject doesn't makes much sense.


ComponentSystem is just a kind of of back-port of an idea of (dots-) System for Monobehaviours. I think about them as invisible Monobehaviour-singletons.

SystemBase (new and universal) and JobComponentSystem (older implementation) are examples of some of the proper entity systems. Meaning: those are types where you want to define how data associated with entities is being changed (what happens to them after after creation).

Sidenote: there is always max 1 system instance of given type per World.

GameObjectConversionSystem is system type that is dedicated to what they name suggest - converting monobehaviours into entities.


How do I access entity commands such as getting the components I need to compare against? Getting PhysicsVelocity from entities is important for this task as I'm comparing it to a minimum velocity before adding a new velocity. However, EntityManager doesn't seem to be something I can access within a job.

Answer to this greatly depends on your entities package version and system you choose to do that with. I, personally, prefer SystemBase workflow (i.e: jobs generated from lambda expressions):

 public class VelocityEntitiesSpawner : UnityEngine.MonoBehaviour
 {
     public int _count = 100;
     void Start ()
     {
         var world = World.DefaultGameObjectInjectionWorld;
         var EntityManager = world.EntityManager;
         var archetype = EntityManager.CreateArchetype( typeof(Velocity) , typeof(LocalToWorld) );
 
         float3 pos = transform.position;
         quaternion rot = transform.rotation;
         for( int i=0 ; i<_count ; i++ )
         {
             var entity = EntityManager.CreateEntity( archetype );
             EntityManager.SetComponentData( entity , new LocalToWorld{
                 Value=float4x4.TRS( pos , rot , new float3{x=1,y=1,z=1} )
             } );
         }
     }
 }
 
 public struct Velocity : IComponentData
 {
     public float3 Value;
 }
 
 [UpdateInGroup( typeof(InitializationSystemGroup) )]
 public class MinVelocitySystem : SystemBase
 {
     Unity.Mathematics.Random _random;
     protected override void OnCreate()
     {
         _random = new Random( (uint) System.DateTime.Now.Ticks.GetHashCode() );
     }
     protected override void OnUpdate ()
     {
         const float minSpeed = 100f;
         var random = _random;// alias
 
         Entities
             .WithName("min_velocity_job")
             .ForEach( ( ref Velocity velocity , in int entityInQueryIndex )=>
             {
                 float speed = math.length( velocity.Value );
                 if( speed<minSpeed )
                 {
                     random.state += (uint) entityInQueryIndex;
                     velocity.Value += random.NextFloat3(
                         min:    new float3{ x=-1 , y=-1 , z=-1 } ,
                         max:    new float3{ x=1 , y=1 , z=1 }
                     );
                 }
             } )
             .WithBurst().ScheduleParallel();
     }
 }
 
 [UpdateInGroup( typeof(SimulationSystemGroup) )]
 public class VelocitySystem : SystemBase
 {
     protected override void OnUpdate ()
     {
         float dt = Time.DeltaTime;
 
         Entities
             .WithName("apply_velocity_job")
             .ForEach( ( ref LocalToWorld ltr , in Velocity velocity )=>
             {
                 ltr.Value = math.mul(
                     ltr.Value ,
                     float4x4.Translate( velocity.Value * dt )
                 );
             } )
             .WithBurst().ScheduleParallel();
     }
 }
 
 #if UNITY_EDITOR
 [UpdateInGroup( typeof(PresentationSystemGroup) )]
 public class VelocityDebugSystem : SystemBase
 {
     protected override void OnUpdate ()
     {
         Entities
             .WithName("debug_velocity_job")
             .ForEach( ( in LocalToWorld ltr , in Velocity velocity )=>
             {
                 UnityEngine.Debug.DrawLine( ltr.Position , ltr.Position + velocity.Value , UnityEngine.Color.yellow , 0f );
             } )
             .WithoutBurst().Run();// no burst for UnityEngine namespace
     }
 }
 #endif

How to compare entities against each other:

 // ClosestOther.cs
 using Unity.Entities;
 using Unity.Mathematics;
 using Unity.Collections;
 using Unity.Transforms;
 
 [GenerateAuthoringComponent]
 public struct ClosestOther : IComponentData
 {
     public Entity Value;
 }
 
 [UpdateInGroup( typeof(SimulationSystemGroup) )]
 public class FindClosestOtherSystem : SystemBase
 {
     EntityQuery _query;
     protected override void OnCreate ()
     {
         _query = EntityManager.CreateEntityQuery(
                 ComponentType.ReadWrite<ClosestOther>()
             ,    ComponentType.ReadOnly<LocalToWorld>()
         );
     }
     protected override void OnUpdate ()
     {
         var ltwData = GetComponentDataFromEntity<LocalToWorld>( isReadOnly:true );
         NativeArray<Entity> entities = _query.ToEntityArray( Allocator.TempJob );
         
         Entities
             .WithName("closest_other_job")
             //.WithChangeFilter<LocalToWorld>()// so it wont schedule repeatedly for static objects
             .WithReadOnly(ltwData)
             .WithReadOnly(entities).WithDisposeOnCompletion(entities)
             .ForEach( ( ref ClosestOther findClosestOther , in LocalToWorld ltw , in Entity entity )=>
             {
                 float3 myPos = ltw.Position;
                 float closestDistSq = float.MaxValue;
                 findClosestOther.Value = Entity.Null;// value when no other candidate found
                 for( int i=0 ; i<entities.Length ; i++ )
                 {
                     Entity otherEntity = entities[i];
                     if( otherEntity!=entity )// not me
                     {
                         float3 otherPos = ltwData[otherEntity].Position;
                         float distSq = math.lengthsq( otherPos - myPos );
                         if( distSq<closestDistSq )
                         {
                             findClosestOther.Value = otherEntity;
                             closestDistSq = distSq;
                         }
                     }
                 }
             } )
             .WithBurst().ScheduleParallel();
     }
 }
 
 #if UNITY_EDITOR
 [UpdateInGroup( typeof(PresentationSystemGroup) )]
 public class ClosestOtherDebugSystem : SystemBase
 {
     protected override void OnUpdate ()
     {
         var ltwData = GetComponentDataFromEntity<LocalToWorld>( isReadOnly:true );
 
         Entities
             .WithName("debug_closestOther_job")
             .ForEach( ( in LocalToWorld ltw , in ClosestOther closestOther )=>
             {
                 float3 myPos = ltw.Position;
                 UnityEngine.Debug.DrawLine( new float3{ x=myPos.x , z=myPos.z } , myPos , UnityEngine.Color.white );
                 if( ltwData.HasComponent(closestOther.Value) )
                 {
                     var otherLtw = ltwData[closestOther.Value];
                     UnityEngine.Debug.DrawLine( myPos , otherLtw.Position , UnityEngine.Color.yellow );
                 }
             } )
             .WithoutBurst().Run();// no burst for UnityEngine namespace
     }
 }
 #endif

How to Add and Append to DynamicBuffer in parallel jobs:

 // IWantDynamicBufferExample.cs
 using Unity.Entities;
 using Unity.Collections;
 
 [GenerateAuthoringComponent] public struct IWantDynamicBufferExample : IComponentData { /* tag only */ }
 [InternalBufferCapacity(8)] public struct DynamicBufferExample : IBufferElementData { public FixedString32 Value; }
 public class DynamicBufferParallelWriteExampleSystem : SystemBase
 {
     EndSimulationEntityCommandBufferSystem _endSimulationEcbSystem;
     protected override void OnCreate ()
     {
         _endSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
     }
     protected override void OnUpdate ()
     {
         if( Time.ElapsedTime>0 ) this.Enabled = false;// run once only
 
         var ecb = _endSimulationEcbSystem.CreateCommandBuffer();
         var ecb_pw = ecb.AsParallelWriter();
 
         Entities
             .WithName("add_buffers_job")
             .WithAll<IWantDynamicBufferExample>()
             .ForEach( ( in int entityInQueryIndex , in Entity entity )=>
             {
                 var buffer = ecb_pw.AddBuffer<DynamicBufferExample>( entityInQueryIndex , entity );
                 buffer.Add( new DynamicBufferExample{ Value = "initial value example" } );
                 ecb_pw.RemoveComponent<IWantDynamicBufferExample>( entityInQueryIndex , entity );// remove tag
             } )
             .WithBurst().ScheduleParallel();
 
         Entities
             .WithName("append_buffer_example_1_job")
             .WithAll<DynamicBufferExample>()
             .ForEach( ( in int entityInQueryIndex , in Entity entity )=>
             {
                 if( entity.Index%2==0 )
                 ecb_pw.AppendToBuffer<DynamicBufferExample>( entityInQueryIndex , entity ,
                     new DynamicBufferExample{ Value = "entity index in even" }
                 );
             } )
             .WithBurst().ScheduleParallel();
         
         Entities
             .WithName("append_buffer_example_2_job")
             .ForEach( ( ref DynamicBuffer<DynamicBufferExample> buffer , in Entity entity )=>
             {
                 if( entity.Index%2!=0 )
                     buffer.Add( new DynamicBufferExample{ Value = "entity index in uneven" } );
             } )
             .WithBurst().ScheduleParallel();
         
         _endSimulationEcbSystem.AddJobHandleForProducer( Dependency );
     }
 }
Comment
Add comment · Show 23 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image guerman2008 · Nov 19, 2020 at 01:05 AM 0
Share

Thanks! This helped out a bit.

I'm still confused though - how do these SystemBase Jobs know which entities to operate on? Is there any way I can define that with more control, as well as when I want it to happen? For example, if I only want to begin changing velocities after a few seconds.

Additionally, is there a way to serialize the values being used for the $$anonymous$$imum velocity and random velocity, and pass them to these jobs?

Thanks again!

avatar image myzzie guerman2008 · Nov 19, 2020 at 01:12 AM 0
Share

how do these SystemBase Jobs know which entities to operate on?

Entities.WithName("debug_velocity_job").ForEach( ( in LocalToWorld ltr , in Velocity velocity )

LocalToWorld and Velocity are components. It only iterates entities these components.

avatar image guerman2008 myzzie · Nov 19, 2020 at 01:42 AM 0
Share

Ah, okay. Is there a way to narrow that down? Would I have to define a custom component, add it to the entities, and use that component in that function?

Show more comments
avatar image andrew-lukasik guerman2008 · Nov 19, 2020 at 01:42 AM 0
Share

For example, if I only want to begin changing velocities after a few seconds.

Yes. I present to you power of (drum roll) If statement:

 protected override void OnUpdate ()
 {
     if( Time.ElapsedTime>3f )
     {
         float dt = Time.DeltaTime;
         Entities
             .WithName("apply_velocity_job")
             .ForEach( ( ref LocalToWorld ltr , in Velocity velocity )=>
             {
                 ltr.Value = math.mul( ltr.Value , float4x4.Translate( velocity.Value*dt ) );
             } )
             .WithBurst().ScheduleParallel();
     }
     else { /* no job is scheduled so nothing happens */ }
 }
avatar image andrew-lukasik guerman2008 · Nov 19, 2020 at 01:47 AM 0
Share

how do these SystemBase Jobs know which entities to operate on?

What @myzzie said. SystemBase creates implicit EntityQuery-ies based on what components did you use in lambda expressions (`ref` prefix means read+write access, in means read only access).
Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

139 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

ECS: How to access to separate "groups" of same-archetype entities? 0 Answers

Unity Jobs Error - "Job can only create Temp memory" 1 Answer

Unity DOTS Burst Mathematics pnoise 2 Answers

How can I get a component data from an entity inside a JobComponentSystem? 1 Answer

How can I pass data from one job to to the next? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges