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()?
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
var manager = state.EntityManager;
var builder = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
var singletonQuery = manager.CreateEntityQuery(builder);
var collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
foreach (var (previousPositionData, localTransform, projectileAspect, collisionFilterData, projectileEntity) in SystemAPI.Query<PreviousPositionData, LocalTransform, ProjectileAspect, CollisionFilterData>().WithEntityAccess())
{
var input = new RaycastInput
{
// Setup raycast
};
if (!collisionWorld.CastRay(input, out var hit)) continue;
if (manager.HasComponent<HitEffectsData>(projectileEntity))
{
// spawn impact effect
}
if (manager.HasComponent<DamageTaker>(hit.Entity)
{
var damageTaker = manager.GetComponentData<DamageTaker>(hit.Entity);
// add damage data
}
ecb.DestroyEntity(projectileEntity);
}
ecb.Playback(manager);
ecb.Dispose();
singletonQuery.Dispose();
}
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 like 10 times per job.
1 Like
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 .
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.
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.
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.
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)