Evenly distribute/split homing projectiles between multiple enemies

Hello,

I am working on a character or some type or turret if you want, that shoots six homing projectile instances at enemies in a given range. The homing logic is made with the “Set Homing Target Component” node. Now I want all six of them to evenly split between enemies. So if there is only one enemy, he gets hit by all projectiles. If there are two, each gets three projectiles and so forth. For the uneven numbers, the spare projectiles need to be distributed to the closest enemy or enemies. That is probably done with some kind of “Get Distance To Actor” logic or something, but that is not the important thing right now- The part I stuggle with is the distribution. Everything esle is working so far.

So if anyone has an idea how to implement this, I’d be happy to read your answer!

Edit: Some additional information in how I try to make it work:

|-|If there are no enemies in the detection radius, the ability cannot be activated|
|-|If there is one enemy in the detection radius, it gets hit by all six projectiles|
|-|If there are two enemies in the detection radius, they each get hit by three projectiles|
|-|If there are three enemies in the detection radius, they each get hit by two projectiles|
|-|If there are four enemies in the detection radius, they each get hit by one projectile and the two enemies closest to the player character get hit by the fifth and sixth projectile respectively, all enemies get hit at least once, two enemies get hit twice|
|-|If there are five enemies in the detection radius, they each get hit by one projectile and the enemy closest to the player character gets hit by the sixth projectile|
|-|If there are six enemies in the detection radius, every enemy gets hit by one projectile|
|-|If there are more than six enemies in the detection radius, the six enemies closest to the player character each get hit by one projectile|

Here is how I set it up, which could be an entirely wrong approach, typical beginner mistakes:

I have the character BP for the turret. There is a sphere collision that constantly checks, if an enemy is within the sphere. If so, I can press a button (for testing purposes) to put all detected enemies in an array (not yet based on distance, I am just using six dummies for now) and then spawn a projectile BP six times (so six instances) and call an event in the projectile BP, so they would set their homing target when they spawn.

First you need to get all enemy actors, and sort them by distance.

Old topic about it (i recommend C++ code):

when you have that sorted by distance array. Shoot those projectiles in order.
When there are left, go for second loop.

1 Like

Well that would take care of the distance problem. So thanks for that! Now what’s left is the even distribution part I guess.

Sorted by distance array solves distribution, for any number of projectiles you want shoot and any number of targets. So you can have multiple turrets shooting multiple targets. However that would need some more thinking about how to make distance function.

Let me explain (assuming single turret, that shoots 6 projectiles).

  • first it sorts enemy targets by distance.
  • so if there is more targets than projectiles, it will shoot 6 closest targets.
  • if there are more projectiles than targets, it will shoot all targets at first,
  • then remaining projectiles will be shot at closest targets.
  • and loop trough targets until all projectiles are fired.

So if there are 6 projectiles and single target, it will loop 6 times each time shooting at that single target.

For two targets it will loop 3 times, shooting at closest then other one, then repeating

For 4 targets and 6 projectiles, it will shoot all 4 of them, then loop it and with 2 remaining projectiles it will shoot second time at two closest targets.

So just make sorting by distance array. I suggest map variable that as index has distance (rounded to closest int), and as variable, it has either target location or target actor reference.

Uh, forget MAP variable, you cannot have 2 entries with same key.

So only way is to make struct then sort array of it by float as key.

PS. just found some great source for sorting in blueprints:

pps. However i think C++ would be much better for this.

and that sorting topic has link to FREE functions made in blueprints, that has SORTING:

So get that, use it to sort targets.

2 Likes

depending on how often you are doing this C++ might not be that much of an improvement, and it can be speed up considerably still by using “Get Squared Distance” (just keep in mind the distance limit would need to be squared to compensate) this way so save having to do the most expensive part of distance checks being the square roots.

once the array is sorted (or even during the sort) anything outside the range would be removed. the reason to suggest C++ for array sorting is to define a sorting predicate, but by nature of sorting predicates they can not trim/remove. and trimming would make sorting faster, but we come back to how often this is going to be happening.

if you don’t want to go into C++ just don’t have this be usable too often. get all Actors of Type then walk through the array doing a distance check if greater then distanceMax remove them.

then you would sort the Array (this is where C++ you could define a sorting predicate, but) this is doable in Blueprints as long as you don’t do this “to often” you can look here for implementing a sort How to sort an array? in blueprints.

