- Home /
Enemy Spawn Script Spawning Infinite Enemies [Help]
I'm trying to test my enemy spawn script (c#), and I thought I had it pretty well figured out. For the meantime it's all attached to my enemy prefab, but eventually I will attach it to the player. This is how the script is supposed to work:
In the start function, check how many objects have the tag "Enemy".
If it's greater than 0 (for testing) run the StartLevel function, passing a levelIndex variable.
In StartLevel, do some things (not implemented), then call SpawnEnemy, passing the number of enemies to spawn.
In SpawnEnemy, for each enemy that needs to be spawned, find a random point around the player (x,z), then pass that point to a function that finds the terrain height at this point. If it returns a valid number, and passes some other conditions (isn't too close to player, or too close to other spawned enemies) spawn this enemy, facing the player.
Save this spawn point as the lastSpawnPoint for any other enemies that need spawning.
Here is the relevant code:
using UnityEngine;
using System.Collections;
public class EnemyScript : MonoBehaviour {
public Transform playerPos;
public float enemySpeed = 5f;
public float detectionRadius = 150;
public CharacterController cc;
public GameObject enemyPrefab;
public bool sawPlayer = false;
public Vector3 playerDirection = Vector3.zero;
public float verticalSpeed;
public float spawnDistance = 50.0f;
public float minEnemySpawnDensity = 6f;
public float minSpawnDistance = 20f;
public int spawnThisMany = 0;
// Use this for initialization
void Start () {
//get the transform of the player
playerPos = GameObject.FindWithTag("Player").transform;
//get the character controller attached to the enemyPrefab
cc.GetComponent<CharacterController>();
//if there are no enemies present, start level 1
if(GameObject.FindGameObjectsWithTag("Enemy").Length >= 0){
StartLevel(spawnThisMany);
}
}
//this function handles the spawning of enemies around you. It is called
//from the startLevel function, which handles the number of enemies in
//each level.
void SpawnEnemy(int numberOfEnemies){
//find a random point around the player
Vector3 spawnPoint = Random.insideUnitCircle.normalized*spawnDistance;
Vector3 lastSpawnPoint = Vector3.zero;
//this sets the spawn point height to the terrain height, plus a the radius of the sphere's character controller
spawnPoint.y = TerrainHeight(spawnPoint) + cc.radius;
for(int enemyCount = 0; enemyCount < numberOfEnemies; enemyCount++){
//if terrain height returns infinity, or the difference in this spawn
//point and the last spawn point is greater than the minimus spawn
//density, or the enemy spawns to close to the player, start over.
if(spawnPoint.y == Mathf.Infinity || (spawnPoint - lastSpawnPoint).magnitude <= minEnemySpawnDensity || (spawnPoint - playerPos.position).magnitude <= minSpawnDistance){
SpawnEnemy(numberOfEnemies - enemyCount);
}
//if all the conditions are met, an enemy is spawned facing the player,
//and the spawn point is saved as the last spawn point.
else{
//setting look direction for enemy, cancelling out the y portion
Vector3 lookDirection = playerPos.position;
lookDirection.y = 0f;
Instantiate(enemyPrefab,spawnPoint,Quaternion.LookRotation(lookDirection));
lastSpawnPoint = spawnPoint;
}
}
}
//this starts each level
void StartLevel(int levelIndex){
//do stuff here
SpawnEnemy (levelIndex);
}
//TerrainHeight finds the height of the terrain given a 2D position vector
float TerrainHeight(Vector2 spawnPoint){
//declare a raycast hitpoint
RaycastHit hitPoint;
//go to spawnPoint and cast a ray upwards, if it hits return that y position.
//if it doesn't, shoot a ray downwards and it should hit, if not, return infinity.
if(Physics.Raycast(spawnPoint,Vector3.up, out hitPoint)){
return hitPoint.transform.position.y;
}
else if(Physics.Raycast(spawnPoint,Vector3.down, out hitPoint)){
return hitPoint.transform.position.y;
}
else{
return Mathf.Infinity;
}
}
}
I'm pretty sure the problem lies within my for statement, although for the life of me I can't figure it out. It's probably something silly. But everytime I go to play mode, the enemies just keep piling up and lagging my game. About 150 spawn before I can get out of play mode. Thanks for any help.
Where are you declaring the value of 'numberofenemies'?
It's passed as an integer into SpawnEnemy from StartLevel. I declared the integer spawnThis$$anonymous$$any, which is passed to StartLevel as the levelIndex, then levelIndex is passed to SpawnEnemy as numberOfEnemies. Yea, it's a bit convoluted at the moment. But eventually I'll do stuff in the startlevel method to calculate an actual number of enemies per level.
Answer by KellyThomas · Jan 12, 2014 at 02:18 AM
@Moohasha is correct in that every time you call recursively call SpawnEnemy
you are going to double up on the remainder of your enemy spawns. The reason you will double up is that you are starting a new loop, and will (once it is finished) continue the old loop.
I would recommend an iterative rather than recursive approach:
void SpawnEnemy(int numberOfEnemies) {
Vector3 lastSpawnPoint = Vector3.zero;
Vector3 spawnPoint = Vector3.zero;
for(int enemyCount = 0; enemyCount < numberOfEnemies; enemyCount++){
do {
spawnPoint = Random.insideUnitCircle.normalized * spawnDistance;
spawnPoint.y = TerrainHeight(spawnPoint) + cc.radius;
} while(IsInvalidSpawnPoint(spawnPoint, lastSpawnPoint));
Vector3 lookDirection = playerPos.position;
lookDirection.y = 0f;
Instantiate(enemyPrefab, spawnPoint, Quaternion.LookRotation(lookDirection));
lastSpawnPoint = spawnPoint;
}
}
boolean IsInvalidSpawnPoint(Vector3 spawnPoint,Vector3 lastSpawnPoint){
return spawnPoint.y == Mathf.Infinity ||
(spawnPoint - lastSpawnPoint).magnitude <= minEnemySpawnDensity ||
(spawnPoint - playerPos.position).magnitude <= minSpawnDistance;
}
Thank you for this. There were some errors in your code, as well some unwanted behaviour. For instance my raycast hit was returning the height of the terrain gameobject, and my enemies were spawning at world coords (0,0). But I managed to sort them out, and now it works perfectly, thanks you to and @$$anonymous$$oohasha.
@GregoryNeal glad it hear it worked out. Without running it in the context of you project it's sure to have a few kinks.
I'm using this solution as a reference for my own Spawn$$anonymous$$anager class...but I'm having the same problem you had @GregoryNeal where all my enemies are spawning at (0,0). Can you help me out with the solution you found?
Answer by Moohasha · Jan 12, 2014 at 01:22 AM
It looks like you accidentally set up a recursive method in SpawnEnemy. You pass in the number to spawn and use that in your for loop, but then call SpawnEnemy with the same number within the for loop. This will just keep going forever. Rather than having your for loop inside SpawnEnemy, have it outside, and have the methods only spawn one at a time.
When I call SpawnEnemy recursively, I pass it the number of enemies - the number of spawned enemies. If the spawn is successful, it increases enemyCount, then loops, if it isn't successful, it calls SpawnEnemy(numberOfEnemies - enemyCount). I don't understand how that's the problem D:
Your answer
Follow this Question
Related Questions
How do I get my emeny AI to detect if he sees a player, or one of the good AI? (C#) 3 Answers
If I duplicate enemies, will their scripts mess each other up? 2 Answers
Make player not be seen by AI, when player in foilage and shadows. 1 Answer
Make AI walk around randomly until Player is Seen. (C#) 2 Answers
The name 'Joystick' does not denote a valid type ('not found') 2 Answers