- Home /
How to check if the player has beaten a seperate scene that is required to progress in another scene
Sorry if the question was worded badly XD
So basically my team and I have been hitting a wall for a couple of days now with our game. Our game has 12 levels but 16 playable scenes. Level 12 is the last level but in level 12 we have four sub rooms that are their own separate scenes. The player has to complete the sub rooms in order to unlock the elevator in level 12. We used to stream all the levels in the game but as a team we decided to not go about the streaming way because it was too intense on our resources. So my question is how can I make it so that the elevator in level 12 knows that the player completed the 4 sub rooms? Would anyone be able to point me in the right direction?
Kindly,
Harpoaian
Use PlayerPrefs. $$anonymous$$ost simple way. Works all the time. Can be used from anywhere. PlayerPrefs.
That sounds like a PR announcement...
Answer by melsy · Apr 06, 2018 at 12:03 AM
Simple serialized class with bools would work very easy as well. Have either 4 bools, one for each section , or a bool array, and have the bool set to true on completing the section. Then just save that to the player data and check that data when the player gets to the elevator.
How is the team handling the save data.
Sorry for the late reply but this is how we save data public static GameData data;
void Awake() { if (data == null) { DontDestroyOnLoad(gameObject); data = this; } else if (data != this) { Destroy(gameObject); } }
public void Save()
{
FileStream file;
if (File.Exists(Application.persistentDataPath + "/gameInfo.dat"))
{
file = File.Open(Application.persistentDataPath + "/gameInfo.dat", File$$anonymous$$ode.Open);
}
else
{
file = File.Create(Application.persistentDataPath + "/gameInfo.dat");
}
BinaryFormatter bf = new BinaryFormatter();
LevelData data = new LevelData();
data = SetData(data);
bf.Serialize(file, data);
file.Close();
}
public void Load()
{
if (File.Exists(Application.persistentDataPath + "/gameInfo.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/gameInfo.dat", File$$anonymous$$ode.Open);
LevelData data = (LevelData)bf.Deserialize(file);
file.Close();
AquireData(data);
} else
{
Save();
}
}
private void Delete()
{
if (File.Exists(Application.persistentDataPath + "/gameInfo.dat"))
{
File.Delete(Application.persistentDataPath + "/gameInfo.dat");
}
}
// Set data to be save
LevelData SetData(LevelData data)
{
// Basic
data.currentLevel = currentLevel;
data.completeGame = completeGame;
data.noUnlockComplete = noUnlockComplete;
data.timeAttack = timeAttack;
// Settings
data.fullscreen = fullscreen;
data.screenWidth = screenWidth;
data.screenHeight = screenHeight;
data.antialiasing = antialiasing;
data.masterVolume = masterVolume;
// Level Unlock
data.level1Unlock = level1Unlock;
data.level2Unlock = level2Unlock;
data.level3Unlock = level3Unlock;
data.level4Unlock = level4Unlock;
data.level5Unlock = level5Unlock;
data.level6Unlock = level6Unlock;
data.level7Unlock = level7Unlock;
data.level8Unlock = level8Unlock;
data.level9Unlock = level9Unlock;
data.level10Unlock = level10Unlock;
data.level11Unlock = level11Unlock;
data.level12Unlock = level12Unlock;
// Level 12 Complete Prefs
data.lightComplete = lightComplete;
data.electricComplete = electricComplete;
data.soundComplete = soundComplete;
data.thermalComplete = thermalComplete;
data.playing12 = playing12;
data.level12CurTimer = level12CurTimer;
data.level12Start = level12Start;
Looks like it got cut off but i get the idea. You are already doing what i thought. Just add those bools and change them to true when the player gets the section complete. Also looking at the save data file it could be simplified quite a bit. Is this a $$anonymous$$onoBehaviour? If so what is the reason for needing this class to be in the scene? A serialized class will keep and hold its variables when it is instantiated by a public static SaveClass saveClass = new SaveClass();
Check out my answer here for a basic save class https://answers.unity.com/questions/1463102/inventory-that-works-across-scenes.html
This can further be simplified by classes that can be defaulted and called during the serialization. So you dont have to set all the variables every time you save and recall every time you load. You only save and load what you need at the moment.
Hello thank you for the reply I am just slightly confused as to go about implementing your solution. $$anonymous$$y $$anonymous$$mmate that started this venue has been really busy so I wanted to try and help him out by solving this issue (once this issue is solved we will be that much closer to release). To answer your initial question yes this is a public class $$anonymous$$onoBehaviour script. I have been working at this issue for a few days now and I have managed to make it so when the player completes the sub room (electric room in this case) the game data recognizes that I completed the level. When I go back to the main room that data from that completed bool ISN'T carrying over. I am at my wits end on how to fix this.
Here is the function in the Electric Room Script that changes the electriccomplete variable. Do I need to have the gameData save in this function?
void UnlockHub ()
{
//HubRoom.GetComponent<Level12CentralRoom>().electricUnlocked = true;
if(gameDataScript.electricComplete == false)
{
gameDataScript.electricComplete = true;
hasCompletedElectricRoom = true;
Debug.Log("Electric Complete: " + gameDataScript.electricComplete);
}
}
Well I took my own advice and I called the save function right after the gameDataScript.electricComplete and it worked! The game data remembered, I swear I look over the simplest things. Thank you for helping me out, it was your previous Unity answer that helped me! All the points to you good sir!!
Answer by ImpOfThePerverse · Apr 05, 2018 at 03:31 PM
My guess would be something using Object.DontDestroyOnLoad:
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
You could create a gameobject with a monobehaviour script with four bools that keep track of whether the player has completed the sub-rooms, and make it persist with Object.DontDestroyOnLoad.
*Edit - You'll probably want to instantiate that gameobject from a script that first checks to see if there is already something in the scene with that component or a unique tag (using FindObjectOfType or FindWithTag).
Answer by ClumsyLobster · Apr 05, 2018 at 05:36 PM
So, you could have a GlobalDataManager.cs script attatched to an empty game object. This exists in the first scene (so the menu scene). It is persistent between scenes which means you can use scripts from each level (level 2 scene, level 3 scene) to access the component.
ie - Player completes level 1. Access GlobalDataManager and set globalDataManager.hasCompletedLevel1 to true.
Don't allow level 2 to be loaded unless level 1 is complete. Imagine a level select menu, you could make the buttons "not interactable". so the "Load Level 2" button only works when the Level 1 is marked as "complete" in the GDM.
GlobalDataManager.cs
using UnityEngine;
public class GlobalDataManager : MonoBehaviour
{
private static bool created = false;
// set booleans for completed levels
public bool CompletedLevel1;
public bool CompletedLevel2;
void Awake()
{
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
Debug.Log("Awake: " + this.gameObject);
}
// set default value to false
CompletedLevel1 = false;
CompletedLevel2 = false;
}
}
Level2.cs
using UnityEngine;
public class Level2 : MonoBehaviour
{
// reference GDM GameObject
public GameObject GlobalDataManagerGO;
// reference global data manager
private GlobalDataManager globalDataManager;
public bool hasCompletedLevel1;
void Awake() {
// find the Global Data Manager GO
GlobalDataManagerGO = GameObject.FindWithTag("GDM");
// reference the GlobalDataManager.cs script component of the GDM GameObject
globalDataManager = GlobalDataManagerGO.GetComponent<GlobalDataManager>();
}
void Start() {
// access a bool from GlobalDataManager.cs and use it to set a value in this script
hasCompletedLevel1 = globalDataManager.CompletedLevel1;
}
}
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html https://unity3d.com/learn/tutorials/topics/scripting/persistence-saving-and-loading-data
Hello sorry for the late reply. Turns out my $$anonymous$$mmate who started working on this solution has a game data empty object with the game data script attached to it. I have implemented your solution to the best of my ability and I can get the game data to state that the electric room is complete and everything works hunky dory BUT the problem that I am having now is when the player leaves the electric room the game data script in the level 12 area says that the electricRoomComplete bool is still running false. So I need to figure out why the variable isn't carrying over to the level 12 room.
Electric Room Script: (important parts)
public GameObject gameDataGO; private GameData gameDataScript; public bool hasCompletedElectricRoom;
void Awake()
{
gameDataGO = GameObject.Find("GameData");
gameDataScript = gameDataGO.GetComponent<GameData>();
}
// Use this for initialization
void Start () {
//HubRoom = GameObject.Find ("FinalLift");
G$$anonymous$$ineticSwitch = GameObject.Find ("G$$anonymous$$ineticSwitch");
GThermalSwitch = GameObject.Find ("GThermalSwitch");
GLightSwitch = GameObject.Find ("GLightSwitch");
GSoundSwitch = GameObject.Find ("GSoundSwitch");
//gameData = GameObject.Find("GameData");
hasCompletedElectricRoom = gameDataScript.electricComplete;
//Referencing each script directly so I can call DoorControl(); from this script.
$$anonymous$$ainElevScript = (ElecRoomElevator) $$anonymous$$ainElevator.GetComponent(typeof(ElecRoomElevator));
EntranceElevScript = (ElecRoomEntrance) EntranceElevator.GetComponent(typeof(ElecRoomEntrance));
if(gameDataGO != null)
{
Debug.Log("Game Data has been found");
}
}
void UnlockHub () { //HubRoom.GetComponent().electricUnlocked = true;
if(gameDataScript.electricComplete == false)
{
//gameDataScript.electricComplete = true;
hasCompletedElectricRoom = true;
Debug.Log("Electric Complete: " + gameDataScript.electricComplete);
}
}
void GroundSwitch () { //If Ground Floor Switch is Triggered
if (GLightSwitch.GetComponent<ElectricObject>().hasEnergy == true && GThermalSwitch.GetComponent<ElectricObject>().hasEnergy == true &&
GSoundSwitch.GetComponent<ElectricObject>().hasEnergy == true && G$$anonymous$$ineticSwitch.GetComponent<ElectricObject>().hasEnergy == true && FloorGComplete == false)
{
//EntranceElevator.GetComponent<ElecRoomEntrance>().Locked = false;
//EntranceElevator.GetComponent<ElecRoomEntrance>().Can$$anonymous$$ove = true;
//EntranceElevScript.DoorControl();
//Debug.Log("This is the reason why I am repeating");
//UnlockHub();
FloorGComplete = true;
//Added Section
if (FloorGComplete == true)
{
EntranceElevator.GetComponent<ElecRoomEntrance>().Locked = false;
EntranceElevator.GetComponent<ElecRoomEntrance>().Can$$anonymous$$ove = true;
EntranceElevScript.DoorControl();
UnlockHub();
//EntranceElevator.GetComponent<Animation>().Play("EntElvOpen");
//Debug.Log("The door should only run the animation once");
//Debug.Log(FloorGComplete);
}
}
}
I will post another comment with the level 12 script as I am running out of characters XD
using UnityEngine;
using System.Collections;
public class Level12CentralRoom : $$anonymous$$onoBehaviour {
public GameObject lightDoor;
public GameObject electricDoor;
public GameObject soundDoor;
public GameObject thermalDoor;
public GameObject finalPlatform;
GameObject FPC;
public GameObject gameDataGO;
private GameData gameDataScript;
void Awake()
{
gameDataGO = GameObject.Find("GameData");
gameDataScript = gameDataGO.GetComponent<GameData>();
}
// Use this for initialization
void Start ()
{
FPC = GameObject.Find("FPS_Proxy");
SetPlayer();
Setup();
PlayerPrefs.SetString("Playing12", "True");
//gameData = GameObject.Find("GameData");
//gameData.GetComponent<GameData>().playing12 = true;
Debug.Log("Electric Complete: " + gameDataScript.electricComplete);
if(gameDataGO != null)
{
Debug.Log("Game Data has been found");
}
}
// Setup saves
void Setup()
{
if (PlayerPrefs.GetString("LightComplete") == "True")
{
lightDoor.GetComponent<Animation>().Play();
}
if (gameDataScript.electricComplete == true)
{
electricDoor.GetComponent<Animation>().Play();
Debug.Log("Electric Complete: " + gameDataScript.electricComplete);
Debug.Log("If I see this that means the completion of the electric room carried over and we are working as intended");
}
Answer by shadowpuppet · Apr 17, 2018 at 03:42 PM
the four sub rooms are on the same level as the elevator so you can simply have a subRoomManager script - maybe on the elevator so that every time a sub room has been visited you set a bool to true. Then in the script you have to use the elevator have a condition that all four bools must be true
on the subrooms when visited add the appropriate subroom to the script:
void OnTriggerEnter(Collider other) {
if(other.tag == "Player")
{
subRoomManager.room1.visited = true;
}
}
///Then on the subRoomManager script//
public static bool room1;
public static bool room2;
public static bool room3;
public static bool room4;
///And on the elevator script//
if (subRoomManager.room1 == true && subRoomManager.room2 == true && subRoomManager.room3 == true && subRoomManager.room4 == true){
//then the script that allow the elevator to be used here
}
}
}
sadly that method won't work because the sub rooms of level 12 are actually their own scenes(Did it for performance issues) So all in all there are 16 levels in my game but we want the player to just feel like level 12 is a huge level. So I am still trying to figure out how to get the GameData to carry over to the level 12 HUB room. In the picture below where you see the blue squiggles those represent the triggers that load each sub room of level 12
ah, I see. I guess that is why some of the answers had the "doNotDestroyOnload". So ins$$anonymous$$d of putting the subRoom$$anonymous$$anager on the elevator it would go on a gameobject ( empty probably) that you can introduce in level 11 and will carry over to 12-16 with the donotdestroyonload