for the distribution system given your criteria, have a temp integer equal to you ProjectilesMax, then there are a couple of options depending on code flow if if(Array.Length >= ProjectilesMax) then just use a loop
if(Array.Length < ProjectilesMax) then you need a bit more

the getting the array could be cleaned up by using a manager to keep track of your Entities, and you could cash the distances on the first traversal to save on math for each compare, but these are optimizations.

other optimizations after it is functional:

  • object pooling so you don’t have to create and destroy these objects all the time which can lead to segmentation
  • replace the top for loop with maybe a Do N where the target index would also be the CurrentN (only do this if the Length is less then the MaxProjectiles) this might require unit testing to determine if it is faster.
  • do these need to be physical objects or could they be just a visual effect after a trace
1 Like

iterating through the array takes care of that naturally:

1 for you, 1 for you, 1 for you…

still three missiles to go, but no more bad guys? Back to the start → 1 for you, 1 for you.

(didnt realize there was more answers above before posting this)

Okay, I tried to implement what you wrote and made in the screenshot, buuuut didn’t work at first. So maybe some more information how I initially set it up. I must mention I am trying to learn programming at the moment, so I am not yet familiar with many things, so an explanation for dummies here and there is much appreciated! Knowing what is supposed to happen in your example is (for me at least) a big help to learn and understand these things.

I have the character BP for the turret. There is a sphere collision that constantly checks, if an enemy is within the sphere. If so, I can press a button (for testing purposes) to put all detected enemies in an array (not yet based on distance, I am just using six dummies for now) and then spawn a projectile BP six times (so six instances) and call an event in the projectile BP, so they would set their homing target when they spawn.

Maybe that is the wrong approach, I don’t know. It has always and only worked for one enemy so far.

Now I am not sure where I would put what you made in that screenshot and if I need to change something in my setup.

But I am still glad for your help so far! That goes for everyone here of course.

That’s what I thought, too. But every instance seems to be focused on a specific target. Who knows, maybe I messed something up, I AM a beginner at the end of the day.

you’ll probably have an easier time if you first get just each small part working in isolation, away from your main project. Learning to work with arrays is important but it is confusing at first, so it’s a lot easier to do some simplified testing first.

You might setup an array that just prints strings and runs from button press in a separate project in order to figure out the iteration part. This way you don’t have any additional hijinks with the referencing different actors and so on.

“a sphere collision that constantly checks”
this could be somewhere between Ok too, PLEASE don’t do this collisions can be rather intensive. if you want to check spherical collision with regularity then you could have the Sphere Collider set to Generate Overlap Events (it is under Collision in the details window for the collider in question) then place its OnBeginOverlap() (you can right click the collider in the Hierarchy->Add Event->OnBeginOverlap), and its OnEndOverlap() (same process just pick OnEndOverlap). in the OnBeginOverlap() check for the relevant things that define a target, and add them to an array. in the OnEndOverlap() remove the target from the array (it is safe to remove something from an array that was never in the array)

the other method would be to every so often do a Sphere Trace Multi every so often this way you can specify your criteria at the point of the Trace, and you will get an array anyways. you can even have the start point and end point be the same for just a “within this radius”

once you have the array and it “canFire” then sort the array for your criteria (keep in mind that a sphere radius followed by a distance sort will return things even if it is behind, above, or below the checks source this would require an extra angle check to determine if it “is in front of”)

the image I posted would be at the point of firing, and I even included a function call to “FireProjectile()” (this will probably need to be an event as things like spawning actors need to be in Events as the engine will prefer to spawn Actors Asynchronously to prevent stutters but when you have object pooling it could be a function as you would just be placing the projectile at its starting point, orienting it, and then activating it)

  • first we check to see if the Length() of the Array is Greater then or Equal to the Max projectiles. True go to point 2, False GoTo point 5
  • we start a for loop Iterating (walking/traversing) through the array
  • on each of those Entities we call FireProjectile() with the Entity as the target
  • then we check to see if the index of Entities is greater then MaxProjectiles (looking at this again it is actually wrong, and should be MaxProjectiles - 2 as projectile 1 will be shot at index 0, Projectile 2 will be shot at index 1, … projectile 6 will be shot at index 5, but I am breaking at the end meaning I need a minus 2 if I put the check at the start it could be minus 1) if the test is “true” then we break out of the loop early
  • I set RemainingProjectiles equal to MaxProjectiles
  • start a while loop that will continue as long as RemainingProjectiles is greater then 0
  • inside the While Loop I start a For loop iterating over the array
  • calling FireProjectile() on the element
  • decrement RemainingProjectiles (this is the same as GetRemainingProjectiles()RemainProjectiles-1SetRemainingProjectiles() compressed into 1 node)
  • check to see if the amount of RemainingProjectiles is still Greater then 0 (which upon inspection should probably be a “less then or equal to” node instead as the current check is the opposite of what is needed)
