I need to add a target cycle in my game. I know how I want it to work but I’m struggling to figure the easiest and most logical way to do it.
It’s a 2D top down game, and how it works is you press a button to LOCK ON. This sets a target on the nearest enemy. If then you press the right stick in any direction, it’ll move to the next nearest enemy in that direction, RELATIVE to where the current target is.
I’ve currently got it locking onto the nearest, but kinda stumped now. Any ideas on how to logically add in the target cycle?
Thanks
Yes. Iterate over all targets and calculate its angle to the player. Then just compare the current targets angle with all other angles and take the target having closest angle.
That should be it. However, you’ll have to decide whether distance is more important than the relative angle or if the angle matters more. Or make some sort of “score”, like say score = Mathf.Abs(relativeAngle)/180f+distance/100.0f and pick the lowest. An enemy could be really close to the current locked target, but in the completely opposite direction of the stick while another could be a bit further away but on a much closer relative angle.
Edit : That would be a very crappy scoring formula where the angle should be normalized and 100.0 would be some sort of maximum possible distance so the dist is normalized as well. And the score would have values between 0 and 2, 0 being the best (but it would not be possible since the the next target would have to sit right on top of the current target)
Yes, my bad for not being explicit enough. By stickAngle i meant the Vector2 direction given by your input.
Vector2 stickDir = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); //No need to normalize it
float angle = Vector2.Angle(stickDir, nextPossibleLock-currentLock);
Watch out for negative values! Not sure if Vector2.Angle gives absolute values ranging between 0 and 360, 0 and 180 or -180 and 180… If it happens to be 0 to 360, substract 180 and get the absolute value to make it 0 to 180
//get stick direction
stick_dir = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); //No need to normalize it
GameObject closest = null;
Vector3 position = lock_on_obj.transform.position;
float bestScore = Mathf.Infinity;
// loop through
foreach (GameObject enemy in all_enemies)
if(enemy == lock_on_obj){continue;} //Make sure you don't check the current lock-on itself
Vector3 diff = enemy.transform.position - position;
float curDistance = diff.sqrMagnitude;
float angle = Vector2.Angle(stick_dir, diff);
float score = Mathf.Abs(angle)/180f + curDistance / 100.0f; //Watch out, because you're using squared distance. So the max value (100 in this case) should be squared as well... You're checking for a max distance of 10, not 100 in this case
if(score < bestScore){
closest = enemy;
}
}
Hope it works
Edit : About that scoring value : Technically, given that the real max distance right now is 10 meters, an enemy sitting 5 meters away from the current target at a 0 degrees angle would be just as important as a an enemy sitting right next to the current locked target but at a 90 degrees angle.
If you would like to add more emphasis on the angle than distance for instance, you could do a weighted average :
I can now scroll targets, although it often completely skips over a target if they’re not exactly snapped to the same axis as the current target. I don’t need the range to be so strict and would favor distance, so would it be the distanceWeight value to tamper with? (I did try but without much luck)
As an eg. I’m looking to have the ranges like this (8 directions, so the closest enemy in that area will be chosen)
EDIT: Sorry, should have called the red center ‘CURRENT TARGET’ instead of player
Yes, it would be increasing the distance weight or simply set the 100 from curDistance/100.0f to something else. Again, curDistance is actually the squared distance, so your max distance would actually be 10 units. If most of the times your enemies are way closer to each other, that curDistance/100.0f would give results way below 1.0f, thus making distance always matter less. Say your enemies are 20 units away, that would already be 20*20/100.0f = 4. You might actually want to use a non-squared distance rather than a squared distance for accurately calculating the score, as the result from that division would be squared too. There you should have a 2, not a 4.
Try “float curDistance = diff.magnitude;” instead and tweak that 100.0f to an average distance between your enemies. Again, i do not know how large your scene is and how far apart they are.
Also, make sure the angle is between 0 and 180, that could be causing problems too otherwise.
If tweaked right, it should give a pretty good result.
Edit : Are you using the keyboard arrows, not a controller?
Actually, it is more important how far away they are from each other. But yeah, a value of a few units should be good then.
Also, chunking the search into sectors as show in the picture might lead to some problems. Say you only have 1 enemy left in the game, but he’s in the complete opposite direction of what the player pressed. If you would like to be able to select him too, you’d have to perform a search in all sectors.
Anyway, the final code should be something like :
//get stick direction
stick_dir = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); //No need to normalize it
GameObject closest = null;
Vector3 position = lock_on_obj.transform.position;
float bestScore = Mathf.Infinity;
float averageDistance = 5.0f; //Tweak this
// loop through
foreach (GameObject enemy in all_enemies)
if(enemy == lock_on_obj){continue;} //Make sure you don't check the current lock-on itself
Vector3 diff = enemy.transform.position - position;
float curDistance = diff.magnitude;
float angle = Vector2.Angle(stick_dir, diff);
float score = Mathf.Abs(angle)/180f + curDistance / averageDistance;
if(score < bestScore){
closest = enemy;
bestScore = score; //This!!
}
}
You can add the weights too if you want, but tweaking the averageDistance should lead to the same result.
Good luck!
Edit : Ah, hold on! Also, don’t forget to set the bestScore to score! Sorry about that