At the second and third level, the Player doesn't lose lives

At the second and third level, the Player doesn’t lose lives. At the Inspector, for GameSession, I can see Player Lives reducing from 3 to 2 at the first level, but at the second, after death, the Player Lives is still 2. I added a debug at the ProcessPlayerDeath() and TakeLife() methods. The Player Lives always looks to reset to 3, after death.
YouTube - Example

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;

public class GameSession : MonoBehaviour
{
    [SerializeField] int playerLives = 3;
    [SerializeField] int score = 0;
    [SerializeField] TextMeshProUGUI livesText;
    [SerializeField] TextMeshProUGUI scoreText;    
    void Awake()
    {
        int numGameSessions = FindObjectsOfType<GameSession>().Length;
        if (numGameSessions > 1)
        {
            Destroy(gameObject);
            gameObject.SetActive(false);
        }
        else
        {
            DontDestroyOnLoad(gameObject);
        }
         Debug.Log("PlayerLives Awake: " + playerLives);
    }
    private void Start() 
    {
        livesText.text = playerLives.ToString();
        scoreText.text = score.ToString();
    }
    public void ProcessPlayerDeath()
    {
        if (playerLives > 1)
        {
            Debug.Log("PlayerLives 0: " + playerLives);
            TakeLife();
        }
        else
        {
            ResetGameSession();
        }
    }
     public void AddToScore(int pointsToAdd)
    {
        score += pointsToAdd;
        scoreText.text = score.ToString();
    }
    void TakeLife()
    {
        Debug.Log("PlayerLives 1: " + playerLives);
        playerLives--;
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
        livesText.text = playerLives.ToString();
        Debug.Log("PlayerLives 2: " + playerLives);
    }   
    void ResetGameSession()
    {
        FindObjectOfType<ScenePersist>().ResetScenePersist();
        SceneManager.LoadScene(0);
        Destroy(gameObject);
        gameObject.SetActive(false);
    }
}

Hi,

Please note, it’s better to copy/paste your code and apply the code fencing characters, rather than using screenshots. Screenshots are ideal for displaying specific details from within a game engine editor or even error messages, but for code, they tend to be less readable, especially on mobile devices which can require extensive zooming and scrolling.

You also prevent those that may offer to help you the ability to copy/paste part of your code back to you with suggestions and/or corrections, meaning that they would need to type a potentially lengthy response. You will often find that people are more likely to respond to your questions if you make it as easy as possible for them to do so.

Check if your “singleton” calls gameObject.SetActive(false); in the same if-block where Destroy(gameObject); gets called. If there isn’t that line of code, add it.

Hope this helps :slight_smile:


See also;

Thank you for your reply.
I added the code to the singleton, but still, the player is immortal after level 1 :neutral_face:

(Following your instruction, I edited the post replacing the screen capture by the code itself).

Thank you. That’s helpful. Now I’m able to copy and paste your code. :slight_smile:

I’m wondering if the issue might be caused by this line:
FindObjectOfType<ScenePersist>().ResetScenePersist();

Is the GameSession object part of a game object which has got the ScenePersist component attached?

In your Debug.Logs, try to add GetInstanceID() like this:
Debug.Log(GetInstanceID() + " --- PlayerLives Awake: " + playerLives);

Maybe your player is not immortal but a new GameSession object gets created. The same instance id means the same object. If you get different ids, there are either two GameSession objects in your scene, or the “singleton” destroys the wrong one, or something else destroys the GameSession object. That’s impossible to tell just by looking at the code, so try to figure out what’s going on during runtime. :slight_smile:

Nina, you are right about the IDs, they change (video here: youtube example).

I remembered that the instructor, Rick, provided de code at GitLab. I replaced my code for his and the problem remains.
I verified all GameSession objects for each of the three Levels and they look ok. No Overrides, so they are all the same.

I’m afraid the link to your new video does not work. The URL is missing. :frowning:

How many “singletons” classes do you have in your project? By “singleton”, I’m referring to classes calling DontDestroyOnLoad(gameObject);.

In Awake(), log Time.frameCount, the instance id and the classname into your console, and pass on gameObject as the second parameter of the Debug.Log method. Like this:
Debug.Log("your string", gameObject);

You could copy and paste your Debug.Log to the OnDestroy() method, which you declare somewhere in your “singleton” class. That’s a Unity method like Awake but it gets called when the object gets destroyed.

This way, you will be able to figure out how many “singleton” objects exist in your scene and when they get destroyed. Maybe the order of the messages will help you figure out what might have gone wrong. When clicking on one of the message, the corresponding game object (given it still exists) gets highlighted in the Hierarchy.

I fixed the link for the previous video.
This is the new video with the code you sugested (if I understood correctly): https://youtu.be/xUJMIBH8bck

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;