2 Likes

Absolutely. I’ve worked with the Print String quite a lot, but am not always realising what might be wrong. But in regards to what you said at first with iterating through the array. Maybe you can spot a mistake in my setup:

So when enemies are detected, the turret can shoot on a button press for now, like the 1 key. That also adds all detected enemies to an array. Then the projectiles get spawned, six instances to be exact. When they spawn they iterate through the array of detected enemies with a “For Each Loop” node and whatever comes out of there is the selected target.

I am pretty sure something is wrong about that approach, but I can’t put my finger on it :frowning: Maybe it’s because of the instancing of the projectiles.

share your blueprint code, will be easier to see.

also you’’ need to for each from the bullets probably, because you are doing something per bullet.

so for each bullet, you’ll be going ot the next enemy in the enemy array, and when you reach the end of hte enemy array, then you start back from beginning of it (if there is still bullets left)

this will be pretty tricky if you are getting used to blueprint arrays, but easier to see what you are doing with some images.

1 Like

Hey @MatzeOoo

Made a simple project to try and (hopefully) illustrate what has been shared above.

Turret

TurretTest_UE5_2.zip (75.9 KB)

2 Likes

Holy smokes, thanks for the effort! I tried to set it up as you did and in theory it works! Everyone gets registered and those too far away got sorted out. Buuuuuut for whatever reason they still like to chase one and the same target.

I created your logic, functions and all inside the projectile blueprint.

Not tidy, I know. But maybe it doesn’t work because each projectile instance runs through that individually? So what I think, and correct me if I am wrong please, is that some of the parameters need to be set in the character/turret blueprint or set globally?

What I also tried was to create the logic in the character/turret blueprint and assign the homing target via a custom event after a projectile is spawned, but that didn’t work at all.

But at least it looks like this now:
ezgif.com-video-to-gif

Yeah, I used a Third Person Character as turret :smiley: Please ignore that.

This.

Assign the target as they are spawned. Notice the end result was an array of actors and an array of integers; the int array has distributed the amount to spawn and the reference array the target that correspon to each amount.

This should be in the class that spawns. The only job for the projectile is to navitage to the target.


Idealy you can save yourself the int array and spawn them in that loop. It was done mainly for debug… but it works either way. There are also better sorting algorithms you can go for, @Nawrot shared a priceless link for that. From that link check out shell sort.

1 Like

Hey, I tried it again to do that in the character/turret blueprint and now it actually WORKS! Except for the part where the projectiles now spawn from only one socket but that is something I can hopefully do on my own.

I will also look into what the others said regarding using a sphere trace and such.

Big thanks to everyone who helped me! @pezzott1 @BIGTIMEMASTER @gardian206

1 Like

Hello,

sorry to bother but it looks like I need help again. I fiddled around to try and fix this but I got the issue that even though I want to spawn only six projectiles, I spawn projectiles per socket based on number of detected enemies. I certainly know why thanks to the For Each Loop node, but I can’t find a way around it.

To summarise: I want to spawn one projectile per socket (there are six sockets, makes six projectiles max.) but instead I spawn as many projectiles per socket as amount of enemies detected

IMO this is how it could work:
TurretTest.zip (90.2 KB)

For example:

1 Like

Okay, so I tried to use my homing logic in your example here in case I did something wrong in my project, buuut in both cases it only worked in theory, by that I mean with the traces. In case of two enemies, still only two projectiles spawn.

I still think there is something wrong with how I set it up. I mean if it only finds two enemies it will based on what I built only spawn as many projectiles, up to 6, but I can’t think of another way to determine the targets. :frowning:

But see for yourself:

TurretTest.zip (1.7 MB)

I suggest you try and recreate it to understand how it works. Feel free to ask if you ever get stuck.

Turret

TurretTest_UE5_2.zip (109.6 KB)

1 Like