Pagina's

Sunday, 14 April 2019

Scene Management Trouble

Another technical post today, because I'm having problems again. Yesterday evening, I started trying to get the rotation right when getting through a door to another scene.I have this GameHandler singleton that controls some things that should not be lost when changing scenes. In this case, it's a door number and a rotation .

When changing scenes, the plan is that the GameHandler gives information to the player-scripts to put him on the right position (which often works) and rotation (which does not seem to work).


In short: I have two problems. 



  1. First I can't figure out why sometimes the scene puts the player on the 0,0,0-position when loading and how to fix that.
  2. Second, somehow I can't get the rotation of the player to work to look at the door orientation point or to keep the rotation that he had in the previous scene.
Here's my project on GitHub: https://github.com/Xrocetoxtos/RPG2



So what is going on? I have two scenes right now: DevScene and DevScene 1, in the build settings as scene 0 and 1:

I have a GameHandler script that should not be destroyed on scene change and that has a couple of pieces of code including:

public class GameHandler : MonoBehaviour
{
    public static GameHandler instance = null;

    public GameObject player;
    private PlayerState playerState;

    [Header("SceneManagement")]
    public int currentDoorNumber;
    public GameObject[] doorArray;
    public Quaternion doorRotation;
    public PlayerLook playerLook;
    private int index;
     [...]

}

The Player works with a couple of scripts right now. PlayerState tracks the state of the player, like wether it's crouching or not. PlayerLook controls the camera and rotation of the first person controller. That one is the one I'm trying to change in code.

Then there's the Awake-function where amongst other things this happens:


    private void Awake()
    {
        if(instance ==null)
        {
            DontDestroyOnLoad(gameObject);
            instance = this;
        }
        else
        {
            Destroy(gameObject);
        }

        if (player ==null)
        {
            player = GameObject.FindGameObjectWithTag("Player");
        }

        if(doorArray==null ||doorArray.Length==0)
        {
            doorArray = GameObject.FindGameObjectsWithTag("DoorScene");
        }
        
        [...]
playerState = player.GetComponent<PlayerState>(); playerLook = player.transform.Find("PlayerCamera").gameObject.GetComponent<PlayerLook>(); [...] }

And, where the magic happend: the Scene Management-portion:


    public void LoadScene(int passedDoorNumber, int passedSceneNumber)
    {
        currentDoorNumber = passedDoorNumber;

        int index = SceneManager.GetActiveScene().buildIndex;
        SceneManager.LoadScene(passedSceneNumber);
    }

    private void OnLevelWasLoaded()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        doorArray = GameObject.FindGameObjectsWithTag("DoorScene");
        playerLook = player.transform.Find("PlayerCamera").gameObject.GetComponent<PlayerLook>();


        for (int i=0; i<doorArray.Length; i++)
        {
            DoorScene doorScene = doorArray[i].GetComponent<DoorScene>();
            if (doorScene.doorNumber == currentDoorNumber)
            {
                index = i;
                FunctionTimer.Create(() => PlayerInNewScene(), .1f);
            }
        }
    }

    private void PlayerInNewScene()
    {
        //player naar de locatie van de deur verplaatsen
        player.transform.position = doorArray[index].transform.position;
        playerLook.transform.position = doorArray[index].transform.position;
        playerLook.LookCameraExternal(instance.doorRotation);
    }

This part references a DoorScene script. That's a simple script controlling access to a door and this door has information on where to go. For now, the DoorScene script fires when the player presses E while colliding to a collider near the door. This is the entire script:


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

public class DoorScene : MonoBehaviour
{
    public int doorNumber;
    public int doorSceneNumber;
    

    private void OnTriggerStay()
    {
        if(Input.GetKeyDown(KeyCode.E))
        {
            GameHandler.instance.doorRotation = transform.rotation;

            GameHandler.instance.LoadScene(doorNumber, doorSceneNumber);
        }
    }

}

