I have a class in Unity that a list of toggle switches that get turned on and off in a separate scene from the rest of my game. What I'm wanting is to have the user select one button and then have a corresponding action happen in my main game when they go back to that scene. However, I'm having issue sending information between scenes.
At the moment my toggle class looks like this:
private bool action1 = false;
public bool Action1
{
get { return action1;}
}
void OnGUI()
{
action1 = GUI.Toggle(new Rect(10, 10, 100, 30), action1, "test");
}
void Update()
{
if(Input.GetButton("Jump"))
{
Application.LoadLevel("Main");
}
}
Then in a class held in my Main scene, I have the following code:
private ActionClass actionIsOn = new ActionClass();
void Start()
{
if(actionIsOn.Action1 == true)
{
Debug.Log("action is on");
}
else
{
Debug.Log("nothing happening");
}
}
However, when I test this, nothing happens.
Have I set this up correctly? Is there a better way to pass this information from one scene to another?
Option #1:
Use a static class to hold global info that's relevant to multiple scenes.
public static class GlobalData
{
public static bool SomeBooleanFlag;
}
This way in your first scene you can set GlobalData.SomeBooleanFlag to some value, and in your second scene you can check for it.
Option #2:
You can use Object.DontDestroyOnLoad to ensure that an object in your scene doesn't get destroyed when a new scene is loading. This way you can aggregate all of the info that you want to pass to the other scene in a single object (or use multiple objects, and keep them all alive), and make sure it stays alive even after the scene has changed.
http://docs.unity3d.com/Documentation/ScriptReference/Object.DontDestroyOnLoad.html
Related
Edit: It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I have an issue with the class below called ProjectileWeapon. It is based on an abstract class called Weapon, and that class inherits MonoBehaviour.
Weapon has two abstract functions called BeginCycle and EndCycle which are implemented in the ProjectileWeapon class. Those functions set a variable called "firing".
The problem is, "firing" doesn't ever seem to be set despite the functions being called correctly. I know the functions are called because I can see the prints in the console.
Also, when I use that variable in the update function, it doesn't do anything because the variable never changes.
The OnGUI function is working and is displaying text on screen, however the "firing" variable is never updated.
Am I misunderstanding how to use inheritance?
This class is on the weapon prefab which is then instantiated during the equipping function in the game
public class ProjectileWeapon : Weapon
{
private bool firing;
private float firingTimer;
void Start()
{
print("ProjectileWeapon start");
}
void OnGUI()
{
GUI.Label(new Rect(0,100,100,100), "ProjectileWeapon firing: " + firing);
}
void Update()
{
// this function is called but "firing" is not updated
}
public override void BeginCycle()
{
print("projectile begin cycle");
firing = true;
}
public override void EndCycle()
{
print("projectile end cycle");
firing = false;
}
}
Here's the base class:
public abstract class Weapon : MonoBehaviour
{
public abstract void BeginCycle();
public abstract void EndCycle();
}
EDIT: Here is the code that calls the above
This component is added to the player game object
public class WeaponHandler : MonoBehaviour
{
public bool FireInput { get; set; } // set to true when user holds the mouse button down, and false when let go
public Weapon WeaponBehaviour; // this is the script that does the weapon functionalility. Any subclass of Weapon can be put here e.g. ProjectileWeapon, MeleeWeapon
private bool isFiring = false;
void OnGUI()
{
GUI.Label(new Rect(0,0,100,100), "fire input:" + FireInput + ", isFiring:" + isFiring);
}
void Update()
{
// fire weapon
if (WeaponBehaviour && FireInput && !isFiring)
{
ActivateWeapon();
}
else if (!FireInput && isFiring)
{
DeActivateWeapon();
}
}
private void ActivateWeapon()
{
print("activate weapon");
isFiring = true;
WeaponBehaviour.BeginCycle();
}
private void DeActivateWeapon()
{
print("deactivate weapon");
isFiring = false;
WeaponBehaviour.EndCycle();
}
}
Based on the code you've provided, there are three possible scenarios:
You're not calling the functions. However, if the print functions are called, then you must be calling them.
You're calling both functions, which sets the variable to true, and then to false.
You're overriding the variable with a local variable with the same name. Visual Studio will warn you if that's the case.
It's hard to tell without knowing where the variables are supposed to be called. If you upload the rest of the code, I'm sure the answer will be clear.
It turns out it was how I was getting the ProjectileWeapon component. I was getting the one that was on the non-instantiated prefab instead of getting the one on weapon gameobject. I changed it so that the code instantiates the game object first (or gets the existing one if we've already picked it up), and then get the component from that. So the rest of the code works fine. Now I can move on and improve it!
I'm new to coding and Unity, I'm working on a simple click style game to learn the basics of both. I've created several scenes: MainMenu, UI, 1st level and 2nd level. After pressing 'Start' in main menu i'm loading UI and 1st level additively.
In UI layer I have a shop UI and other bits that I never want to unload. On the 1st and 2nd level i have the bits that i want to have only on those scenes.
So, what I'm trying to do is when i purchase an item or upgrade for the 1st level i want a GameObject (sprite) to be set as active.
What I've tried to do is to call a function in one script that is attached to GameObject in 1st level from script that is attached to a purchase button in UI scene, but from what I was able to understand from messing with it will set that game object active if it's assigned in the UI scene - so the whole thing is basicly pointless and i can do it much easier.
Code in script attached to GameObject in 1st level scene
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Huberd()
{
hubert12.SetActive(true);
}
}
Code in script attached to GameObject in UI scene
public class Shop : MonoBehaviour
{
public GameObject buyHubertOnebutton;
public GameObject test;
public void UnlockHubert1()
{
if (Global_Cash.CashCount >= 20)
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button>().interactable = false;
Work_Button sn = test.GetComponent<Work_Button>();
sn.Huberd();
}
}
}
If you have any remarks anout how i've spit scenes or anything else they will be more than welcome!
Thanks!
There's something you need to be aware of, and that is that you can't reference an object from one scene, in another. You CAN reference the same Prefab though, but that's a slightly different issue.
One way I find effective is to do something like this:
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Awake ( )
{
Shop.Register ( this );
}
public void Huberd ( )
{
hubert12.SetActive ( true );
}
}
Then you can have a Shop manager that has instance and static components.
public class Shop : MonoBehaviour
{
// Static variables
private static Work_Button _workButton;
// Instance variables
public GameObject buyHubertOnebutton;
public static void Register ( Work_Button workButton )
{
_workButton = workButton;
}
public void UnlockHubert1 ( )
{
if ( Global_Cash.CashCount >= 20 )
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button> ( ).interactable = false;
if ( _workButton != null )
_workButton.Huberd ( );
}
}
}
This works by having your button register with the shop. And because we're using the static variable here, we don't need to "find" the Shop object. The instance method UnlockHubert1 will check to make sure there is a button registered.
This represents just one of many ways to accomplish this. It's also the most basic of basic implementations. But as of right now I'm fond of this method. Extending this, you could store the registered items in a Dictionary collection, and you could do some cleanup/deregister when a button is destroyed (i.e. OnDestroy ).
The assumption here is that there is only one Work_Button in the scene, otherwise you'll need a way to differentiate them (i.e. a Dictionary with am identifier as the key).
So I have this manager in my start scene with DontdesoryOnload, that managers all the UIs, etc. It follows singleton pattern, so if I go to scene 2 and come back to my start-scene, the first manager will remain the same, and the manager in the newly opened start-scene will figure that there's already a manager, and destroy itself.
From here let's call the Manager remains alive Manager-Singleton and the manager that is destroyed as planned Manager-Dead.
The problem I'm having is that the references in my Manager-Singleton seem to false-reference.
When Manager-Dead is destroyed as planned, if I access a public GameObject under my Manager-Singleton, it will show me an error. Where if I click on those References fields in Inspector, it will lead me to the correct Gameobject which not not destoryed at all.
MissingReferenceException: The object of type 'GameObject' has been destroyed, but you are still trying to access it.
However, if I avoid Manager-Dead from being destroyed, (So there will be two managers in one scene), the code worked just fine.
I know you might be thinking, if there are two managers in the scene, there might be a UI overlap so that I might be clicking on ManagerDead's Button, and accessing its References. So after I got back to the Start scene, I manually disable the ManagerDead. And it turns out ManagerSingleton is changing ManagerDead's UI !
I really couldn't figure out where it went wrong. Any suggestions would be appreciated.
Following is some of my codes in case they might be useful:
[RequireComponent(typeof(UIManager))]
[RequireComponent(typeof(DataManager))]
[RequireComponent(typeof(StateManager))]
public class Managers : MonoBehaviour {
private static UIManager _UIManager;
public static UIManager UI
{
get { return _UIManager; }
}
private static DataManager _DataManager;
public static DataManager Data
{
get { return _DataManager; }
}
private static StateManager _StateManager;
public static StateManager State
{
get { return _StateManager; }
}
public string debugString = "";
void Awake(){
//Only one dataControl obj is allowed to exist and pass along.
if (GameObject.FindObjectsOfType<Managers> ().Length > 1) {
Destroy (gameObject);
} else {
DontDestroyOnLoad (gameObject);
}
_UIManager = GetComponent<UIManager> (); //!!!!! This is a Singleton class.
_DataManager = GetComponent<DataManager> ();
_StateManager = GetComponent<StateManager> ();
}
}
Problem solved, the Component referencing part is what's causing the trouble, Since _UIManager etc is static, that means when component referencing is called in Awake function of Manager-Dead, it will refer those reference to the GameObjects Under it even after it's destroyed.
This results in the static _UIManager from Manager-Singleton will be referred to the Destroyed Gameobjects under Manager-Dead.
I am doing a school project. I need to check the destroyed objects in my scene as soon as the scene starts. problem is I don't know how to make it load or where to attach the c# script.
public static class DestroyedObject {
static List<GameObject> objs = new List<GameObject>();
public static void Add(GameObject obj)
{
if(!objs.Contains(obj))
objs.Add(obj);
}
}
If you want it to run when you start the scene you need to attach it to a GameObject. Create empty and attach it as a component.
The code that you want to run on start should be in the:
void Awake
{
//Your code here
}
or
void Start
{
//Your code here
}
functions.
Start is called as soon as the class is instantiated and Awake is called when the scene is started. Depends where you want it in the call stack, but in your case i think it will do essentially the same thing.
I think what you're looking for is a way to "save" what objects have been deleted : you simply have to make your class inherit from MonoBehaviour and call DontDestroyOnLoad() so your object containing the script will exist between the scenes.
public static class DestroyedObject : MonoBehaviour
{
public static DestroyedObject Instance;
private static List<GameObject> objs = new List<GameObject>();
private void Awake()
{
if (!Instance)
{
Instance = this;
}
else
{
DestroyImmediate(gameObject);
}
DontDestroyOnLoad(gameObject);
}
public static void Add(GameObject obj)
{
if(!objs.Contains(obj))
objs.Add(obj);
}
public static List<GameObject> GetDestroyedObjects()
{
return objs;
}
}
Then you simply access your script using DestroyedObject.Instance.Add() or DestroyedObject.Instance.GetDestroyedObjects() (some people don't like this kind of design pattern but it has proven to be very effective when using Unity).
Also as #Sergey asked, why creating objects (on scene loading) in order to delete them afterward : you could do the revers operation (only instantiate the needed ones).
Hope this helps,
Can you describe what you are trying to achieve in total? Because it looks like your way is not the best way to do it ;).
If all you want to know is how to execute a script at scene start: create a script that inherits from MonoBehaviour (no need for static class), attach it to a gameobject in your scene, and thats it!
If you want to execute code as soon as the scene starts (and the gameobject is loaded), put your code in Awake() or Start(). You can read about the execution order of those functions here: https://docs.unity3d.com/Manual/ExecutionOrder.html
Making a script static means it will be active in all scenes and even before any scene is loaded.
Additionally, i would not recommend the use of static classes unless you really need them.
I'm trying to make a button interactable after a certain scene (playing level) has been loaded. It's a button in a menu scene that represent the loaded scene itself (level selectable after playing it).
The problem is this: If the users passes a certain level, say level 1, level 2 gets loaded, and a static method gets called:
public static void AllowTut2()
{
Tut2Allowed = true; //public static bool initialised in this script
tutorial2.interactable = true; //tutorial2 is a button in the "menu scene"
}
To make it clear where the variables come from, this is part of the same script:
public class LevelSelectScript : MonoBehaviour {
public Button tutorial2;
public static bool Tut2Allowed = false;
//...some other variables
void Start ()
{
tutorial2 = tutorial2.GetComponent<Button>();
tutorial2.enabled = false; //more later on
///... some other code
}
}
Now the problem is this error: An object reference is required to access non-static member `LevelSelectScript.tutorial2' (refers to method AllowTut2).
It seems that I cannot change tutorial2.interactable trough the given static method (called in another script).
It basically says the button tutorial2 is non-static, therefore cannot use this in a static method.
Now if I make the button static, i.e. change
public Button tutorial2;
to
public static Button tutorial2;
then I have no way to assign the button object in the scene to this variable in the attached script.
Does someone perhaps know a solution to this problem?
Thanks in advance!
if you want to use static varaibles and also use inspector for assigning you can use singleton , here is where you can learn it