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

Question Best Practice for Multiple Raycasts in ISystem

Discussion in 'Entity Component System' started by andreyakladov, Apr 16, 2023.

  1. andreyakladov

    andreyakladov

    Joined:
    Nov 11, 2016
    Posts:
    29
    Projectile system based on raycasts from old to new position, see the code below. Is there a better approach for this? I see no problem so far, just want to make sure as I expect LOTS of projectiles flying around. For instance, does it make sense to move obtaining CollisionWorld to OnCreate()?

    Code (CSharp):
    1. [BurstCompile]
    2. public void OnUpdate(ref SystemState state)
    3. {
    4.     var ecb = new EntityCommandBuffer(Allocator.Temp);
    5.     var manager = state.EntityManager;
    6.     var builder = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
    7.     var singletonQuery = manager.CreateEntityQuery(builder);
    8.     var collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
    9.     foreach (var (previousPositionData, localTransform, projectileAspect, collisionFilterData, projectileEntity) in SystemAPI.Query<PreviousPositionData, LocalTransform, ProjectileAspect, CollisionFilterData>().WithEntityAccess())
    10.     {
    11.         var input = new RaycastInput
    12.         {
    13.             // Setup raycast
    14.         };
    15.         if (!collisionWorld.CastRay(input, out var hit)) continue;
    16.         if (manager.HasComponent<HitEffectsData>(projectileEntity))
    17.         {
    18.             // spawn impact effect
    19.         }
    20.         if (manager.HasComponent<DamageTaker>(hit.Entity)
    21.         {
    22.             var damageTaker = manager.GetComponentData<DamageTaker>(hit.Entity);
    23.             // add damage data
    24.         }
    25.         ecb.DestroyEntity(projectileEntity);
    26.     }
    27.     ecb.Playback(manager);
    28.     ecb.Dispose();
    29.     singletonQuery.Dispose();
    30. }      
    31.  
    Can move it into Job, but need good example. Should it be IJobEntity? Also this is a very ugly way looking for components from within a Job via ComponentLookups is killing me every time I make var propertyLookup: ComponentLookup<Type> like 10 times per job.
     
    Last edited: Apr 16, 2023
    Opeth001 likes this.
  2. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,068
    In my ability system, I use a similar approach for any kind of projectile. I use IJobEntity and raycasts to calculate the trajectory between the current and next positions. Although this technique can potentially yield high performance, it also has its own drawbacks, such as the potential for collisions between projectiles.
    Btw, you shouldn't access many components from random entities as it will introduce a lot of random access memory 'cache misses', you should instead append the damage event to the Actor's Buffer and process them later .
     
  3. andreyakladov

    andreyakladov

    Joined:
    Nov 11, 2016
    Posts:
    29
    What is the Actor's Buffer? Currently I aggregate all damage made on frame on target entity's component and then process it.
    Projectiles should not collide as theirs' BelongsTo and CollidesWith do not intercept in my case.
     
  4. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,068
    An Actor can be anything 'Entity' in your game that has Attributes ( health, mana, shield... ) , in my case it can be a player, monster, wall or a tree...
    The Buffer is a DynamicBuffer holding GameplayEffects, which are applied to the Attributes later.
     
  5. andreyakladov

    andreyakladov

    Joined:
    Nov 11, 2016
    Posts:
    29
    That's clear, thanks.
    I approached effects a bit different: every effect in my case is (IComponentData, IEnableableComponent), whenever it is enabled a corresponding system applies it on Entity it is attached to, and Effects System controls the enablement (updated and checks time of effect) and disables it when needed.
    Good thing about it is when effect disabled query does not even triggered for it.
     
  6. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,068
    it depends from game to game.
    In a case where several abilities (projectiles, melee attacks...) can apply several effects on the same frame, this can lead to an issue where a hit can override a previous one from the same frame which is still not applied.
    using a DynamicBuffer to contains as many effects as needed can be a good solution.
    btw, you can also use the change filter to detect newly added effects to the dynamic buffer and systems will not run unless a new effect is added and there is no need for SyncPoints.
    in most cases hitting enemies or environment entities is something quite rare (not all frames) that can be skipped using per-chunk change filters instead of per-entity based via IEnableComponent . (it's very related to your game concept)