Player Health persists through multiple scenes using Unity - c#

I currently have a 2d platformer, where the player is given 9 lives on start, and loses a life everytime they hit an obstacle.
The GameManager script stores and displays the health and the Entity script updates the health in GameManager if the player loses a life.
If the player loses a life, they can respawn in that level.
On completion of a level, the player goes to a new scene "Level Complete", where they can choose to continue to the next level or quit the game.
However, when the player continues to the next level, the currentHealth variable does not persist and resets to 0.
I know WHY this problem is occurring, but I do not know how to fix it.
currentHealth is not being called anywhere in LevelComplete, so on Start in GameManager, it has lost the value of currentHealth.
I can't call GameManager on LevelComplete because it will spawn the player. So I'm not sure how to pass the variable.
Here are my scripts.
Game Manager
using UnityEngine.UI;
using System.Collections;
public class GameManager : MonoBehaviour {
public GameObject player;
private GameCamera cam;
private GameObject currentPlayer;
private Vector3 checkpoint;
public Text livesText;
private Entity livesCount;
public static int startHealth = 9;
public int currentHealth;
public bool playerDeath = false;
public static int levelCount = 2;
public static int currentLevel = 1;
void Start () {
cam = GetComponent<GameCamera> ();
livesText.text = currentHealth.ToString ();
if(GameObject.FindGameObjectWithTag("Spawn")){
checkpoint = GameObject.FindGameObjectWithTag("Spawn").transform.position;
}
SpawnPlayer (checkpoint);
}
private void SpawnPlayer(Vector3 spawnPos) {
currentPlayer = Instantiate (player, spawnPos, Quaternion.identity) as GameObject;
cam.setTarget(currentPlayer.transform);
}
public void Update(){
if(!currentPlayer){
if (Input.GetButtonDown ("Respawn")) {
playerDeath = false;
SpawnPlayer (checkpoint);
}
}
if (playerDeath) {
Destroy (currentPlayer);
}
if (currentPlayer) {
currentHealth = currentPlayer.GetComponent<Entity> ().GetHealth ();
}
livesText.text = currentHealth.ToString ();
}
public void SetCheckPoint(Vector3 cp){
checkpoint = cp;
}
public void EndLevel(){
if(currentLevel < levelCount){
currentLevel++;
Application.LoadLevel("Level Complete");
}
else{
Application.LoadLevel("Game Complete");
}
}
}
Entity
using UnityEngine;
using System.Collections;
public class Entity : MonoBehaviour {
public static int currHealth;
private int checkHealth; //Make sure 0 < health < 9
public void ModifyHealth(int amount){
checkHealth = currHealth + amount;
if (checkHealth >= 9) {
currHealth = 9; //Health can't be greater than 9
}
else if(checkHealth <= 0){
Die (); //End game if health is less than or equal to 0
}
else{
currHealth = checkHealth; //Otherwise, update Health
}
}
public int GetHealth(){
return currHealth;
}
public void SetHealth(int currentHealth){
currHealth = currentHealth;
}
public void Die(){
Application.LoadLevel ("Game Over");
}
}
Level Complete
using UnityEngine;
using System.Collections;
public class LevelComplete : MonoBehaviour {
public GUISkin gSkin;
void OnGUI(){
GUI.skin = gSkin;
if(GUI.Button(new Rect((Screen.width/2) - Screen.width *0.15f,Screen.height/2 - Screen.height *0.05f,Screen.width*0.3f,Screen.height*0.1f),"Next Level")){
Application.LoadLevel ("Level 2");
}
if(GUI.Button(new Rect((Screen.width/2) - Screen.width *0.15f,Screen.height/2 + 20.0f,Screen.width*0.3f,Screen.height*0.1f),"Quit")){
Application.Quit ();
}
}
}

Related

How do i add multiple enemies to my unity wave system?

