I've trying to make an adventure game which saves different bools in order to get to the next scene. The problem is that one bool is in another scene with makes it null. Is there any advice?
public class Farmer : MonoBehaviour
{
public bool NextLevel = false;
public Cow1 cow1;
public Cow2 cow2;
public Cow3 cow3;
public Dialogue dialogue;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (GetComponent<Cow1>().getIsDisplayed() && GetComponent<Cow2>().getIsDisplayed() && GetComponent<Cow3>().getIsDisplayed())
{
NextLevel = true;
}
if (NextLevel == true)
{
FindObjectOfType<DialogueManager>().startDialogue(dialogue);
}
}
}
Yes, there is quite an easy way to do this, simply give a game object the DontDestroyOnLoad property
{
public bool NextLevel = false;
public Cow1 cow1;
public Cow2 cow2;
public Cow3 cow3;
public Dialogue dialogue;
// Start is called before the first frame update
void Start()
{
DontDestroyOnLoad(this.gameObject);
}
// Update is called once per frame
void Update()
{
if (GetComponent<Cow1>().getIsDisplayed() && GetComponent<Cow2>().getIsDisplayed() && GetComponent<Cow3>().getIsDisplayed())
{
NextLevel = true;
}
if (NextLevel == true)
{
FindObjectOfType<DialogueManager>().startDialogue(dialogue);
}
}
}
This will make your game object persistent through scene changes
You can make use of multi-scene editing so you have some values and variables carry on between your scenes. You can learn more about this here.
Related
So I have two scripts one to press a button which changes between two objects, and what I want to do is be able to switch out the second avatar with others when I hit e when near another object. the scripts are working, but I have no clue how to get it to switch
here are the trigger script and the switch script
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SwitchCharacterScript : MonoBehaviour
{
private const string ButtonName = "E";
// references to controlled game objects
public GameObject avatar1, avatar2;
// variable contains which avatar is on and active
int whichAvatarIsOn = 1;
// Use this for initialization
void Start()
{
// anable first avatar and disable another one
avatar1.gameObject.SetActive(true);
avatar2.gameObject.SetActive(false);
}
// public method to switch avatars by pressing the UI button
public void SwitchAvatar()
{
// processing whichAvatarIsOn variable
switch (whichAvatarIsOn)
{
// if the first avatar is on
case 1:
// then the second avatar is on now
whichAvatarIsOn = 2;
// disable the first one and enable the second one
avatar1.gameObject.SetActive(false);
avatar2.gameObject.SetActive(true);
break;
// if the second avatar is on
case 2:
// then the first avatar is on now
whichAvatarIsOn = 1;
// disable the second one and enable the first one
avatar1.gameObject.SetActive(true);
avatar2.gameObject.SetActive(false);
break;
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftAlt))
{
SwitchAvatar();
}
}
}
`
`
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
public class Trigger : MonoBehaviour
{
public SwitchCharacterScript player;
[SerializeField] private bool triggerActive = false;
public GameObject GameObject;
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player")
{
triggerActive = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.tag == "Player")
{
triggerActive = false;
}
}
private void Update()
{
if (triggerActive && Input.GetKeyDown(KeyCode.E))
{
SomeCoolAction();
}
}
public void SomeCoolAction()
{
}
}
`
In the somecoolaction is where I don't know what to do to be able to switch them; if you could not tell, I am new at this both coding and using StackOverflow.
I could not find anything on how to do something similar, or I did not know what to search for and could not find if anyone had a way to do it or a video or something.
Instead of bools I would rather store the actual reference:
public class Trigger : MonoBehaviour
{
private SwitchCharacterScript player;
void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent<SwitchCharacterScript>(out var switchCharacer))
{
player = switcCharacter;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (player.gameObject == other.gameObject)
{
player = null;
}
}
private void Update()
{
if (player && Input.GetKeyDown(KeyCode.E))
{
player.SwitchAvatar();
}
}
}
In general to be a bit more dynamic I would rather use an array for the characters and do e.g.
public class SwitchCharacterScript : MonoBehaviour
{
// references to controlled game objects
public GameObject[] avatars;
// variable contains which avatar is on and active
private int whichAvatarIsOn = -1;
// Use this for initialization
private void Start()
{
foreach(var avatar in avatars)
{
avatar.SetActive(false);
}
SwitchAvatar();
}
// public method to switch avatars by pressing the UI button
public void SwitchAvatar()
{
if(whichAvatarIsOn > 0 && whichAvatarIsOn < avatars.Length)
{
avatars[whichAvatarIsOn].SetActive(false);
}
whichAvatarIsOn = (whichAvatarIsOn + 1) % avatars.Length;
avatars[whichAvatarIsOn].SetActive(true);
}
}
I am making Replay logic for my game, where when I click replay I got to the Main Page. The problem I am facing is that after clicking Play on the game after coming from Replay, the Zombie character in my game is not showing up. The game is running without the player. I am posting the script, check the Replay function which is attached to Replay button in the game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class GameManager : MonoBehaviour
{
public static GameManager instance = null;
private bool playerActive = false;
private bool gameOver = false;
private bool gameStarted = false;
private GameObject newZombie;
[SerializeField] private GameObject mainMenu; //contains main menu content
[SerializeField] private GameObject endGame; //contains game over content
[SerializeField] private GameObject zombie;
public bool PlayerActive{
get{
return playerActive;
}
}
public bool GameOver{
get{
return gameOver;
}
}
public bool GameStarted{
get{
return gameStarted;
}
}
void Awake()
{
if(instance == null){
instance = this;
}else if(instance != this){
Destroy(gameObject);
}
Assert.IsNotNull(mainMenu);
Assert.IsNotNull(endGame);
DontDestroyOnLoad(gameObject);
}
// Start is called before the first frame update
void Start()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
}
// Update is called once per frame
void Update()
{
}
public void PlayerCollided()
{
gameOver = true;
endGame.SetActive(true);
mainMenu.SetActive(false);
DontDestroyOnLoad(gameObject);
}
public void PlayerStartedGame()
{
playerActive = true;
}
public void EnterGame()
{
endGame.SetActive(false);
mainMenu.SetActive(false);
gameStarted = true;
}
public void Replay()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
gameOver = false;
newZombie = Instantiate(zombie) as GameObject;
}
There are a lot of assumptions we have to make based on the information you gave.
Try instantiating the zombie on a specific location. You're using Instantiate(gameObject), but there's a different variant to the Instantiate method which also takes a Vector3 position to spawn the object as a second argument.
If that doesn't work, please reply with answers to the following questions:
Does the zombie spawn at all (is it in the hierarchy)
Which methods exactly do the buttons invoke, for example:
[1]: https://i.stack.imgur.com/9xbgy.png
You're using a singleton pattern but you don't change scenes in this class. Are you changing scenes in any of your scripts? If yes, you will have to consider looking into them because every script that is persisting between scenes with the DontDestroyOnLoad() method could potentially interfere with your player.
Vlad
In the Hierarchy I have 3 objects I want to keep and to not destroy when starting a new game.
But I want to destroy them when switching back to the main menu.
The objects are : Player , Game Manager , Scene Loader
On the 3 objects Player , Game Manager , Scene Loader I added to each one a script name PersistentManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PersistentManager : MonoBehaviour
{
public static PersistentManager Instance { get; private set; }
private void Awake()
{
if(Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
The Scene Loader script that attached to Scene Loader :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class SceneLoader : MonoBehaviour
{
private bool loadScene = false;
[SerializeField]
private int scene;
[SerializeField]
private Text loadingText;
// Updates once per frame
void Update()
{
// If the player has pressed the space bar and a new scene is not loading yet...
if (Input.GetKeyUp(KeyCode.Space) && loadScene == false)
{
LoadScene(scene);
}
// If the new scene has started loading...
if (loadScene == true)
{
// ...then pulse the transparency of the loading text to let the player know that the computer is still working.
loadingText.color = new Color(loadingText.color.r, loadingText.color.g, loadingText.color.b, Mathf.PingPong(Time.time, 1));
}
}
// The coroutine runs on its own at the same time as Update() and takes an integer indicating which scene to load.
IEnumerator LoadNewScene()
{
// This line waits for 3 seconds before executing the next line in the coroutine.
// This line is only necessary for this demo. The scenes are so simple that they load too fast to read the "Loading..." text.
//yield return new WaitForSeconds(3);
// Start an asynchronous operation to load the scene that was passed to the LoadNewScene coroutine.
AsyncOperation async = SceneManager.LoadSceneAsync(scene);
// While the asynchronous operation to load the new scene is not yet complete, continue waiting until it's done.
while (!async.isDone)
{
yield return null;
}
}
public void LoadScene(int scene)
{
// ...set the loadScene boolean to true to prevent loading a new scene more than once...
loadScene = true;
// ...change the instruction text to read "Loading..."
loadingText.text = "Loading...";
this.scene = scene;
// ...and start a coroutine that will load the desired scene.
StartCoroutine(LoadNewScene());
}
}
And the Game Manager script that attached to Game Manager :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public SceneLoader sceneLoader;
public PlayerController playerController;
public CamMouseLook camMouseLook;
public static bool backToMainMenu = false;
public static bool togglePauseGame;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
PauseGame();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
BackToMainMenu();
}
}
public void PauseGame()
{
togglePauseGame = !togglePauseGame;
if (togglePauseGame == true)
{
playerController.enabled = false;
camMouseLook.enabled = false;
Time.timeScale = 0f;
}
else
{
playerController.enabled = true;
camMouseLook.enabled = true;
Time.timeScale = 1f;
}
}
private void BackToMainMenu()
{
sceneLoader.LoadScene(0);
playerController.enabled = false;
camMouseLook.enabled = false;
Cursor.lockState = CursorLockMode.None;
Time.timeScale = 0f;
backToMainMenu = true;
}
}
Now when I'm running the game and then pressing the space bar for starting a new game the Main Menu scene is removed not sure why.
Before using the PersistentManager script I used on each of the 3 objects another script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DontDestroy : MonoBehaviour
{
private void Awake()
{
if (GameManager.backToMainMenu == false)
{
DontDestroyOnLoad(transform);
}
}
}
That keep the 3 objects alive when starting a new game and if switching back to the main menu the problem was that the 3 objects was also in the main menu when switching back to the main menu.
So I had 6 copies of the objects. 3 in the main menu scene and 3 in the DontDestroyOnLoad scene.
That's why I'm trying to use singleton.
I want that when I'm switching back to the main menu the 3 object will not be in the main menu scene in the hierarchy only on the DontDestroyOnLoad.
The problem is that the class you are inheriting from has it's instance in the parent
public static PersistentManager Instance { get; private set; }
this means that the first object will go
Is instance null? yes
Set me as instance
Set a don't destroy on load
the other two objects will check the instance, see that it's not null because the first object is referenced, and they will destroy themselves.
In general I would advise against using singletons as much as possible, as some might consider them an anti design pattern.
However, if you still want to use one; I'm attaching here a script I used in games that are in production for multiple years with millions of users, so it's already set up against most of the edge cases we encountered.
#region Using
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
#endregion
/// <summary>
/// Generic singleton Class. Extend this class to make singleton component.
/// Example:
/// <code>
/// public class Foo : GenericSingleton<Foo>
/// </code>
/// . To get the instance of Foo class, use <code>Foo.instance</code>
/// Override <code>Init()</code> method instead of using <code>Awake()</code>
/// from this class.
/// </summary>
public abstract class GenericSingleton<T> : MonoBehaviour where T : GenericSingleton<T>
{
private static T _instance;
[SerializeField, Tooltip("If set to true, the gameobject will deactive on Awake")] private bool _deactivateOnLoad;
[SerializeField, Tooltip("If set to true, the singleton will be marked as \"don't destroy on load\"")] private bool _dontDestroyOnLoad;
private bool _isInitialized;
public static T instance
{
get
{
// Instance required for the first time, we look for it
if (_instance != null)
{
return _instance;
}
var instances = Resources.FindObjectsOfTypeAll<T>();
if (instances == null || instances.Length == 0)
{
return null;
}
_instance = instances.FirstOrDefault(i => i.gameObject.scene.buildIndex != -1);
if (Application.isPlaying)
{
_instance?.Init();
}
return _instance;
}
}
// If no other monobehaviour request the instance in an awake function
// executing before this one, no need to search the object.
protected virtual void Awake()
{
if (_instance == null || !_instance || !_instance.gameObject)
{
_instance = (T)this;
}
else if (_instance != this)
{
Debug.LogError($"Another instance of {GetType()} already exist! Destroying self...");
Destroy(this);
return;
}
_instance.Init();
}
/// <summary>
/// This function is called when the instance is used the first time
/// Put all the initializations you need here, as you would do in Awake
/// </summary>
public void Init()
{
if (_isInitialized)
{
return;
}
if (_dontDestroyOnLoad)
{
DontDestroyOnLoad(gameObject);
}
if (_deactivateOnLoad)
{
gameObject.SetActive(false);
}
SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged;
InternalInit();
_isInitialized = true;
}
private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene scene)
{
// Sanity
if (!instance || gameObject == null)
{
SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
_instance = null;
return;
}
if (_dontDestroyOnLoad)
{
return;
}
SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
_instance = null;
}
protected abstract void InternalInit();
/// Make sure the instance isn't referenced anymore when the user quit, just in case.
private void OnApplicationQuit()
{
_instance = null;
}
void OnDestroy()
{
// Clear static listener OnDestroy
SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
StopAllCoroutines();
InternalOnDestroy();
if (_instance != this)
{
return;
}
_instance = null;
_isInitialized = false;
}
protected abstract void InternalOnDestroy();
}
I'm currently working on a game where i have a lightswitch, whenever i'm touching the spherecollider around the switch, the light should turn on. And when i'm not touching the spherecollider anymore, it should turn off. I've not been doing this in a long time, so i've gotten a bit rusty.
public Light ceilingLight;
public bool LS;
private void Update()
{
if (LS == true)
{
ceilingLight.enabled = true;
}
if(LS == false)
{
ceilingLight.enabled = false;
}
}
private void OnTriggerEnter(Collider other)
{
if(other.tag == "Player")
{
LS = true;
}
if(other.tag != "Player")
{
LS = false;
}
}
First: Instead of using Update you rather want it to be event driven and this avoid unnecessary Update calls when nothing changed anyway.
Then as was mentioned in the comment what you currently do is enable the light if the player touches, disable it if anything else touches it.
You rather want to disable it when the player doesn't touch anymore and ignore everything else.
Simply do
public Light ceilingLight;
// optionally additionally configure whether the light should start enabled or not
public bool InitiallyEnabled;
private void Start()
{
ceilingLight.enabled = InitiallyEnabled;
}
private void OnTriggerEnter(Collider other)
{
if(!other.CompareTag("Player")) return;
ceilingLight.enabled = true;
}
private void OnTriggerExit(Collider other)
{
if(!other.CompareTag("Player")) return;
ceilingLight.enabled = false;
}
As alternative you could also use it as a toggle like
private void OnTriggerEnter(Collider other)
{
if(!other.CompareTag("Player")) return;
ceilingLight.enabled = ! ceilingLight.enabled;
}
so with every touch the player would toggle the light state without having to keep pressing it continuously.
Consider that you may need all types of things to be pressable later on in your app, so you could start with a basic Pressable class to handle the basics. You can then add this behavior to the compositing mix of your respective prefab objects (which may be Holdable, Steerable, Pressable and so on):
using UnityEngine;
public class Pressable : MonoBehaviour
{
[SerializeField] protected bool active = false;
[SerializeField] AudioSource pressSound = null;
protected virtual void Start()
{
}
public virtual void Press()
{
if (pressSound != null) { pressSound.Play(); }
active = !active;
}
}
Note the active bool will appear in the inspector, so you can set it to true at start. (You may also want to pass Press(Person person) in case something should be done with the person pressing.)
Now for your specific light switch, you would inherit this and add the logic for just lights:
using UnityEngine;
public class LightPressable : Pressable
{
Light light = null;
protected override void Start()
{
base.Start();
light = GetComponentInChildren<Light>();
UpdateBasedOnActive();
}
public override void Press()
{
base.Press();
UpdateBasedOnActive();
}
void UpdateBasedOnActive()
{
light.enabled = active;
}
}
Good luck!
I tried both setting enabled to true and SetActive(true) to diplay a Gameover Image. However, none of them works. I have a public Image gameOverImage declared and set the gameOverImage.enabled in the Start() to false.
private void Start()
{
gameOverImage.enabled = false;
}
Then in one of my function, I put:
public void killAt(Vector2 loc)
{
foreach (GameObject obj in setup.GetActive())
{
if (obj.GetComponent<PieceInfo>().GetLocation() == loc)
{
if (obj.GetComponent<PieceInfo>().GetPieceType() == 'G')
{
gameOver = true;
gameOverImage.enabled = true;
Debug.Log("?????");
}
setup.deactivate(obj);
break;
}
}
}
The console does have ????? logged but no gameOverImage displayed in the game view. The game is over because I couldn't click my game any more. Any ideas? I also tried UI text. It doesn't work as well.
In Unity in order to activate an object you need to have it in the scene. If the GameObject does not exist in the scene, or in your case the UI Element that contains the Image, is not in your scene SetActive(true) or Enabled = true will not have any effect. You will need to instantiate the object. To make it exist in your world.
Prefabs, are useful to store a common configuration that can be used multiple times in your game but they do not exist in the scene, that is why you have to instantiate them.
For your GameOver Image you have a few options the simplest is this:
using UnityEngine;
using UnityEngine.UI;
public class EnableUI : MonoBehaviour {
public Image GameOverImage;
// Use this for initialization
void Start () {
GameOverImage.gameObject.SetActive(false);
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
GameOverImage.gameObject.SetActive(true);
}
}
}
If you want to instantiate it:
using UnityEngine;
public class EnableUI : MonoBehaviour {
// This is a prefab that is canvas with your game over image nested as a child image UI under it
public GameObject GameOverObject;
// Use this for initialization
void Start () { }
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(GameOverObject);
}
}
}