Hey guys, I have been stumped on this all day, and could really use some input. Basically I have a prototype RPG game in the works, and I am using ScriptableObjects to create my dialogue system as well as book reading system (each NPC or book has its own object created).
My problem is as follows: When my player character needs to leave the scene through a door, I use DontDestroyOnLoad so that the player character continues to exist to the next scene location. To avoid duplicating the player when he returns to the original scene, I am using Singleton pattern, and destroying the original player gameobject. Unfortunately, the NPC objects as well as the book objects require several variable references to the player gameobject as well as the UI elements that I have stored as a child to the player. Originally I used the process of FindGameObjectWithTag(“Tag Name”) to set the variable, but I later learned that it is very heavy to do that (especially because each individual book and NPC was running this process at the start of the scene). I then switched to using public variable references to the player and UI, and just dragged and dropped to set the reference. The thing is, when I switch scenes, there is no way to set these variables to reference an object that doesn’t exist in the current scene. When the player returns to the original scene, after deleting the duplicated gameobject of the player, all of the original books and NPCs no longer recognize the references, and I get a bunch of errors.
After this, I though maybe I should try reverting back to the FndGameObjectsWithTag system, though I am having issues with this now as well. I am trying to assign the variable references in my Start function, and in another script am deleting my duplicate player object in my Awake method (as I believe the Awake is called before Start), though when loading the scene, my variables remain unassigned, resulting in a ton of error messages.
I even tried delaying the assigning of the variables with a coroutine, but still did not work.
I also tried using a “middle man” gameobject that would basically sit in each scene and try to detect when the player entered, and then assign the variables of the scene’s books and NPCs, rather than each individual object searching for the player at once. This also did not work.
Essentially, my preferred method would be assigning all the variables once by making them public, and then simply dragging and dropping each reference, and then doing this for other scenes as well that do not contain the player gameobject (though to my understanding this cannot be done).
I know there must be a better way to do this…
Here are all of my relevant scripts: (the NPC system is very similar to the book, so the logic is the same behind both…)
Script for the book scriptable object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = “New Book Register”, menuName = “Book Registrar”)]
public class Book : ScriptableObject
{
public string itemID;
public string title;
[TextArea(5,19)]
public string pageOneText;
[TextArea(5, 19)]
public string pageTwoText;
}
Script for calling the book into action:
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.UI;
public class BookManager : MonoBehaviour
{
bool isReading = false;
public Book book;
float distance;
Text bookTextOne;
Text bookTextTwo;
Text bookTitleText;
Text interactiveText;
GameObject player;
GameObject bookUI;
void Start()
{
bookTextOne = GameObject.FindGameObjectWithTag(“Book Text”).GetComponent<UnityEngine.UI.Text>();
bookTextTwo = GameObject.FindGameObjectWithTag(“Book Text 2”).GetComponent<UnityEngine.UI.Text>();
bookTitleText = GameObject.FindGameObjectWithTag(“Book Title Text”).GetComponent<UnityEngine.UI.Text>();
interactiveText = GameObject.FindGameObjectWithTag(“Interactable Text”).GetComponent<UnityEngine.UI.Text>();
player = GameObject.FindGameObjectWithTag(“Player”);
bookUI = GameObject.FindGameObjectWithTag(“Book UI”);
}
void OnMouseOver()
{
distance = Vector3.Distance(player.transform.position, this.transform.position);
if (distance <= 2.5f)
{
interactiveText.text = “Read '” + book.title + “'”;
if (Input.GetKeyDown(KeyCode.E) && isReading == false)
{
bookUI.SetActive(true);
bookTitleText.text = book.title;
bookTextOne.text = book.pageOneText;
bookTextTwo.text = book.pageTwoText;
PlayerMovement.canMove = false;
MouseLook.canLook = false;
PlayerMovement.canRegenStamina = true;
isReading = true;
}
else if(Input.GetKeyDown(KeyCode.E) && isReading == true)
{
bookUI.SetActive(false);
PlayerMovement.canMove = true;
MouseLook.canLook = true;
PlayerMovement.canRegenStamina = false;
isReading = false;
}
}
else
{
interactiveText.text = “”;
}
}
void OnMouseExit()
{
interactiveText.text = “”;
}
}
Script for removing the duplicate player object when returning to original scene:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
static GameManager instance;
void Awake()
{
if(instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
DontDestroyOnLoad(gameObject);
}
}
}
Script for the door scriptable object(to move the player to the new scene)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = “New Door Register”, menuName = “Door Registrar”)]
public class Door : ScriptableObject
{
public string startingScene;
public string destinationScene;
public float destinationX;
public float destinationY;
public float destinationZ;
}
Script for Scene Management (switching the scene using the door variables)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class SceneManager : MonoBehaviour
{
public Door door;
//public Text interactiveText;
GameObject interactiveText;
GameObject player;
float distance;
private static bool created = false;
void Start()
{
player = GameObject.FindGameObjectWithTag(“Player”);
interactiveText = GameObject.FindGameObjectWithTag(“Interactable Text”);
}
void OnMouseOver()
{
distance = Vector3.Distance(player.transform.position, this.transform.position);
if (distance <= 2.5f)
{
interactiveText.GetComponent<UnityEngine.UI.Text>().text = "Open to " + door.destinationScene;
if(Input.GetKeyDown(KeyCode.E))
{
LoadScene();
}
}
else
{
interactiveText.GetComponent<UnityEngine.UI.Text>().text = “”;
}
}
void OnMouseExit()
{
interactiveText.GetComponent<UnityEngine.UI.Text>().text = “”;
}
void LoadScene()
{
player.GetComponent().enabled = false;
player.transform.position = new Vector3 (door.destinationX, door.destinationY, door.destinationZ);
player.GetComponent().enabled = true;
UnityEngine.SceneManagement.SceneManager.LoadScene(door.destinationScene, LoadSceneMode.Single);
}
}