Now note that these two scripts have been changed to try things, but what it should do is that once the code on the OnTriggerStay method in the DoorScene script is triggered, the GameHandler should load the scene that it passed by the DoorScene as doorSceneNumber. There, the GameHandler should look for a door with a matching doorNumber and place the player there.

Finally, scriptwise, there's the PlayerLook script that controls looking around with the mouse. It has this code. Most of it might be irrelevant, but I'm not sure if it messes stuff up, so I'll share it anyway. Important stuff might be the LookCameraExternal that should get the Quaternion rotation from the GameHandler and use it:


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

public class PlayerLook : MonoBehaviour
{
    private Vector2 mouseLook;
    private Vector2 smoothV;

    public float sensitivity = 5f;
    public float smoothing = 2f;
    public float clampStanding = 90f;
    public float clampCrouching = 60f;
    public float clampValue = 90f;

    public GameObject player;

    private void Start()
    {
        player = this.transform.parent.gameObject;
    }

    private void Update()
    {
        GetLookInput();
        if (mouseLook != Vector2.zero)
        {
            LookCamera();
        }
    }

    private void GetLookInput()
    {
        float scaling = sensitivity * smoothing;
        var md = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
        md = Vector2.Scale(md, new Vector2(scaling, scaling));

        smoothV.x = Mathf.Lerp(smoothV.x, md.x, 1f / smoothing);
        smoothV.y = Mathf.Lerp(smoothV.y, md.y, 1f / smoothing);

        mouseLook += smoothV;

        //voorkomen dat je op zijn kop over je heen of onmder je door kunt kijken
        mouseLook.y = Mathf.Clamp(mouseLook.y, -clampValue, clampValue);
    }

    private void LookCamera()
    {
        transform.localRotation = Quaternion.AngleAxis(-mouseLook.y, Vector3.right);
        player.transform.localRotation = Quaternion.AngleAxis(mouseLook.x, player.transform.up);
    }

    public void LookCameraExternal(Quaternion look)
    {
        player = this.transform.parent.gameObject;

        transform.localRotation = look;
        player.transform.localRotation = look;
    }
}

Now to Unity. There's this DevScene:


The DevScene 1 looks the same, but without the GameHandler object, because that it kept when changing scenes in a DontDestroyOnLoad thingy.

There's these DoorScene prefabs. They have a pivot for rotating the door open and right, a cube for the door itself and a collider on the empty object itself. Then there's an orientation that I tried to use in this scene management thing. I think that's probably the neatest way to fix the issue. When the player enters the scene, it needs to look to the orientation point of the door it's standing in front of.


Both Scenes have two doors with a DoorScene script. in DevScene, the first door has doorNumber 1 and doorSceneNumber 1, the other door has doorNumber 2 and doorSceneNumber one. In DevScene 1, it's the same, but both doors with doorSceneNumber 0.  So the left doors are linked together and so are the right doors. In DevScene, the doors are rotated 180 degrees on the y-axis, so the collider is on the other side of the wall. When the player enters a door, he should move to the other side of the door in the other Scene, facing away from the door.

Sooooo.. What seems to happen? When I walk to a door in DevScene  and i press E, usually, the player gets brought to the corresponding door in DevScene 1, facing away from the door. This is exactly what I want. Sometimes though, the player gets to the (0,0,0) point in the target scene.  That's not right. I haven't seen in what cases this happens, so I'll just say sometimes.

When the player is in DevScene 1, on the other side of a door and turns to return, sometimes, he gets to DevScene (0,0,0), and sometimes in front of the right door and facing the door.



In short: I have two problems. 
  1. First I can't figure out why sometimes the scene puts the player on the 0,0,0-position when loading and how to fix that.
  2. Second, somehow I can't get the rotation of the player to work to look at the door orientation point or to keep the rotation that he had in the previous scene.

If anyone could help me out, i'd be mightily grateful!

Here's my project on GitHub: https://github.com/Xrocetoxtos/RPG2


No comments:

Post a Comment