I have this issue with my lives script where when the player dies he will lose more than one life, even though in my script the lives calculation function is only meant to minus 1 life, I've been told that it is due to the fact my void function is being called so fast that is counts one death as 2 lives lost sometimes, anyone know how to fix.
Code (CSharp): // You call this method when the player collides with something that kills him // or whenever anything else happens that kills him This is the function that kills the player public void Die() { currentLives--; Debug.Log($"dead at {Time.time}"); SetLivesText(); if (currentLives <= 0) { ResetPlayerState(); // trigger your gameover code } } This is a separate script attached to the collider that if the player touches calls the Die void. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Dead : MonoBehaviour { public GameMaster gameMaster; public GameObject player; void Start() { gameMaster = FindObjectOfType<GameMaster>(); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.tag == "Player") { gameMaster.RespawnPlayer(); other.gameObject.GetComponent<PlayerMovement>().Die(); } } }
First check if Die() is the only place to adjust currentLives. Afterwards check if OnTriggerEnter is the only place to call Die(). If both of these are true, check if OnTriggerEnter maybe gets called twice for some reason, by adding a Debug.Log(). Report back with results to the above.
1. Die() is the only way in which the lives can be deducted from the player 2. The OnTriggerEnter2D is the only place where the function is being called 3. Right so now I'm really confused , after doing some testing, I found that a debug.log placed after the calculation on line 7 is called twice but after the debug.log is played.
You need to clarify what you mean. The sentence may be formulated a bit weirdly, or i'm just not understanding you correctly. Effectively you seem to say that "Debug.Log is printed twice after Debug.Log is played", which doesnt make a whole lot of sense to me. So I assume you placed a Debug.Log in Die() and it gets printed twice for dying once? Seeing how you also lose two lives, this was to be expected. Now we need to figure out why it gets called twice. To create more easily followable console logs, put a Debug.Log("DIE") into die Die() method. Also put a Debug.Log("TRIGGERED") into the OnTriggerEnter2D() method. Now play and die once. Are both messages printed twice?
This screenshot shows the Debug.log's only being printed once but the player has lost 2 lives when they should have lost one.
Code (CSharp): void SetLivesText() { livesText.text = "Lives: " + currentLives; } This works with the void Die funtion
Can you just show the entire script which includes the Die() method? I'm guessing that is PlayerMovement.cs
Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class PlayerMovement : MonoBehaviour { public CharacterController2D controller; public float runSpeed = 40f; public Animator animator; float horizontalMove = 0f; bool jump = false; public Text livesText; // Manage your max and current lives [SerializeField] public int maxLives = 3; // settable through the inspector private static int currentLives; void Start() { // Create a temporary reference to the current scene. Scene currentScene = SceneManager.GetActiveScene(); // Retrieve the name of this scene. string sceneName = currentScene.name; if (sceneName == "Easy") { livesText.text = "Lives: " + maxLives; currentLives = maxLives; } else livesText.text = "Lives: " + currentLives; } // Update is called once per frame void Update() { horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed; animator.SetFloat("Speed", Mathf.Abs(horizontalMove)); if (PauseMenu.GameIsPaused) { jump = false; }else { if (Input.GetButtonDown("Jump")) { jump = true; animator.SetBool("IsJumping", true); } } } public void OnLanding() { animator.SetBool("IsJumping", false); } void FixedUpdate() { // Move our character controller.Move(horizontalMove * Time.fixedDeltaTime, false, jump); jump = false; } // You call this method when the player collides with something that kills him // or whenever anything else happens that kills him public void Die() { currentLives--; Debug.Log("die"); SetLivesText(); if (currentLives <= 0) { ResetPlayerState(); // trigger your gameover code } } // you call this to reset the player state, ie when the player is completely dead, // or when you enter the main menu.. or whatever is your condition for getting new full lives public void ResetPlayerState() { currentLives = maxLives; SceneManager.LoadScene("Menu Screen1"); // potentially some other code that happens when you want a fresh player state } void SetLivesText() { livesText.text = "Lives: " + currentLives; } }
private static int currentLives; is most likely the issue here. Try replacing it with: private int currentLives; Try doing that and then come back here to let us know if it worked. If it did I'll explain why.
From what I see your Die() is being called twice? Please paste in your OnTriggerEnter() from where you are calling Die()
Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Dead : MonoBehaviour { public GameMaster gameMaster; public GameObject player; void Start() { gameMaster = FindObjectOfType<GameMaster>(); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.tag == "Player") { gameMaster.RespawnPlayer(); other.gameObject.GetComponent<PlayerMovement>().Die(); } } }
All right, can you confirm, with 100% certainty, that your Die() method is being called once, and only once when the player dies? Use stack traces to see where it's being called from: https://forum.unity.com/threads/using-environment-stacktrace.2426/
after doing a bunch of testing I've notice that my OnTriggerEnter2D is calling the Die() function twice, but i don't have a clue why. This is the script that is calling the function twice. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Dead : MonoBehaviour { public GameMaster gameMaster; public GameObject player; void Start() { gameMaster = FindObjectOfType<GameMaster>(); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.tag == "Player") { gameMaster.RespawnPlayer(); other.gameObject.GetComponent<PlayerMovement>().Die(); } } }
Use the stack trace I mentioned. Stack trace enables you to see exactly where something is being called from. Is it possible that your player has 2 colliders? It would explain why this is happening. A simple fix to all of this is to have a boolean isPlayerDead. If the bool is true, don't call the Die() and don't respawn the player (since it's already happening). If it's false, then kill him and respawn him.
How exactly did you test this? Because i asked you to print a debug.log message in Die() and OnTriggerEnter2D() a couple posts above. The console log showed us 1x trigger and 1x die, with no errors. While this is not entirely failsafe, without an exception (which we would have seen in the console as well), this basically means the methods both executed once, which is the desired behavior. Now you're saying Die() gets executed twice.. so i gotta ask, how did you test it, or what changed? The thing is, since currentLives is private to the PlayerMovement class, only said class can change its value. The only place in PlayerMovement where we do this is Die(). So Die() is the only way currentLives can be changed, and it only decreases it once per call. So over the course of your game, currentLives should only decrease exactly by the amount of Die()-calls. If it only gets called in OnTriggerEnter2D(), which only gets called once per valid death, then it's not possible to lose two lives. So one of our assumptions / tests would have to be wrong. Of course it could also be a UI problem, where currentLives has the correct value but we show something different, but you update the UI by simply printing "Lives: {currentLives}" so that's not possible either. If, of course, Die() actually gets triggered twice per collision, that's a different story and should be rather easy to figure out and fix. However, if so, i dont understand why we would only have seen one debug.log message in the console screenshot you posted. By the way, what does RespawnPlayer() do?
The player does have two colliders Basically all I could say I was a tard and didn't realize that they were being called twice but it was only being displayed once. Respawn player is a function in my gamemaster which takes the player and puts him back at the start of the level
yea okay could you give an example, I am a tard when it comes to c#, this is for my college assignment
Isnt this the problem? If they both have the player tag, then you call Die() twice per 'one collision'.
I have two colliders on the player right, but they aren't separate from the player, I had a similar issue with my pickups in the game and someone suggested using a bool to detect whether the player is dead or not and it worked great, I have tried applying it to this situation but had no luck getting it to work as I am not the greatest at c#
Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Dead : MonoBehaviour { public GameMaster gameMaster; public GameObject player; void Start() { gameMaster = FindObjectOfType<GameMaster>(); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.tag == "Player") { PlayerMovement playerMovement = other.gameObject.GetComponent<PlayerMovement>(); if(!playerMovement.IsPlayerDead) { gameMaster.RespawnPlayer(); other.gameObject.GetComponent<PlayerMovement>().Die(); } } } } public class PlayerMovement : MonoBehaviour { public bool PlayerIsDead; // Your code public void Die() { PlayerIsDead = true; // your code } } This is a very very ugly solution. PlayerMovement should contain information about player's life. You should have a separate script called PlayerHP or something like that. That is something you'll have to figure out on your own.