- Home /
Correct player position when moving between scenes.
In my game, I am building it in a similar method to Fallout/Elder Scrolls to prevent my levels from becoming too large. To accommodate this, I am making new scenes for each level. However, like Fallout/TES, this is an exploration-based RPG, and therefore I need to keep the player location consistent when moving between scenes.
To facilitate this, I am having the character interact with a door with the tag transfer_Door
, and I am allowing the script on this door to manage movement between scenes. Where the problem exists is since I'm going to have multiple doors in each scene that link to other scenes, and sometimes two doors in a single scene that link to the same scene, allowing access to different areas.
My question is, how can I control the location where the player will appear in the destination scene? Fallout/TES handles this by placing an XMarker object (attached to the destination door) that the player moves his/her (X,Y,Z) coords to when the cell loads. What I need, from previous game-making experience, is a global gameObject with code that can interact with all scenes in which I can store the destination information. Is this possible?
Other information that may be beneficial: I am running the free version of Unity I do not mind purchasing a plugin, though I'd like to avoid it if possible
And here's my character interact script (attached to the first person player object)
using UnityEngine;
using System.Collections;
public class interactScript : MonoBehaviour
{
GameObject selectedObject;
//activateDoor door;
//activateButton button;
// Update is called once per frame
void Update ()
{
int layerMask = 1 << 8;
layerMask = ~layerMask;
if (Input.GetKeyDown (KeyCode.E))
{
RaycastHit hit;
//If the Raycast hit something within 5.0f meters
if(Physics.Raycast (Camera.main.transform.position, Camera.main.transform.forward, out hit, 10.0f, layerMask))
{
//draw a line to see which way it's going
Debug.DrawRay(Camera.main.transform.position, Camera.main.transform.forward * hit.distance, Color.red);
// store the gameObject of the collider hit
selectedObject = hit.collider.gameObject;
Debug.Log ("Selected Object: " + selectedObject.name);
if (selectedObject.CompareTag("SF_Door"))
{
// activate the object
//door.currentObject = selectedObject.gameObject;
//Debug.Log ("Object with tag 'SF_Door' = " + selectedObject.name);
//door.beenActivated = true;
selectedObject.GetComponent<activateDoor>().setActivated(selectedObject, "player");
}
else if (selectedObject.CompareTag("SF_Button"))
{
//activate the object
// button.currentObject = selectedObject.gameObject;
// button.beenActivated = true;
selectedObject.GetComponent<activateButton>().setActivated(selectedObject);
}
else if (selectedObject.CompareTag ("Transfer_Door"))
{
selectedObject.GetComponent<transferDoor>().setActivated(selectedObject);
}
else
{
Debug.Log ("You cannot interact with this object.");
}
}
}
}
}
And here's my transferDoor script. Note, it's just a framework at this time. (Attached to the door)
using UnityEngine;
using System.Collections;
public class transferDoor : MonoBehaviour
{
public AudioClip doorOpenSound;
public AudioClip doorCloseSound;
public bool inaccessable = false;
[HideInInspector]
public GameObject currentObject;
public GameObject target;
[HideInInspector]
public bool beenActivated = false;
bool doOnce = false;
public void setActivated (GameObject temp)
{
currentObject = temp;
if (inaccessable == false)
{
beenActivated = true;
}
else
{
Debug.Log ("This door is inaccessable");
}
}
// Update is called once per frame
void Update ()
{
if (beenActivated = true)
{
beenActivated = false;
if (doOnce = false)
{
doOnce = true;
//stuff
}
}
}
}
You can use DontDestroyOnLoad to keep preferred objects and store values.
Answer by spiceboy9994 · Nov 28, 2014 at 09:34 PM
From what I could understand on your question, what you need it's a game controller, that stores the last door used by the player. Here's a very good basic tutorial on how to create a Game Controller Class.
public class GameManager: MonoBehaviour
{
private static GameManager_instance;
//used to store latest used door
public Vector3 LastUsedDoorPosition;
public static GameManager instance
{
get
{
if(_instance == null)
{
_instance = GameObject.FindObjectOfType<GameManager>();
//Tell unity not to destroy this object when loading a new scene!
DontDestroyOnLoad(_instance.gameObject);
}
return _instance;
}
}
void Awake()
{
if(_instance == null)
{
//If I am the first instance, make me the Singleton
_instance = this;
DontDestroyOnLoad(this);
}
else
{
//If a Singleton already exists and you find
//another reference in scene, destroy it!
if(this != _instance)
Destroy(this.gameObject);
}
}
}
Once you have your controller created and a prefab on your scenes with the behavior attached, you could use your interaction script to add an Start method and use it to position the player on the correct spawn point:
using UnityEngine;
using System.Collections;
public class interactScript : MonoBehaviour
{
GameObject selectedObject;
//activateDoor door;
//activateButton button;
void Start() {
//get the last door position
transform.localPosition = GameManager.instance.LastUsedDoorPosition;
}
// Update is called once per frame
void Update ()
{
//your code
}
}
And don't forget to update the door position when door is activated
public class transferDoor : MonoBehaviour
{
.
.
public void setActivated (GameObject temp)
{
currentObject = temp;
//Update singleton variable
GameManager.instance.LastUsedDoorPosition = transform.localPosition;
if (inaccessable == false)
{
beenActivated = true;
}
else
{
Debug.Log ("This door is inaccessable");
}
}
.
.
}
There is a lot of info on the web about how to create a Game controller as a persistent singleton. Here's some examples:
http://www.glenstevens.ca/unity3d-best-practices/
http://unitypatterns.com/singletons/
Regards