0

I made a pretty basic 2D game to learn. I have 2 Scenes, and switching between them worked great. I used empty gameObjects as Start/Exit point of the Scene, so that the game would know to put player on point X after exiting through point X (for example exit outside house if I walk out the door).

Then I added a "Scene0", to use for persistent general scripts like GameManager, Sounds, Music, etc. With just one object called "Controller" that I DontDestroyOnLoad(). After adding this Scene and then just switching Scenes right away to my MainScene, all of a sudden the game starts acting really strange;

the first time I move from my MainScene (Scene1), to my secondary Scene (Scene2), it works fine, but then when I leave Scene2 to go back to Scene1, the player spawns in the middle of nowhere. And this ONLY happens if I launch the game from my Persistent Scene.

I have no idea what is wrong, I don't add anything that interferes with my scene transitions, all I've added so far is playerHealth, for testing.

Scripts attached to my (persistent) Controller:

DDOL:

public class DDOL : MonoBehaviour {

    // Use this for initialization
    void Awake () {
        DontDestroyOnLoad (gameObject);
    }

}

GameManager:

public class GameManager : MonoBehaviour {

    public static GameManager manager;

    public int playerMaxHealth;
    public int playerCurrentHealth;

    void Awake(){
        if (manager == null) {
            manager = this;
        } else if (manager != this) {
            Destroy (gameObject);
        }
    }

    // Use this for initialization
    void Start () {
        SceneManager.LoadScene("test_scene");
    }

    // Update is called once per frame
    void Update () {

    }
}

Scripts attached to my StartPoint:

PlayerStartPoint:

public class PlayerStartPoint : MonoBehaviour {

    private PlayerController thePlayer;
    private CameraController theCamera;

    public Vector2 startDir;

    public string pointName;

    // Use this for initialization
    void Start () {
        thePlayer = FindObjectOfType<PlayerController> ();

        if (thePlayer.startPoint == pointName) {
            thePlayer.transform.position = transform.position;
            thePlayer.lastMove = startDir;

            theCamera = FindObjectOfType<CameraController> ();
            theCamera.transform.position = new Vector3(transform.position.x, transform.position.y, theCamera.transform.position.z);
        }
    }
}

And finally ExitPoint:

LoadNewArea:

public class LoadNewArea : MonoBehaviour {

    public string levelToLoad;
    public string exitPoint;
    private PlayerController thePlayer;

    // Use this for initialization
    void Start () {
        thePlayer = FindObjectOfType<PlayerController> ();
    }

    void OnTriggerEnter2D(Collider2D other){
        if (other.gameObject.name == "Player") 
        {
            SceneManager.LoadScene(levelToLoad);
            thePlayer.startPoint = exitPoint;
        }
    }
}

EDIT: After moving all my DDOL gameObject to the Pre-Scene, it worked. So, I can assume the fault is inside Player or Cameras Start() functions since when they start in Scene1 they get called every time I enter the Scene (only DDOL). I tried adjusting their Start()functions like follows:

Original camera:

void Start () {
        Debug.Log("Starting camera");
        if (!cameraExists) {
            cameraExists = true;
            DontDestroyOnLoad (gameObject);}
           else{
        Destroy (gameObject);
        }
    }

Changed Camera:

void Start () {
            DontDestroyOnLoad (gameObject);
        }

The exact same changes was made in Player. Obviously this doesnt work because it creates a new Camera/Player every time I enter Scene1 (btw why does it not try to create them when I enter Scene2?, is it because they start in Scene1?). HOWEVER, the new player/camera do start at the correct position, and if I zoom out I can see the old player/camera at that same wrong position as before. So something weird happens when their Start() is called a second time it seems.

