Search Unity

Respawning issue

Discussion in 'Scripting' started by HarryWhitelegg02, Mar 24, 2020.

  1. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    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.
     
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    You'll need to show some code.
     
  3. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Code (CSharp):
    1.     // You call this method when the player collides with something that kills him
    2.     // or whenever anything else happens that kills him
    3. This is the function that kills the player
    4.     public void Die()
    5.     {
    6.  
    7.         currentLives--;
    8.         Debug.Log($"dead at {Time.time}");
    9.         SetLivesText();
    10.         if (currentLives <= 0)
    11.         {
    12.  
    13.             ResetPlayerState();
    14.             // trigger your gameover code
    15.         }
    16.  
    17.  
    18.     }
    This is a separate script attached to the collider that if the player touches calls the Die void.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Dead : MonoBehaviour
    6. {
    7.     public GameMaster gameMaster;
    8.     public GameObject player;
    9.  
    10.  
    11.     void Start()
    12.     {
    13.         gameMaster = FindObjectOfType<GameMaster>();
    14.     }
    15.  
    16.     void OnTriggerEnter2D(Collider2D other)
    17.     {
    18.         if (other.gameObject.tag == "Player")
    19.         {
    20.  
    21.  
    22.                 gameMaster.RespawnPlayer();
    23.  
    24.                 other.gameObject.GetComponent<PlayerMovement>().Die();
    25.  
    26.         }
    27.  
    28.     }
    29. }
     
  4. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    1. First check if Die() is the only place to adjust currentLives.
    2. Afterwards check if OnTriggerEnter is the only place to call Die().
    3. 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.
     
  5. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    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
    , upload_2020-3-24_15-5-23.png
    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.
     
  6. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    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?
     
  7. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    upload_2020-3-24_15-36-11.png
    This screenshot shows the Debug.log's only being printed once but the player has lost 2 lives when they should have lost one.
     
  8. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    Please show the function which updates lives to the GUI.
     
  9. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Code (CSharp):
    1.     void SetLivesText()
    2.     {
    3.         livesText.text = "Lives: " + currentLives;
    4.     }
    This works with the void Die funtion
     
  10. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Can you just show the entire script which includes the Die() method? I'm guessing that is PlayerMovement.cs
     
  11. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.SceneManagement;
    6.  
    7. public class PlayerMovement : MonoBehaviour
    8. {
    9.     public CharacterController2D controller;
    10.     public float runSpeed = 40f;
    11.     public Animator animator;
    12.     float horizontalMove = 0f;
    13.     bool jump = false;
    14.     public Text livesText;
    15. // Manage your max and current lives
    16.     [SerializeField] public int maxLives = 3; // settable through the inspector
    17.     private static int currentLives;
    18.  
    19.     void Start()
    20.     {
    21.         // Create a temporary reference to the current scene.
    22.         Scene currentScene = SceneManager.GetActiveScene();
    23.         // Retrieve the name of this scene.
    24.         string sceneName = currentScene.name;
    25.  
    26.         if (sceneName == "Easy")
    27.         {
    28.             livesText.text = "Lives: " + maxLives;
    29.             currentLives = maxLives;
    30.         }
    31.         else livesText.text = "Lives: " + currentLives;
    32.     }
    33.     // Update is called once per frame
    34.     void Update()
    35.     {
    36.         horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
    37.  
    38.         animator.SetFloat("Speed", Mathf.Abs(horizontalMove));
    39.         if (PauseMenu.GameIsPaused)
    40.         {
    41.             jump = false;
    42.         }else
    43.         {
    44.             if (Input.GetButtonDown("Jump"))
    45.         {
    46.             jump = true;
    47.             animator.SetBool("IsJumping", true);
    48.         }
    49.  
    50.         }
    51.  
    52.     }
    53.     public void OnLanding()
    54.     {
    55.         animator.SetBool("IsJumping", false);
    56.     }
    57.     void FixedUpdate()
    58.     {
    59.         // Move our character
    60.         controller.Move(horizontalMove * Time.fixedDeltaTime, false, jump);
    61.         jump = false;
    62.     }
    63.     // You call this method when the player collides with something that kills him
    64.     // or whenever anything else happens that kills him
    65.     public void Die()
    66.     {
    67.         currentLives--;
    68.         Debug.Log("die");
    69.         SetLivesText();
    70.        
    71.         if (currentLives <= 0)
    72.         {
    73.             ResetPlayerState();
    74.             // trigger your gameover code
    75.         }    
    76.     }
    77.     // you call this to reset the player state, ie when the player is completely dead,
    78.     // or when you enter the main menu.. or whatever is your condition for getting new full lives
    79.     public void ResetPlayerState()
    80.     {
    81.         currentLives = maxLives;
    82.         SceneManager.LoadScene("Menu Screen1");
    83.         // potentially some other code that happens when you want a fresh player state
    84.     }
    85.     void SetLivesText()
    86.     {
    87.         livesText.text = "Lives: " + currentLives;
    88.     }
    89. }
     
  12. Dextozz

    Dextozz

    Joined:
    Apr 8, 2018
    Posts:
    493
     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.
     
  13. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Nope didn't work lol.
     
  14. Dextozz

    Dextozz

    Joined:
    Apr 8, 2018
    Posts:
    493
    From what I see your Die() is being called twice? Please paste in your OnTriggerEnter() from where you are calling Die()
     
  15. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Dead : MonoBehaviour
    5. {
    6.     public GameMaster gameMaster;
    7.     public GameObject player;
    8.     void Start()
    9.     {
    10.         gameMaster = FindObjectOfType<GameMaster>();
    11.     }
    12.     void OnTriggerEnter2D(Collider2D other)
    13.     {
    14.         if (other.gameObject.tag == "Player")
    15.         {
    16.                 gameMaster.RespawnPlayer();
    17.                 other.gameObject.GetComponent<PlayerMovement>().Die();
    18.         }
    19.     }
    20. }
     
  16. Dextozz

    Dextozz

    Joined:
    Apr 8, 2018
    Posts:
    493
  17. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    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):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Dead : MonoBehaviour
    5. {
    6.     public GameMaster gameMaster;
    7.     public GameObject player;
    8.     void Start()
    9.     {
    10.         gameMaster = FindObjectOfType<GameMaster>();
    11.     }
    12.     void OnTriggerEnter2D(Collider2D other)
    13.     {
    14.         if (other.gameObject.tag == "Player")
    15.         {
    16.                 gameMaster.RespawnPlayer();
    17.                 other.gameObject.GetComponent<PlayerMovement>().Die();
    18.         }
    19.     }
    20. }
     
  18. Dextozz

    Dextozz

    Joined:
    Apr 8, 2018
    Posts:
    493
    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.
     
  19. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    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?
     
  20. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    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. upload_2020-3-28_20-5-54.png
    Respawn player is a function in my gamemaster which takes the player and puts him back at the start of the level
     
  21. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    yea okay could you give an example, I am a tard when it comes to c#, this is for my college assignment
     
  22. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    Isnt this the problem? If they both have the player tag, then you call Die() twice per 'one collision'.
     
  23. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    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#
     
  24. HarryWhitelegg02

    HarryWhitelegg02

    Joined:
    Mar 11, 2020
    Posts:
    39
    Yea he does, could you please give an example of some code that could solve the issue.
     
  25. Dextozz

    Dextozz

    Joined:
    Apr 8, 2018
    Posts:
    493
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Dead : MonoBehaviour
    5. {
    6.     public GameMaster gameMaster;
    7.     public GameObject player;
    8.     void Start()
    9.     {
    10.         gameMaster = FindObjectOfType<GameMaster>();
    11.     }
    12.     void OnTriggerEnter2D(Collider2D other)
    13.     {
    14.         if (other.gameObject.tag == "Player")
    15.         {
    16.                 PlayerMovement playerMovement = other.gameObject.GetComponent<PlayerMovement>();
    17.                
    18.                 if(!playerMovement.IsPlayerDead)
    19.                 {
    20.                     gameMaster.RespawnPlayer();
    21.                     other.gameObject.GetComponent<PlayerMovement>().Die();
    22.                 }
    23.         }
    24.     }
    25. }
    26.  
    27. public class PlayerMovement : MonoBehaviour
    28. {
    29.     public bool PlayerIsDead;
    30.    
    31.     // Your code
    32.    
    33.     public void Die()
    34.     {
    35.        PlayerIsDead = true;
    36.        // your code
    37.     }
    38. }
    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.
     
    HarryWhitelegg02 likes this.