- Home /
The question is answered, right answer was accepted
Calculating a Respawn Position at Runtime
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.
I think OverlapSphere, rather than spherecast.. Otherwise that's what I would do too
Sorry, I'll correct that, I always get those two mixed up!
That makes sense. I can't believe I'd forgotten about OverlapSphere.
Thanks!
Answer by SheepHugger · Jul 25, 2012 at 12:16 PM
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);
Here's an older answer for one solution of how to go through multiple distances. http://answers.unity3d.com/questions/178600/check-distance-between-multiple-objects.html
I hope this helps you get started. Is there anything else?
SheepHugger out.
You wouldn't to push a list of available spawn points if you are happy with the first that matches your criteria, or you could randomly ignore a few.
Similar example on Unity's docs for FindGameObjectWithTag.
http://docs.unity3d.com/Documentation/ScriptReference/GameObject.FindGameObjectsWithTag.html
This is an alternative approach but you end up considering every enemy and every spawn potentially.
Yes, I assumed that he was going to apply additional criterion to the respawns, like, if it was a game similar to Battlefield, you wouldn't want to spawn the players 5km behind the frontline - you'd rather want them spawned near the front but to a safe respawn. But you're also correct, it takes more CPU this way. Hence why it should only be done once when a player / NPC dies. That way the additional CPU usage would be hardly noticeable, unless you 'nuked' the scene and killed everyone at once.
So the basic idea's to have a large amount of invisible respawn points spaced around the level, use a simple "foreach" to have all of them run an OverlapSphere to check for enemies the instant the character dies, adding them to an array if no enemies overlap said sphere, and then picking the closest?
Well, in exchange for having to set a bunch of respawn points for every map (as opposed to the procedurally generated ones I intended to do), that makes the work a thousand times easier. In fact, I already had a bunch of loops set up for picking the nearest object out of an array in other scripts, so I could probably write this in two $$anonymous$$utes.
Answer by Bovine · Jul 25, 2012 at 09:08 AM
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.
Converted to answer as @burnpsy seemed happy with that suggestion.
Answer by SheepHugger · Jul 25, 2012 at 01:10 PM
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;
And everytime instead of Push() you'd do this:
safeRespawns[safeRespawnIndex] = respawns[zInt];
safeRespawnIndex += 1;
The benefit of this would be that safeRespawnIndex also tells you how many safe respawns were found!
Hope this is what you were after.. :/