Green_qaue
  • 3,561
  • 11
  • 47
  • 89
  • It shouldn't make a difference, but it might be better to set `thePlayer.startPoint = exitPoint;` **before** doing `SceneManager.LoadScene(levelToLoad);`, just to be absolutely sure. – Thomas Hilbert Jun 01 '17 at 06:24
  • @Fattie the player is marked DDOL, should hav mentioned that. just confused why it works without a pre-load scene – Green_qaue Jun 01 '17 at 06:56
  • note that the code fragment directly under "Original camera:" is **totally and completely wrong**, heh. You're just totally mixed-up there my man. – Fattie Jun 01 '17 at 10:25
  • in a sense @Green_qaue the simplest possible answer to your question is that you're **having a problem between Awake and Start**. – Fattie Jun 01 '17 at 11:22

3 Answers3

3

You've now mentioned that you had code something like this,

void Start () {
        Debug.Log("Starting camera");
        if (!cameraExists) {
            cameraExists = true;
            DontDestroyOnLoad (gameObject);}
           else{
        Destroy (gameObject);
        }
    }

Note that this is unfortunately just "utterly incorrect", heh :)

The issues you mention in the question (preload scenes etc) are just totally unrelated to the problem here.


In Unity if you have a character C that persists between scenes a, b, c as you load those scenes, you must kick-off C in it's own (perhaps otherwise empty) scene, you can not use "a" as a matter of convenience to kick off C.

The pattern is, in each of a, b, c just have a line of code like p = FindObjectOfType<Player>(); which runs when the scene loads, and position C as you wish.


Now, regarding your specific puzzle about the unusual behavior you are seeing.

I understand that you want to know why you are observing what you do.

