1
\$\begingroup\$

I'm developing 2.5D space shooter (3D objects, 2D gameplay). I'm searching for a way to pick targets for homing missiles.

There are two types of missiles: active-radar-guided and heatseekers. The first ones are relatively simple to implement: I search for a target once and then store it in the missile's navigation code. If object is destroyed or escapes missile's triangle of view, I seek for another target. Searching for target is costly, because I have to iterate through all game objects and eliminate ones behind the rocket or out of its "radar range". However, that happens rarely - only after launch and when missile loses the target from sight, so I guess it will be acceptable.

Now imagine heatseeker missile. Situation is different here - I have to make decision about flight direction on every frame - gather information about nearby heat sources, create some kind of heatmap, find hottest direction and navigate rocket towards it. This may degrade performance - especially if I have multiple missiles on scene and this is very likely scenario.

I thought of using collider as first-degree sieve to keep track of potential targets. This collider would have to be quite big but it would narrow down count of potential targets to 10-20 and I guess that should be acceptable for processing. However, I'm falling into problem of multiple colliders: I use one for regular collisions and I wouldn't want the other one to interfere. Also, I'm not sure what is the performance penalty for having a couple of big colliders on scene.

How can I solve the problem of picking target for missile every frame such that it won't have big impact on overall performance? Are the colliders correct path to follow? Or should I rely more on coding than on built-in features?

\$\endgroup\$
4
  • \$\begingroup\$ Have you looked into physics overlap queries as alternatives to one big collider? \$\endgroup\$
    – DMGregory
    Apr 24 '18 at 10:57
  • 1
    \$\begingroup\$ because I have to iterate through all game objects So why not add them to a specific array or map them under an empty game object? Then at least you'd only be iterating over relevant objects. Even if you had 1000 enemies at once on screen such iterations are trivial with a known list. A simple squared distance check should be enough to get the closest target. \$\endgroup\$
    – Sidar
    Apr 24 '18 at 14:06
  • \$\begingroup\$ every frame In addition to what said @Sidar, you can recalculate targhets after x frames. So if you spam N missiles in different frames you only recalculate N/x targhets per frames \$\endgroup\$ Apr 24 '18 at 14:25
  • 1
    \$\begingroup\$ You only need to recalculate if the object became invalid. No need to do that ever frame. Once you spawn a homing missile get the first best target and store the reference. Let the missile check if the target is valid if not get the best next target. \$\endgroup\$
    – Sidar
    Apr 24 '18 at 15:44
1
\$\begingroup\$

I would approach this by making use of the Layer Based Collisions to reduce the overhead of the target selection.

Add a Physics layer for Heat Source, and one for Radar Reflection.

In your standard seeker missiles, its collider or rays should only be registered with the Radar Reflection physics layer.

The same approach should be used for Heat-seeking missiles, but with the Heat Source layer.


I'd have to run some tests, but I think using a box collider trigger to reduce the number of possible targets in the layer and then checking the angle between their position and the position of the missile will probably be the most efficient way to simulate a detection cone, without actually attaching a hidden cone mesh to the front of each missile with a meshcollider.

For a 2.5d game you also have the option to set up multiple rays from the nose of the missile at different angles, and have the missile fly along until one of the rays returns a collision then have the missile turn or accelerate accordingly.

In your missile logic, OnTriggerStay(), or the Physics.Raycast() logic will automatically screen out any colliders not in that layer, reducing the number of object you have to check for your selection logic. If you only have one object, great! Lock on! If you have more than one, compare the two by whatever criteria you decide is important and choose one.


On a subjective note, the beauty of this approach is it allows for decoys and bad targets, but gives you easy control over what those things are.

For heat missiles, having them target the sun, lock onto the trail of other missiles, or IR decoys is relatively easy, as you just have to drop a collider in the Heat Source layer onto the object.

For radar, you get the same flexibility, allowing for behavior like missiles targeting an asteroid or pieces of debris because it got confused and got locked onto it instead.

It also lets you make certain things immune to one targeting method or the other. A stealth ship might not ever be a radar target, but be vulnerable to heat seekers. A ship that's not under thrust might only be vulnerable to radar missiles. A ship firing its laser might generate so much heat that all heat missiles nearby break from their target and re-target it instead.

These types of behaviors can make for more interesting game play.

\$\endgroup\$
0
\$\begingroup\$

Choosing between a collider and code would depend on the complexity of the collision shape. If primitive colliders are sufficient to specify the missile's tracking area, adding a collider will cause no more impact than adding any other collider to the scene.

However, if a cone-shaped missile search area is wanted, another approach is to search throughout the scene and populate a list of game objects at game start. From this, at intervals during the game, eliminate game objects that are either too far away or at a too far angle forwards (using Vector3.Angle()), and populate the list of targets each missile can see.

For the collider approach: create a list of all targets within the collider at each physics tick using OnTriggerStay() (if performance really does cause a problem, create a counter that only updates the list every 15-30 physics ticks).

There is no need for complex heatmap imagery/simulation. If all heat sources are the same intensity, just get the game object with the nearest Vector3.Distance(). Comparing all the targets within the missile's tracking list will yield the nearest one.

If heat sources have different intensities, one solution would be to get the heat source distance as a percent of the maximum detection distance, and multiply the heat source's intensity by the inverse of that distance percentage (pseudocode):

targetDistance = Vector3.Distance(transform.position, target.transform.position);
maxDistancePercent = Mathf.Clamp01(targetDistance / maxDetectionDistance))
// Compare list items using this result
brightness = (1f - maxDistancePercent) * heatSourceIntensity

This will give a heat source's brightness at a distance (assuming a linear rolloff from the maximum detection distance). Other more accurate algorithms might be based on the inverse square law of light, where brightness = lumens / 4 * Mathf.PI * distance * distance. In other words:

brightness = heatSourceIntensity / 4 * Mathf.PI * targetDistance * targetDistance

However, a game implementation may not need to be so complex and may simply go through every light source in the list (generated by the isTrigger collider) and find the brightest light source.

Just start from the simplest implementation and keep what takes the least effort for you to make playable. Performance should only be optimized if it becomes a bottleneck.

\$\endgroup\$
1
  • \$\begingroup\$ You might want parentheses around your denominator there, to ensure it gets evaluated the way you expect. ie. brightness = heatSourceIntensity / (4 * Mathf.PI * targetDistance * targetDistance) \$\endgroup\$
    – DMGregory
    Jan 1 '19 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.