public class GameSession : MonoBehaviour
{
    [SerializeField] int playerLives = 3;
    [SerializeField] int score = 0;
    [SerializeField] TextMeshProUGUI livesText;
    [SerializeField] TextMeshProUGUI scoreText;    

    void Awake()
    {
        Debug.Log("-- GameSession.Awake - Time Frame: " + Time.frameCount + " | ID: " + GetInstanceID() , gameObject);
        int numGameSessions = FindObjectsOfType<GameSession>().Length;
        if (numGameSessions > 1)
        {
            Debug.Log("-- GameSession.Awake.Destroy - Time Frame: " + Time.frameCount + " | ID: " + GetInstanceID() , gameObject);
            Destroy(gameObject);
            gameObject.SetActive(false);
        }
        else
        {
            DontDestroyOnLoad(gameObject);
        }
        // Debug.Log(GetInstanceID() + " --- PlayerLives Awake: " + playerLives);
    }

    private void Start() 
    {
        livesText.text = playerLives.ToString();
        scoreText.text = score.ToString();
    }
    public void ProcessPlayerDeath()
    {
        if (playerLives > 1)
        {
            //Debug.Log(GetInstanceID() + " --- PlayerLives 0: " + playerLives);
            TakeLife();
        }
        else
        {
            ResetGameSession();
        }
    }
     public void AddToScore(int pointsToAdd)
    {
        score += pointsToAdd;
        scoreText.text = score.ToString();
    }
    void TakeLife()
    {
        Debug.Log("--Before TakeLife - Time Frame: " + Time.frameCount + " | ID: " + GetInstanceID() + " --- PlayerLives 1: " + playerLives); 
        playerLives--;
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
        livesText.text = playerLives.ToString();
        Debug.Log("--After TakeLife - Time Frame:  " + Time.frameCount + " | ID: " + GetInstanceID() + " --- PlayerLives 2: " + playerLives); 
    }   
    void ResetGameSession()
    {
        FindObjectOfType<ScenePersist>().ResetScenePersist();
        SceneManager.LoadScene(0);
        Destroy(gameObject);
        gameObject.SetActive(false);
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScenePersist : MonoBehaviour
{
    void Awake()
    {
        Debug.Log("-- ScenePersist.Awake - Time Frame: "  + Time.frameCount + " | ID: " + GetInstanceID() , gameObject);
        int numScenePersists = FindObjectsOfType<ScenePersist>().Length;
        if (numScenePersists > 1)
        {
            Debug.Log("-- ScenePersist.Awake.Destroy - Time Frame: " + Time.frameCount + " | ID: " + GetInstanceID() , gameObject);
            Destroy(gameObject);
            gameObject.SetActive(false);
        }
        else
        {
            DontDestroyOnLoad(gameObject);
        }
    }

    public void ResetScenePersist()
    {
        Destroy(gameObject);
    }
}

Thank you, that’s helpful. :slight_smile:

Unfortunately, I’m just missing the output of the OnDestroy method, so it is impossible to tell if something has been destroyed; or rather: if the object was part of a “destroy” event.

What I learnt from your video:

The id of the persistent GameSession (frame 0) is: -2405
The id of the persistent ScenePersist (frame 0) is: -2400

At around mark 0:08, you lose. The level gets reloaded, -2405 loses a life. The new GameSession and ScenePersist objects get created and destroyed. That was expected.

At 0:20, you reach the goal, the next scene gets loaded. The id of the new persistent ScenePersist (frame 5813) is: 27448. So far, so good.

Interestingly, we have one more ScenePersist object and two GameSession objects which get destroyed in this level:

image

At the 0:24 mark, the player dies. We see this output in the console:

image

The TakeLife method is part of the GameSession object. The id of the GameSession object is -27622, which is the object that was supposed to be destroyed in frame 5813. See here:

image

That’s why I asked to use the OnDestroy method because the question is now: Is this a new GameSession object with the name id or is it the GameSession object which was supposed to get destroyed? While we cannot be 100% sure, the OnDestroy method could have told us that the “destroy” event was indeed performed on the aforementioned GameSession object.

So yeah, it seems we found the problem but, at the moment, I have no clue why the GameSession object that was supposed to get destroy did not get destroyed (given Unity did not create a new GameSession object with the same id.)

Another thing you could try to figure out: Does the initial GameSession object still exist or did it get destroyed at some point? In the case of the recorded game session, it would have been the one with id -2405.

Just throwing a thought out there since I can’t see anything calling TakeLife()…

If you have another object looking for GameSession in its Awake(), you might have a race condition where it’s caching the wrong GameSession before it’s had a chance to do the Awake() in GameSession. That way, it tries to run ProcessPlayerDeath() on the wrong object and might not be throwing an error if you’ve done a null check on that part of the code.

2 Likes

Thank You @MichaelP ! My Player script was looking for “GameSession” at “Awake”. I just moved that call to the moment when the player dies. Problem is solved.
Thank You @Nina for your help.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms