#Best approach for missile targeting and navigation system(s)

1 messages · Page 1 of 1 (latest)

plush lichen
#

I'm working on my first ECS/DOTS project and I'm figuring out how to structure my entities and systems! I was hoping someone would be able to share some ideas for how to approach this best.

  • A ship will fire missiles
  • Each missile may be assigned a target up front on instantiation, or pick one at runtime (if the target disappears for example)

Right now I have the data structured like:

    // i'm thinking this may make sense to break into smaller components in time (MissileNavigationData, MissileDamageData, etc?) 
    public struct MissileData : IComponentData
    {
       // info about speed and maneuverability    
    }
    
    public struct TargetData: IComponentData 
    {
        // what I care more about is the entity's LocalTransform data, but I'm not sure if there's a way to keep this up to date, so I'm thinking I need to fetch it via the entity?
        public Entity entity;
    } 
#

I have two systems I'm trying to write: the MissileTargetingSystem and the MissileNavigationSystem

#
namespace Systems
{
    [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    public partial struct MissileTargettingSystem : ISystem
    {
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var missilesQuery = SystemAPI.QueryBuilder().WithAll<MissileData>().WithNone<TargetData>().Build();
            var job = new MissileTargettingJob()
            {
                MissileHandle = SystemAPI.GetComponentTypeHandle<MissileData>(),
            };
            state.Dependency = job.Schedule(missilesQuery, state.Dependency);
        }
    }

    struct MissileTargettingJob : IJobChunk
    {
        public ComponentTypeHandle<MissileData> MissileHandle;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            Assert.IsFalse(useEnabledMask);

            var missiles = chunk.GetNativeArray(ref MissileHandle);

            for (int i = 0, chunkEntityCount = chunk.Count; i < chunkEntityCount; i++)
            {
                var missile = missiles[i];

                // Q1: What's the correct way to determine the closest Target to the missile?
                var possibleTargets = SystemAPI.SomeQueryFor<LocalTransform>().ToArray();
                // find the closest target?
                var closestTarget = possibleTargets[0];
                // should I be using some kind of world query approach here?

                // Q2: How do I assign a new `Target` with `entity` set to the `MissileData` entity?

                var entity = GetEntity(missile);
                AddComponent(entity, new TargetData() { entity = closestTarget });

            }
        }
    }
}
#
    [UpdateInGroup(typeof(SimulationSystemGroup))]
    public partial struct MissileNavigationSystem : ISystem
    {
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var missilesQuery = SystemAPI.QueryBuilder().WithAll<Missile>().WithNone<TargetData>().Build();
            var job = new MissileTargettingJob()
            {
                MissileHandle = SystemAPI.GetComponentTypeHandle<Missile>(),
                PhysicsVelocityHandle = SystemAPI.GetComponentTypeHandle<PhysicsVelocity>(),
                TargetDataHandle = SystemAPI.GetComponentTypeHandle<TargetData>(),
            };
            state.Dependency = job.Schedule(missilesQuery, state.Dependency);
        }
    }

   struct MissileNavigationJob : IJobChunk
    {
        public ComponentTypeHandle<Missile> MissileHandle;
        public ComponentTypeHandle<PhysicsVelocity> PhysicsVelocityHandle;
        public ComponentTypeHandle<TargetData> TargetDataHandle;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            Assert.IsFalse(useEnabledMask);

            var missiles = chunk.GetNativeArray(ref MissileHandle);
            var velocities = chunk.GetNativeArray(ref PhysicsVelocityHandle); 
            var targets = chunk.GetNativeArray(ref TargetData);

            // Q3: What's the best way to grab the LocalTransform data of the Target.entity? 
            var targetTransform = GetEntity(targets[i]).GetComponentData<LocalTransform>();

            // do some math to figure out missile trajectory physics and assign it to `velocities[i]` in the end
            velocities[i] = new PhysicsVelocity() { Linear = someLinear, Angular = someAngular };
        }
    }
#

Q1: What's the correct approach for querying the "world" to determine the closest potential Target to the missile? Let's say these have their own EnemyTargetTag on them. Ideally i could query all potential targets up front and not do this once for each entity

Q2: How do I assign a new Target with entity set to the MissileData entity?

Q3: I want to pull the latest LocalTransform data from the TargetData.entity when I need it. How is this done in a IJobChunk which is passed all the component data in chunks up front?

#

Perhaps IJobChunk is the wrong type of job to reach for here? I was looking for a job that could perform well in parallel for perhaps dozens or hundreds of missiles.

boreal tide
#

Q1: I think you can pass in a native array of positions and one of entities for each potential target
Then just calculate the square distance between each rocket and each object and select the index of the minimum one

Q2: If MissileData is passed in as ref into execute of an Entity Job then you can just set the entity field directly, or you can pass in an ComponentLookup<T>
Currently you seem to overwrite the entity every frame, you're gonna only wanna do that if the entity has been destroyed.