Unity: Weird scene transition bug after adding a “

2019-09-14 07:19发布

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.

3条回答
老娘就宠你
2楼-- · 2019-09-14 08:06

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}

查看更多
男人必须洒脱
3楼-- · 2019-09-14 08:07

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.

查看更多
贼婆χ
4楼-- · 2019-09-14 08:15

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.

查看更多
登录 后发表回答