I have a wave script for my game and it works fine, however after a while I have realised it gets quite dull fighting against the same enemies over and over again, so I was wondering if I could make a list of some sort to store all enemies that will be in each wave.
Here is my script;
SpawnManager.cs
using System.Collections;
using UnityEngine;
[System.Serializable]
public class Wave
{
public int EnemiesPerWave;
public GameObject Enemy;
}
public class SpawnManager : MonoBehaviour
{
public Wave[] Waves; // class to hold information per wave
public Transform[] SpawnPoints;
public float TimeBetweenEnemies = 2f;
public GameObject HelpText;
public GameObject Shop;
public GameObject shopfx;
public Transform ShopL;
bool shopactive = false;
private int _totalEnemiesInCurrentWave;
private int _enemiesInWaveLeft;
private int _spawnedEnemies;
private int _currentWave;
private int _totalWaves;
void Start ()
{
_currentWave = -1; // avoid off by 1
_totalWaves = Waves.Length - 1; // adjust, because we're using 0 index
StartNextWave();
}
void Update()
{
if (_enemiesInWaveLeft <= 0 && Input.GetKeyDown(KeyCode.R))
{
StartNextWave();
}
}
void StartNextWave()
{
_currentWave++;
// win
if (_currentWave > _totalWaves)
{
return;
}
_totalEnemiesInCurrentWave = Waves[_currentWave].EnemiesPerWave;
_enemiesInWaveLeft = 0;
_spawnedEnemies = 0;
StartCoroutine(SpawnEnemies());
}
// Coroutine to spawn all of our enemies
IEnumerator SpawnEnemies()
{
GameObject enemy = Waves[_currentWave].Enemy;
while (_spawnedEnemies < _totalEnemiesInCurrentWave)
{
_spawnedEnemies++;
_enemiesInWaveLeft++;
int spawnPointIndex = Random.Range(0, SpawnPoints.Length);
// Create an instance of the enemy prefab at the randomly selected spawn point's
// position and rotation.
Instantiate(enemy, SpawnPoints[spawnPointIndex].position,
SpawnPoints[spawnPointIndex].rotation);
yield return new WaitForSeconds(TimeBetweenEnemies);
}
yield return null;
}
// called by an enemy when they're defeated
public void EnemyDefeated()
{
_enemiesInWaveLeft--;
// We start the next wave once we have spawned and defeated them all
if (_enemiesInWaveLeft == 0 && _spawnedEnemies == _totalEnemiesInCurrentWave)
{
HelpText.SetActive(true);
Invoke("SetFalse",5.0f);
Shop.SetActive(true);
Invoke("LateUpdate",1f);
Instantiate(shopfx, new Vector3(ShopL.transform.position.x, ShopL.transform.position.y, ShopL.transform.position.z), Quaternion.identity);
shopactive = true;
}
}
void SetFalse()
{
HelpText.SetActive(false);
}
void LateUpdate()
{
if(Input.GetKeyDown(KeyCode.R) && shopactive == true)
{
Shop.SetActive(false);
Instantiate(shopfx, new Vector3(Shop.transform.position.x, Shop.transform.position.y, Shop.transform.position.z), Quaternion.identity);
shopactive = false;
}
}
}

C# spawn manager creating waves of enemies too quickly