It is a combination of confusion over the following issues: 1 - difference between Awake and Start, 2 - confusion over script execution order {but see below1} 3 - confusion about Destroy versus DestroyImmediate 4 - Not using Debug.Log enough, and not using gameObject.name in there (it's a common in Unity to be wildly confused about which object is talking in Debug.Log) 5 - where you mention you see the other object "off to the side", it's common to drastically confuse which one is which in such situations 6 - confusion between the computer programming concept of "instantiation" (ie, of a class or object) and "instantiating" (confusingly, it's the same word - utterly unrelated) game objects in nity.

If you fiddle around with all those issues, you'll discover an explanation for the behavior you're seeing!

But it doesn't amount to much; in Unity in the "C .. a b c" example you have to create C separately beforehand.

1 {aside, never fiddle with the script execution ordering system in Unity in an effort to solve problems; it's only there for R&D purposes; however it could in fact help you investigate the behavior at hand in this problem, if you are particularly keen to fully understand why you're seeing what you're apparently seeing}

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • thanks for trying to help. It seems what it boils down to is that something is happening differently when I redirect to my MainScene compared to when I just start the game in my MainScene. Because all I add in PreScene is an empty script with loadScene(). Is there anything you know of that could cause this? – Green_qaue Jun 01 '17 at 07:36
  • Hi @Green_qaue. I completely understand what you mean; there ***seems to you*** to be some difference. Honestly, there's just some mix up (totally unrelated to the "preload scene", if you will.) If you like, just zip up the project and post it and someone will have a look. – Fattie Jun 01 '17 at 10:24
  • Hey, thanks. Could you explain what is wrong with that code snippet? Since I followed a guide when making it. – Green_qaue Jun 01 '17 at 10:46
  • 1
    hi, the short story is, as I said instantly on seeing this question: say you have the sort of thing where you have a character, C,which is DDOL, and the scenes change "around" C. so, "C" moves from room 1, 2, 3, 1, 3, 2, 1, 3 etc, and you do that by changing scenes (but keeping C in place). **When you do that in Unity, you must begin C in it's own scene, which you *never return to* and then load 1, 2, 3 etc.** I don't know how clear I can be, that's just a rule in Unity. So you have to do that. {All of that totally unrelated to the "preload scene for sound effects", which of course you.. – Fattie Jun 01 '17 at 11:06
  • .. have to do in Unity. Scenes in Unity are like variables, it's fine to have zillions of scenes.} – Fattie Jun 01 '17 at 11:07
  • Regarding your *specific puzzle*,... actually I will put this in the answer OK since it's long. @Green_qaue – Fattie Jun 01 '17 at 11:08
1

Use the debugger. Have breakpoints at the relevant spots, like PlayerStartPoint.Start() and LoadNewArea.OnTriggerEnter2D() and check that they are executed

  • At the right time
  • The right number of times
  • With the expected values

This should make you see where things get out of hand.

If you use Visual Studio, install https://marketplace.visualstudio.com/items?itemName=SebastienLebreton.VisualStudio2015ToolsforUnity to be able to debug Unity from within Visual Studio. If you are not using Visual Studio, you probably should.

Thomas Hilbert
  • 3,559
  • 2
  • 13
  • 33
  • If I debug it behaves exactly as I want it to. It finds the correct startpoint given the exitpoint. But I guess something i breaking somewhere. I just have to find it. So basically, if I start from my main scene, it work, if I start from scene0, it doesnt work. You dont happen to know any behaviours that only trigger when us tart in the scene? – Green_qaue Jun 01 '17 at 07:25
  • I tried finding a solution to your problem in my head, but wasn't able to find anything but "I should debug that", so that's what I suggested ^^ If everything executes just as you expect it to, but the result is not what you want, then there is something wrong with your approach. – Thomas Hilbert Jun 01 '17 at 07:56
  • Im on my way I think ^^ . If I move my DDOL objects (player and camera) to the pre-scene, then it works as it should. So there is something in there that only works if they exist in the scene I start from. Not sure what yet tho :) – Green_qaue Jun 01 '17 at 08:01
  • Not sure if relevant, but the `Start()`-functions in Camera and Player only run when I enter Scene1 (mainScene), not when I enter Scene2(house). – Green_qaue Jun 01 '17 at 08:09
  • Start() will never execute twice on the same object. As I commented on Fiffe's answer, Start() only executes right before the first time Update() is called on an object. If you do anything relevant in there, that you need to execute after scene transitions, go with Fiffe's suggestion of OnLevelWasLoaded() – Thomas Hilbert Jun 01 '17 at 08:25
  • But if I Debug.Log their start functions, I can see that Start() gets called every time I enter Scene1 (The scene they are first created in). Please see my edit. – Green_qaue Jun 01 '17 at 08:27
  • That is because there is a new object created every time you load a scene which contains that object. Unity does not care that you don't destroy them afterwards. You should have DDOL objects in the PreScene only, or at least make sure you immediately destroy new, unwanted instances BEFORE they interfere with your logic. – Thomas Hilbert Jun 01 '17 at 08:30
  • I see, that makes sense. Seems like I might have the wrong approach then. Probably shouldnt need DDOL on my Camera/Player then? Or is that normal? to put them in the pre-scene I mean. Just not sure how else I will keep the Camera/Player between scenes, for example I want to know what direction the player had when he exited a scene, so I can spawn him in that direction. Isn't it weird to destroy/re-create the Camera/Player every time I change scenes? – Green_qaue Jun 01 '17 at 08:31
  • It is common in Unity development, though I'd rather suggest not using DDOL except where absolutely necessary, like on a NetworkManager or similar that has to keep connection alive through state transitions. Better to just throw everything away on scene transitions and keep state in data structures that don't depend on GameObjects. Much easier to understand. – Thomas Hilbert Jun 01 '17 at 08:35
  • Okay, like My GameManager for example. That handles states/save/load data. Its attached to an empty object and is created in Scene0. So really I could put the last direction of my player in there instead I guess. Just seems un-natural. Maybe cus im coming from working on OOP Java, things are more different than I thought in Unity – Green_qaue Jun 01 '17 at 08:38
  • There's a lot of confusion here ! – Fattie Jun 01 '17 at 10:21
  • @Fattie And so were you probably when you started out :) Just trying to learn m8. – Green_qaue Jun 01 '17 at 10:43
  • Firstly, never use a debugger, ever, in Unity or any ECS system as it is utterly, totally, irrelevant and doesn't work. Secondly, simply use "Debug.Log" to solve and and all such simple errors as the one under discussion here in Unity, as the one discussed here. Thirdly as mentioned in my answer, the issue at hand here is just utterly unrelated to the preload scene / etc issue! – Fattie Jun 01 '17 at 10:44
  • notice this somewhat wordy post https://stackoverflow.com/a/37243035/294884 for example – Fattie Jun 01 '17 at 10:47
  • "Firstly, never use a debugger, ever, in Unity" You can't be serious. If it doesn't work for you, you're doing something wrong, but using a debugger is simply one of the most essential things in software development ever! – Thomas Hilbert Jun 01 '17 at 10:48
  • C# code works in Unity just as it does anywhere else. It's not the code that's different, it peoples' expectations. Outside Unity everyone knows to store their data in data structures, but as soon as they are in Unity they think they have to store everything in GameObjects and somehow try to keep them alive over scene transitions. – Thomas Hilbert Jun 01 '17 at 10:51
  • Hi @ThomasHilbert, right, I do a lot of software development. I also work with Unity, which has no connection, at all, to software development. Unity is an ECS system - it's like using photoshop or say microsoft word. – Fattie Jun 01 '17 at 10:53
  • I don't see where you're trying to go. We are on stackoverflow to discuss programming issues. The topic is about a problem caused by invalid programming logic, and we are currently debugging this programming issue using a debugger and will finally solve the problem by changing code. Yes there is also an Editor involved that changes some serialized resource data around, but that's true for nearly any software project out there. – Thomas Hilbert Jun 01 '17 at 11:02
-2

Is player persistent between scenes (does he have DontDestroyOnLoad)? If no then this might be the reason - you can either try loading the scenes by using the additive mode or by instantiating the player on scene load in correct position.

Fiffe
  • 1,196
  • 2
  • 13
  • 23
  • Yes, Like I said it works when I load the game from my MainScene, but not from my Persistent scene. And it makes no sense cus I dont change anything. This is not really an answer, at best a poor comment. – Green_qaue May 31 '17 at 11:46
  • 1
    You do realize that your start points won't work on scene load? Start() fires only on game start, if you wan't them to position your player game object on their transform you need to trigger your code in OnLevelWasLoaded(). You didn't provide any useful informations but u expect good answers. – Fiffe May 31 '17 at 12:44
  • All relevant code is in the question though? even the StartPoint. But if that is true, that Start() only fires on game start (i.e not on every scene start), why does it work perfectly without Scene0? makes no sense – Green_qaue May 31 '17 at 13:41
  • I added a debug.log to the start method of my StartPoints, and its called every time I load a scene, so not sure what you mean. And the method you suggested is deprecated. – Green_qaue May 31 '17 at 13:48
  • If you're using SceneManager then you should use sceneLoaded delegate and add your positioning function there and it will position your player on startpoint's location every time level is loaded. https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager-sceneLoaded.html – Fiffe May 31 '17 at 14:13
  • You didn't really specify that your player is persistent and that's pretty important – Fiffe May 31 '17 at 14:15
  • your right thats my bad. But I still don't see why simply adding an extra empty scene breaks it? There is litterally nothing in Scene0 – Green_qaue May 31 '17 at 14:28
  • If that scene is unnecessary then just move your persistent controller to your main scene it will work just fine there and then remove the Scene0. – Fiffe May 31 '17 at 14:33
  • It is, just seems to be considered a "best practice", so wanted to find a solution. But for now, yeah thats what im doing – Green_qaue May 31 '17 at 15:02
  • Just for the record: Start() is executed right before the first time Update() is executed. This usually happens the same frame the GameObject is instantiated, and has nothing at all to do with game start. – Thomas Hilbert Jun 01 '17 at 06:19