Hi I have been struggling with the code for this lecture.
There are several ways to write code that does work. And I would like to see how other people solved it.
More importantly I would like to understand why my approach doesn’t work as I feel there is more important fundermental learning in that than actually solving the problem.
…
What I want the code to do.
To make the gameObject persist if the level is being reloaded - (with exception of if lose all lives on first level)
To make the gameObject not persist. instead being replaced by the new version of the gameObject if the scene being reloaded is the same
because the character is restarting and never progressed past the first level
To ensure that as soon as possible if on reload of the same level causes another gameObject to be created that the unwanted one (the new one) is destroyed
and the player does not see for a moment the gameObject children of both momentarily before one is destroyed.
The course code has the following problems in my game unity 2017.4.9f1
On loading a new level it destroys the new gameObject in the awake phase and then itself for not being the right object for the scene in the update
Resulting in no gameObject at all (and no coins) when you next lose a life the coins reappear
I think (I’ve changed lots) it may also keep the gameObject
How i would like to solve it if possible
I would like to solve it completely in the awake method so it is resolved at load and there is no threat of the player seeing items. And also so
I learn a little bit about the use of awake, start,update, fixupdate.
(If you’re reading because you just want to solve the issue I assume if you just use a find and destroy in the load next level and go back to first level methods. Or solve issues in update when scene has updated info
you may wish to disable some part of the gameObject so that they are invisible until you have resolved which you want. )
-> Mochant solved it here - About 'Remembering Pickups'!
My attempt
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ScenePersist : MonoBehaviour {
// -1 because I want to be able to search for ScenePersist (s) that have not yet been // assigned a sceneBuildindex number to them which happens in start
private int scenePersisting = -1;
void Awake(){
//array of all ScenePersist (s)
ScenePersist[] scenePersists = FindObjectsOfType<ScenePersist>();
foreach(ScenePersist scenePersist in scenePersists){
if((scenePersist.scenePersisting != -1) && (scenePersist.scenePersisting != SceneManager.GetActiveScene().buildIndex)) //destroy gameObjects that dont have this sceneBuild or -1
{
Debug.Log(" destroying in foreach ");
Destroy(scenePersist.gameObject);}}
Debug.Log(" scenePersist value " + scenePersisting + " I think this level build is (during the wake) " + SceneManager.GetActiveScene().buildIndex);
//having destroyed the gameObjects I dont want there should only be left the one made with this scene, and ones from previous loads of THIS scene
int numScenePersists = FindObjectsOfType<ScenePersist>().Length;
Debug.Log("number scenePersist = " + numScenePersists);
//if there is more than 1 ScenePersist then because this scenePersist is running the Awake method it must be the new one and should be destroyed
if (numScenePersists>1){Debug.Log("destroying in if >1"); Destroy(gameObject);}
//else{DontDestroyOnLoad(gameObject);}
//there should only be one ScenePersist that isnt destroyed by the time it reaches this point in the code which will be the first time a scenePersist is made for a particular scene
//it therefore should not be destroyed on load
DontDestroyOnLoad(gameObject);
}
void Start () {
//This means that the ScenePersist is now numbered to match its scene and so will be destroy on loading a different scene
//by other ScenePersist (s) in their awake function or it will survive if it is the same scene reloaded as they will destroy themselves
scenePersisting = SceneManager.GetActiveScene().buildIndex;
Debug.Log("in start scenePersisting is " + scenePersisting + "I think this level build is (during the wake) " + SceneManager.GetActiveScene().buildIndex);
}
void Update () {
}
}
My git (not done this before sorry if doesnt work) - https://github.com/phy5prt/G08TileVania2/blob/master/Assets/Scripts/ScenePersist.cs
(if you run it sorry about the camera I’ve been running it in scene view until i sort the cameras)
Observed behaviour
My code has the same issues as in the lecture on loading the next level it destroys the new ScenePersist because there is an existing one and the old one
because it does not match the scene.
My code does it because on line 27 (Debug.Log(…numScenePersists)) it reads 2 ScenePersist so destroys the one that is running the awake function which must therefore be the newest.
However the debug says it has destroyed a ScenePersist in the foreach, which must be the old one. And then destroys the new one in the awake as just mentioned.
So it seems I think?! not to register the other scenePersist was deleted as if it counted the number of them before the delete. However the debug has them delete
in the right order so this seems not the case.
Things I assume I understand and are right:
On reloading a scene if an object persists its awake and start are not rerun. So the singleton logic (is that what they called it) works because if
you are running OnAwake you as a gameObject have just been born, and if another of you exists, and one of you have previously existed then because you
are checking in the Awake and finding another you must be the newer version. (As when they ran awake they must of found no other version and so not deleted themselves).
private int scenePersisting = -1; - I believe I can use a private because I am trying to read scenePersisting of other ScenePersist (s) and that it is
private to the class, so if the same class is looking for it doesnt have to be public.
My double not statement seems to be okay.
Ideas why I think it may not work
The code doesn’t execute in order so the number of persistent objects is higher that it should be because no destroys have run.
https://docs.unity3d.com/Manual/ExecutionOrder.html
From reading what other people have found on awake the game doesn’t know its scene build number again due to execution order maybe?
If I create an array of existing gameObject in awake, might it miss gameObjects because this gameObjects Awake() method has been called first? so
the other objects don’t exist yet?
My debug statements seem to occur in the right order but don’t have the information i expect so I am not sure if this means the code is executed top to bottom or not.
Went back to beginning of the course to see how you are supposed to do this as the discussion so far has always covered everything I needed without me asking up to this week.
Thanks so much in advance I’m hoping to get a better understanding by solving this through the awake with help Im given or just by recognising why my solution fails.
Cheers Phil