I'm a beginner making my first game in Unity, following Unity's Create With Code course. I'm creating a shooter game that will use hand tracking. I haven't set up hand tracking yet so i created an OnTrigger input that explodes objects when I hit space. I created the spawn manager below to spawn waves of enemy attack, but they all the waves are spawning enemies too fast. It seems like they're spawning automatically instead of when the first wave has been destroyed.
Is there an easier way to spawn at a slower rate? Or spawn only when there are no more enemies alive?
EDIT: Added Attack script below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnAttack : MonoBehaviour
{
public GameObject Trumps;
private float spawnRange = 9;
public int enemyCount;
public int waveNumber = 1;
void Start()
{
SpawnEnemyWave(waveNumber);
//InvokeRepeating("GenerateSpawnPosition", startDelay, randomInterval);
}
// Update is called once per frame
void Update()
{
enemyCount = FindObjectsOfType<Attack>().Length;
if(enemyCount == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
void SpawnEnemyWave(int enemiesToSpawn)
{
for (int i = 0; i < enemiesToSpawn; i++)
{
Instantiate(Trumps, GenerateSpawnPosition(), Trumps.transform.rotation);
}
}
private Vector3 GenerateSpawnPosition()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, 0, spawnPosZ);
return randomPos;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attack : MonoBehaviour
{
public float speed = 0.5f;
public GameObject Player;
private Rigidbody enemyRb;
// Start is called before the first frame update
void Start()
{
enemyRb = GetComponent<Rigidbody>();
Player = GameObject.Find("Player");
this.transform.LookAt(Player.transform);
}
// Update is called once per frame
void Update()
{
Vector3 lookDirection = (Player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * speed);
transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
private void OnTriggerEnter(Collider other)
{
Destroy(gameObject);
Debug.Log("Game Over");
}
}
I guess that you can use the invoke method:
Invoke("NameOfTheMethod", 1f)
What this method does is that it waits a certain amount of seconds before calling a method. You have to write the name of the method in quotation marks and then select how long you want to wait before the method is called (The "1f" represents the delay in seconds.) In your case, you can make the script wait before spawning enemies.
I don't know your Attack script but I would use something like
public class Attack : MonoBehaviour
{
public static readonly HashSet<Attack> AliveAttackInstances = new HashSet<Attack>();
private void Awake()
{
AliveAttackInstances.Add(this);
}
private void OnDestroy()
{
AliveAttackInstances.Remove(this);
}
}
So you can all the time in a cheaper way check how many and which enemies are alive like
public class SpawnAttack : MonoBehaviour
{
// I would change this type here to make sure your spawned prefab actually
// HAS an Attack attached .. otherwise your enemyCount will always be 0
public Attack Trumps;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
Then in order to add a certain delay before spawning the next wave you could use a simple timer like
public class SpawnAttack : MonoBehaviour
{
public Attack Trumps;
[SerializeField] private float delay = 1f;
private float timer;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
timer = delay;
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
}
Try to use Coroutine.
Here's a video about Coroutines: https://www.youtube.com/watch?v=qolMYyq0nX0
My example:
public class Spawn : MonoBehaviour {
private float TimeToWait = 3f;
public int enemyCount = 0;
public int waveNumber = 0;
public GameObject enemy;
void Start()
{
StartCoroutine(SpawnEnemyWave(waveNumber));
}
void Update()
{
if (enemyCount == 0)
{
waveNumber++;
StartCoroutine(SpawnEnemyWave(waveNumber));
}
if (Input.GetMouseButtonDown(0))
{
enemyCount--;
}
}
IEnumerator SpawnEnemyWave(int enemiesToSpawn)
{
//"Things to do before the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
enemyCount++;
}
yield return new WaitForSeconds(TimeToWait); // at this example we wait 3 seconds (float TimeToWait = 3f;)
//"Things to do after the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
Debug.Log("New Enemy!");
Instantiate(enemy, transform.position, Quaternion.identity);
}
}
}

Unity 2D, C# - Why my OnCollisionEnter2D doesn't collide?

So, i created two scripts, one named "Stats.cs" registers the player stats and the other one named "PlayerHealth.cs" "makes" the player take damage on contact and updates the Hearts in the HUD. My problem is, whenever i collide with an object that has a tag named "Projectile" it simply doesn't work, my player doesn't take damage at all. The Stats.cs script isn't in any object, the PlayerHealth.cs is in my Player object.
Stats.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Stats{
private int health;
public int maxHP = 3;
public int Health
{
get
{
//Some code
return health;
}
set
{
//Some code
health = Mathf.Clamp(value, 0, maxHP);
}
}
public void SetHealth()
{
Health = maxHP;
}
}
PlayerHealth.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
Stats playerStats = new Stats();
public int curHealth;
public int numOfHearts = 3;
public Image[] hearts;
public Sprite fullHeart;
public Sprite emptyHeart;
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Projectile"))
{
Debug.Log("Hello");
DamagePlayer(1);
Destroy(other.gameObject);
}
}
public void DamagePlayer(int damage)
{
playerStats.Health -= damage;
}
// Start is called before the first frame update
void Start()
{
playerStats.SetHealth();
curHealth = numOfHearts;
}
// Update is called once per frame
void Update()
{
curHealth = playerStats.Health;
numOfHearts = playerStats.maxHP;
if (curHealth>numOfHearts){
curHealth = numOfHearts;
}
if(curHealth <= 0){
Die();
}
for (int i = 0; i < hearts.Length; i++)
{
if(i < curHealth){
hearts[i].sprite = fullHeart;
} else
{
hearts[i].sprite = emptyHeart;
}
if(i < numOfHearts){
hearts[i].enabled = true;
} else {
hearts[i].enabled = false;
}
}
}
void Die(){
//Restart
Application.LoadLevel(Application.loadedLevel);
}
}
curHealth is updating so it will stay as the actual Health in Stats and will change the images in HUD.
The Player has a RigidBody2D on him two colliders, one is a box for the body, and the other is a circle collider, so when the player crouches, the circle collider disables.
The Projectiles also have and RigidBody2D with 0 gravity (so it won't fall in mid air) and a BoxCollider2D.
I would check and make sure that the projectile is tagged as Projectile and that the BoxCollider doesn't have "Is Trigger" checked.
I should also say, iterating with that for loop in the Update is very bad practice performance wise. That is happening literally as fast as the machine can loop it and it is doing that every time. I would look into updating it on an event.
Hope this helps!

Load the scene when all enemies die

I am creating a laser defender game in unity and I have this problem where when I shoot the first enemy it takes me directly to the NextLevelMenu scene but I want it to load when all the enemies are killed(on this level I have 5 enemies to kill). I have been told that I need to send a reference to the instance of its spawner to every spawned enemy but I did not quite understand. Can someone help, please?
EnemySpawner Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour {
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public LevelManager myLevelManager;
public int maximumnumberofhits = 0;
public static EnemySpawner Instance = null;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while(CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
if (Instance == null)
Instance = this;
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
public void OnEnemyDeath()
{
CurrentNumOfEnemies--;
if (CurrentNumOfEnemies < 1)
{
// You killed everyone, change scene:
LaserLevelManager.LoadLevel("NextLevelMenu");
}
}
}
EnemyShooting script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if(CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
I would add a two fields to the spawner script. EnemiesToNextLevel and KilledEnemies. Then in the OnEnemyDeath() of your spawner, you may increase KilledEnemies everytime it is called, and then ask if KilledEnemies >= EnemiesToNextLevel, before changing the scene.
Sure there are a lot of other ways to do it, but for me thats the easiest.

score not resetting to zero

i'm having problem in the score and high score when i play the game and eat coin its increasing the score and high score but when i die and play again its start counting from the score before i dead like i dead my score was 3 when i play again without closing the game its start counting from 3 and the high score aren't saving
Score Manager script
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class ScoreManager : MonoBehaviour
{
public Text scoreText;
public Text hiScoreText;
public static int scoreCount;
public int hiScoreCount;
public bool scoreIncreasing;
// Use this for initialization
void Start()
{
if (PlayerPrefs.HasKey("HighScore"))
{
hiScoreCount = PlayerPrefs.GetInt("HighScore");
}
}
// Update is called once per frame
void Update()
{
if (scoreIncreasing)
{
scoreCount = BallMain.getPoints();
}
if (scoreCount > hiScoreCount)
{
hiScoreCount = scoreCount;
PlayerPrefs.SetFloat("HighScore", hiScoreCount);
}
scoreText.text = "Score: " + (scoreCount);
hiScoreText.text = "High Score: " + (hiScoreCount);
}
}
BallMain Script
using UnityEngine;
using System.Collections;
public static class BallMain {
private static float ballSpeed = 1.2f;
private static int points;
private static int lives = 0;
public enum BallStateEnum { shielded,Vulnerable};
public static BallStateEnum ballState = BallStateEnum.Vulnerable;
public static float getBallSpeed()
{
return ballSpeed;
}
public static void IncreaseSpeed()
{
ballSpeed += 0.1f;
}
public static void IncreasePoints()
{
points++;
}
public static int getPoints()
{
return points;
}
public static int getLive()
{
return lives;
}
public static void addLife()
{
lives++;
}
}
CoinHandler Script
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class coinHandler : MonoBehaviour {
public Transform particles;
void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Player")
{
Instantiate(particles, new Vector3(transform.position.x, transform.position.y, -0.2f), Quaternion.identity);
BallMain.IncreaseSpeed();
BallMain.IncreasePoints();
GameObject.FindWithTag("CoinUI").GetComponent<Text>().text = BallMain.getPoints().ToString();
Destroy(gameObject);
}
}
BadCoinHandler Script
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class BadCoinHandler : MonoBehaviour {
public Transform particles;
// Use this for initialization
void Start ()
{
Destroy(gameObject, 8f);
}
void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Player")
{
Instantiate(particles, new Vector3(transform.position.x, transform.position.y, -0.2f), Quaternion.identity);
Destroy(gameObject);
if(BallMain.getLive() == 0 && BallMain.ballState == BallMain.BallStateEnum.Vulnerable)
{
SceneManager.LoadSceneAsync(0);
}
}
}
}
You have to zero the score manually. Because you are using the static keyword. So create another void in BallMain and make the point zero; Example:
public static void ResetPoints()
{
points = 0;
}
And call it on awake method on Score Manager script; Example:
void Awake()
{
scoreCount = 0; //Also the score here
BallMain.ResetPoints();
}
To save your high score use this:
PlayerPrefs.SetInt("HighScore",scoreCount);//HighScore is key and scoreCount is the number you want to save
PlayerPrefs.Save();
BTW I thing you need another void to reset ball speed as well.

Categories

Resources