I know this is bad code (it is to be tidied up later)
I am trying to modify a tutorial by adding in a special move. Without the code for the special move the game works fine. When the enemy count is 0 a new wave of enemies is spawned. But if I use the special move it spawns about 20 waves
I'm guessing it's to do with the Coroutine and delay? My log shows enemyCount.Length goes up to a number but then to 0 for no reason which adds to the wave until it stops at around 18
I can't see why. Help please?
Player code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float playerSpeed = 5;
private Rigidbody playerRb; //to access it for movement
private GameObject focalPoint; //to pan camera around
private int playerHealth = 10;
//powerup vars
public bool hasPowerUp; //to allow player to hit harder with it
public float powerUpStrength = 15f; //so we can adjust how hard the powerup hits
//special move vars
public bool specialMove = false;
public Vector3 jump;
public float jumpForce = 2.0f;
public Vector3 drop;
public float dropForce = 2.0f;
public bool smashCam = false; //to get camera to shake and set enemy flying
public float flingStrength = 40f; //so we can adjust how hard the special move hits
void Start()
{
//set up the rigidbody component to be used for movement
playerRb = GetComponent<Rigidbody>();
focalPoint = GameObject.Find("FocalPoint");
jump = new Vector3(0.0f, 2.0f, 0.0f); //jump for powerup move
drop = new Vector3(0.0f, -2.0f, 0.0f); //drop for powerup move
}
void Update()
{
//moves player
float forwardInput = Input.GetAxis("Vertical");
playerRb.AddForce(focalPoint.transform.forward * playerSpeed * forwardInput);
if (Input.GetKeyDown(KeyCode.Space)) {
SpecialMove();
}
//special move jumping stuff
if (specialMove == true && gameObject.transform.position.y > 0.8)
{
StartCoroutine(SpecialMoveDelay());
}
}
IEnumerator SpecialMoveDelay()
{
playerRb.constraints = RigidbodyConstraints.FreezePositionY; //freeze in air
smashCam = true; //flag for camera spin
yield return new WaitForSeconds(1);
specialMove = false; //to set enemies moving again
smashCam = false; //turn off the smash cam
//smash player into ground
playerRb.constraints = RigidbodyConstraints.None; //release xy constraints from earlier
playerRb.AddForce(drop * dropForce, ForceMode.Impulse); //smash to ground
//*******Fling enemies away*******************
var enemyCount = FindObjectsByType<EnemyClass>(FindObjectsSortMode.None); //get all the enemies
for (int i = 0; i < enemyCount.Length; i++) //for every enemy
{
//get the rigidbody of the enemy so we can fling it
Rigidbody enemyRb = enemyCount[i].gameObject.GetComponent<Rigidbody>();
//set the force that will fling enemies away
Vector3 awayFromPlayer = (enemyCount[i].gameObject.transform.position - transform.position);
//output something and apply the force to the enemy
enemyRb.AddForce(awayFromPlayer * flingStrength, ForceMode.Impulse);
}
}
//method to kill the player if health is below 0
public void CheckHealth()
{
if (playerHealth <= 0)
{
gameObject.transform.SetPositionAndRotation(new Vector3(0,10,0), transform.rotation);
}
}
//method to take player damage
public int TakeDamage(int damage) //gets given damage from other methods
{
playerHealth = playerHealth - damage; //reduce the player health
CheckHealth(); //calls the check health method
Debug.Log("Health is: " + playerHealth); //displays player health (unless player dies in checkHealth method
return damage; //return the damage (not used but we need to return something!)
}
public void OnTriggerEnter(Collider other)
{
if (other.CompareTag("PowerUp") )
{
hasPowerUp = true; //set the powerup trigger for the collission method
Destroy(other.gameObject); //destroy the power up
}
if (other.CompareTag("Health"))
{
playerHealth += 5; //add 5 to player's health
Debug.Log("Health = " + playerHealth); //output current health
Destroy(other.gameObject); //destroy the health object
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Enemy" && hasPowerUp)
{
//get the rigidbody of the enemy so we can fling it
Rigidbody enemyRb = collision.gameObject.GetComponent<Rigidbody>();
//set the force that we will fling it
Vector3 awayFromPlayer = (collision.gameObject.transform.position - transform.position);
//output something and apply the force to the enemy
enemyRb.AddForce(awayFromPlayer * powerUpStrength, ForceMode.Impulse);
}
}
public void SpecialMove()
{
Debug.Log("SM");
specialMove = true;
//jump player up but only on Y pos
playerRb.AddForce(jump * jumpForce, ForceMode.Impulse);
//freeze X & z to stop momentum
playerRb.constraints = RigidbodyConstraints.FreezePositionX;
playerRb.constraints = RigidbodyConstraints.FreezePositionZ;
}
}
Spawner code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManagerScript : MonoBehaviour
{
// vars to store game objects - set in editor
public GameObject enemyPrefab;
public GameObject powerUpPrefab;
public GameObject bombPrefab;
public GameObject trapPrefab;
public GameObject healthPrefab;
private float spawnRange = 9; //var for co-ordinate rage to spawn objects
public int waveNumber = 1; //to keep track of level / number of enemies
private GameObject player; //to use for movement direction
private PlayerController playerController;
void Start()
{
player = GameObject.Find("Player");
playerController = player.GetComponent<PlayerController>();
SpawnEnemyWave(waveNumber); //calls the first wave
Instantiate(powerUpPrefab, GenerateSpawnPoint(), powerUpPrefab.transform.rotation); //spawns first powerup
Debug.Log("Level 0"); //outputs current level
}
//method to spawn enemies, bombs and traps
void SpawnEnemyWave(int enemiesToSpawn)
{
Debug.Log("Spawning = " + enemiesToSpawn);
for (int i = 0; i < enemiesToSpawn; i++)
{
Instantiate(enemyPrefab, GenerateSpawnPoint(), enemyPrefab.transform.rotation);
}
//spawn bombs if on level 4
if (enemiesToSpawn > 3)
{
for (int i = 0; i < enemiesToSpawn-2; i++)
{
Instantiate(bombPrefab, GenerateBombSpawnPoint(), bombPrefab.transform.rotation);
}
}
//spawn traps if on level 6
if (enemiesToSpawn > 5)
{
//delete existing raps
Destroy(GameObject.FindWithTag("Trap"));
//create new traps
for (int i = 0; i < enemiesToSpawn-3; i++)
{
Instantiate(trapPrefab, GenerateTrapSpawnPoint(), trapPrefab.transform.rotation);
}
}
}
//method to randomize spawn points for enemies
private Vector3 GenerateSpawnPoint()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, 0, spawnPosZ);
return randomPos;
}
//method to randomize spawn points for bombs
private Vector3 GenerateBombSpawnPoint()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, 10, spawnPosZ);
return randomPos;
}
//method to randomize spawn points for traps
private Vector3 GenerateTrapSpawnPoint()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, -0.8f, spawnPosZ);
return randomPos;
}
void FixedUpdate()
{
//enemyCount = FindObjectsOfType<EnemyClass>().Length; < Depreciated
var enemyCount = FindObjectsByType<EnemyClass>(FindObjectsSortMode.None);
Debug.Log("enemies = " + enemyCount.Length);
if (enemyCount.Length == 0 && playerController.smashCam == false)
{
waveNumber++;
Debug.Log("Wave Num = " + waveNumber);
SpawnEnemyWave(waveNumber);
Instantiate(powerUpPrefab, GenerateSpawnPoint(), powerUpPrefab.transform.rotation);
if (waveNumber > 3)
{
Instantiate(healthPrefab, GenerateSpawnPoint(), powerUpPrefab.transform.rotation);
}
}
}
}
Enemy code:
public class EnemyClass : MonoBehaviour { private Rigidbody enemyRb; //to use for movement private GameObject player; //to use for movement direction private PlayerController playerController;
public int enemyHealth = 10; //so we can damage the enemey
public int enemySpeed = 3; //speed of enemy
void Start()
{
enemyRb = GetComponent<Rigidbody>();
player = GameObject.Find("Player");
playerController = player.GetComponent<PlayerController>();
}
void Update()
{
EnemyMove(); //move the enemey
CheckHealth(); //check health in case of death
CheckFallen(); //check if they've fallen off the edge
}
public void EnemyMove()
{
if (playerController.specialMove == false)
{
enemyRb.constraints = RigidbodyConstraints.None; //unfreeze constraints
//move player
Vector3 lookDirection = (player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * enemySpeed);
}
else
{
enemyRb.constraints = RigidbodyConstraints.FreezeAll;
}
}
public void CheckHealth()
{
if (enemyHealth <= 0)
{
Debug.Log("Enemy died");
Destroy(gameObject);
}
}
public void CheckFallen()
{
if (transform.position.y < -10) //if they have fallen -10 in y pos destroy it
{
Destroy(gameObject);
}
if (transform.position.x < -30 || transform.position.x > 30 || transform.position.z > 30 || transform.position.z > 30) //if they have gone past 100 in x pos destroy it
{
Destroy(gameObject);
}
}
//method to take damage
public int TakeDamage(int damage)
{
enemyHealth = enemyHealth - damage;
Debug.Log("Enemy hit by bomb");
CheckHealth();
return damage;
}
}
Somehow it's being set to 0 before anything is being destroyed to reset it
Thanks for any help
your problem is here
if (Input.GetKeyDown(KeyCode.Space))
{
SpecialMove();
}
This sets specialMove
to true
the frame space is pressed, but doesn't ever set it back to false.
This means that this code here
if(specialMove == true && gameObject.transform.position.y > 0.8)
{
StartCoroutine(SpecialMoveDelay());
}
will be called every frame your position is above 0.8 (instead of just the first frame), creating many many delayed spawns.
This effect won't be instant, but each frame will begin a new coroutine execution.
To fix this, add a second case to your if statement.
if (Input.GetKeyDown(KeyCode.Space))
{
SpecialMove();
}
else
{
specialMove = false;
}