Due to a certain mechanic in my current project, I cannot have the player respawn at preset locations after dying, and instead have to calculate to the nearest safe location at runtime.
I intend to do so by checking for grounded locations that are at least X distance away from all enemies. The problem is that I have no idea how to go about calculating such a position.
What is an efficient way to determine the nearest spot that’s at least a certain distance away from an array of objects, while making sure that said spot would be touching the ground so the character doesn’t respawn in midair? I’d prefer answers in C#, if possible.
You could use OverlapShere(), presuming your enemies have colliders, to find all enemies within a radius.
If your map is not flat, then you could choose a random x, z location and the ray cast down from some distance to find a ground position. If you don’t find one (you don’t collide with your ground collider), then you could try again.
Probably it would be best to have a list of objects that give you starting points though. Perhaps these could be check points so you choose appropriate spawns.
Placing spawns, players and enemies on separate layers will help you consider only a subset of colliders as well.
Have an array of enemies (and maybe friends) and another for possible respawn positions
enemies : GameObject[];
respawns : GameObject[];
function Start()
{
// Find initial enemies
enemies = GameObject.FindGameObjectsWithTag ("Enemy");
// Same here, find respawns at startup
respawns = GameObject.FindGameObjectsWithTag ("Respawn");
}
You can also have a function for updating those two on the go. Don’t call it on every frame though.
function UpdateEnemies()
{
enemies = GameObject.FindGameObjectsWithTag ("Enemy");
}
function UpdateRespawns()
{
respawns = GameObject.FindGameObjectsWithTag ("Respawn");
}
Then you simply crossreference them to each other. Go through all respawns with while() and under that go through all enemies with another while() for each respawn, or use some other solution you prefer.
You can then have another array, like:
availableRespawns : GameObject;
Or
availableRespawns = new GameObject();
And then if the shortest distance to closest enemy is longer than your ‘safe’ threshold, you add that respawn to the availableRespawns list :
availableRespawns.Push(respawnYouClearedInYourScript);
You can use either the distance or OverlapSphere, I don’t know which is more efficient. I would think that simply iterating through builtin arrays would be pretty fast, making simple trigonometric calculation per object. I don’t exactly recall all the things that happen with OverlapSphere.
But indeed, I would assume that having set respawns per level would have higher long term quality… considering that humans are better at choosing appropriate positions.
I personally like to use something like:
zInt : int = 0;
while (zInt < respawns.length)
{
zInt2 : int = 0;
while (zInt2 < enemies.length)
{
var distance : float =
Vector3.Distance(respawns[zInt].transform.position,
enemies[zInt2].transform.position);
if (distance < safeThreshold)
{
safeRespawns.Push(respawns[zInt]);
}
zInt2 += 1;
}
zInt += 1;
}
safeRespawns could also be a builtin. Then you’d need a var called ‘safeRespawnIndex : int;’.
If so, then you’d make:
safeRespawns = new GameObject[respawns.length];
safeRespawnIndex = 0;