DontDestroyOnLoad

Hi,
I wanted to experiment with a code that I have learnt from online (click here for video). On it’s own, it works well. However, I want to add an element where if you take damage, it will reset the level AND keep data of how many lives have been lost. (Currently, the script will reset your life count when the level is reset)

Damage Code:

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

public class Damage : MonoBehaviour
{
    public GameObject deathEffect, playerHealth;

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Player") //This ensures that you only lose health when the enemy hits the player.
        {
            GameControl3L.health -= 1; //Loads the separate script (which works well) and will remove 1 life when you collide with the enemy.
            SceneManager.LoadScene("SetUp");
            DontDestroyOnLoad(playerHealth);
            //Add new mechanic here to restart the level and keep all health remaining.
        }
    }
}

FYI - Here is the other code that I have used along with this:

Game Control Code

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

public class GameControl3L : MonoBehaviour
{
    public GameObject heart1, heart2, heart3, heart4, gameOver;



    void Start()
    {
        health = 4;
        heart1.gameObject.SetActive (true);
        heart2.gameObject.SetActive (true);
        heart3.gameObject.SetActive (true);
        heart4.gameObject.SetActive (true);
        gameOver.SetActive (false);
    }

    void Update()
    {
        if (health > 4)
        {
            health = 4;
        }

        switch (health)
        {
            case 4: //When no damage is taken, lose no health.
                heart1.gameObject.SetActive (true);
                heart2.gameObject.SetActive (true);
                heart3.gameObject.SetActive (true);
                heart4.gameObject.SetActive (true);
                break;
            case 3: //for every health lost, remove 1 heart.
                heart1.gameObject.SetActive (true);
                heart2.gameObject.SetActive (true);
                heart3.gameObject.SetActive (true);
                heart4.gameObject.SetActive (false);
                break;
            case 2:
                heart1.gameObject.SetActive (true);
                heart2.gameObject.SetActive (true);
                heart3.gameObject.SetActive (false);
                heart4.gameObject.SetActive (false);
                break;
            case 1:
                heart1.gameObject.SetActive (true);
                heart2.gameObject.SetActive (false);
                heart3.gameObject.SetActive (false);
                heart4.gameObject.SetActive (false);
                break;
            default: //also shown if 0 hearts are showing (case 0).
                heart1.gameObject.SetActive (false);
                heart2.gameObject.SetActive (false);
                heart3.gameObject.SetActive (false);
                heart4.gameObject.SetActive (false);
                gameOver.gameObject.SetActive (true);
                SceneManager.LoadScene("Skills - GameOver");
                break;
        } //Adjustments - *Keep current live count after restart.
        //*E.g. You lost 1 life, after restart, you have 3 lives (NOT 4 lives).

    }
 
}

I have tired using the ‘DontDestroyOnLoad’ sequence but this doesn’t work for me. Same goes if I use a sperate game object containing the script. Am I doing something wrong? Or is there another way to keep all remain health after restarting the level?

Thanks in advance.

The 2D Experimental Preview forums are not for you posting your experimental code. :slight_smile:

Your code is simply about scripting so it should be posted on the scripting forum here.

I’ll move your post for you.

You should use a GameManager style approach, which explicitly does DontDestroyOnLoad() in its initialization (Awake, Start, etc.), not conditionally when you happen to get a collision.

Here’s some notes on GameManagers, various ways to approach it:

ULTRA-simple static solution to a GameManager:

https://discussions.unity.com/t/855136/6

https://gist.github.com/kurtdekker/50faa0d78cd978375b2fe465d55b282b

OR for a more-complex “lives as a MonoBehaviour” solution…

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

1 Like

Sorry for the long response. Thank you for giving me this answer. However, my game is a 2D game and when I tried to use the GM script, it didn’t work. I was able to copy and paste the exact script. However, I had a separate code for scoring and therefore removed the score system from the GM script. This is also because I am specifically looking for number of lives, not necessarily the score in this case.
If you have a sperate lives script for the GM, would it be possible for me to use it to see if this works? (Only asking as I noticed you had a separate script for keeping the score).

Thanks Kurt-Dekker and I hope to hear from you soon.

[EDIT]
To be more precise on the issue, there are no error codes showing in the console for the specific script. The code below often says it is an Abstract script if I put it in an empty game object (I sometimes do this so that another script has a reference as to where specific values come from):

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

public static class GM
    {
        public static int Lives;

        public static void InitGame()
        {
            Lives = 4;
        }
    }

As I said, I have a different code for keeping score, so that has been removed from the above code. To me, the game manager currently has no effect. (when the level restarts, it still resets health although I want it to keep track of how many lives have been lost) I am unsure if this is because I need to use the code ‘GM.Lives -= 1’ when I take damage/fall down, or if there would be a separate Lives script to enhance the GM.

Sorry for the long response. Thanks for moving the thread MelvMay.

You would certainly hear a lot more, because I know that script absolutely works and cares NOTHING about 2D, 3D or even 257D… it is just a MonoBehaviour lifecycle control approach that I always use.

When you write this response, nobody here can help you further:

“It didn’t work” is really just an empty statement when it comes to troubleshooting.

How to report your problem productively in the Unity3D forums:

http://plbm.com/?p=220

You can edit your post above.

If you really have no idea what is going on, here is how to change that by instrumenting your code.

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

If your problem would benefit from in-scene or in-game visualization, consider using Debug.DrawRay() or Debug.DrawLine() to visualize things like raycasts or distances.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

Note that when you use DontDestroyOnLoad, the GameObject needs to be at the root of the scene. If it is a child of another GameObject it won’t work.

1 Like