In my unity game I have two toggle buttons that are both in a toggle group together. They are buttons that are labeled "On" and "Off" for turning the game music on and off.
The way I have it set up right now makes it behave very strange. If the music is on and I click the "Off" toggle, the music turns off. Then if I hit "On" again, I get a null object reference error and the check mark doesn't show up in the "On" toggle button until I hit "On" a second time and then the sound comes back on. The really weird part is that when it turns back on, a gameobject is created for the musicplayer, of course. When I try to turn it off again, the music stays on and a second musicplayer gameobject is created. I don't understand what is going on.
void Start ()
{
checkWhatsOn();
}
void checkWhatsOn()
{
bool sound = PlayerPrefs.GetInt("Sound Playing", 1) == 1;
//if (sound) soundToggle.isOn = true; //THESE TWO STATEMENTS
//else soundToggle.isOn = false; //ARE CAUSING THE ISSUE!!
}
public void switchSound()
{
print (string.Format("In switch method sound is {0}", PlayerPrefs.GetInt("Sound Playing", 6)));
bool sound = PlayerPrefs.GetInt("Sound Playing", 1) == 1;
if (sound)
{
if (GameObject.FindObjectOfType<MusicPlayer>() != null)
{
PlayerPrefs.SetInt("Sound Playing", 0);
PlayerPrefs.Save();
GameObject.FindObjectOfType<MusicPlayer>().GetComponent<MusicPlayer>().Disable();
}
else print ("Sound wasn't changed, MusicPlayer needs to be null.");
}
else if (!sound)
{
PlayerPrefs.SetInt("Sound Playing", 1);
PlayerPrefs.Save();
if (GameObject.FindObjectOfType<MusicPlayer>() == null)
{
musicPlayer = Instantiate (Resources.Load ("MusicPlayer")) as GameObject;
GameObject.DontDestroyOnLoad(musicPlayer);
}
else print ("Sound wasn't changed, MusicPlayer shouldn't be null.");
}
print (string.Format("After switch method sound is {0}", PlayerPrefs.GetInt("Sound Playing", 7)));
}
In the inspector I have the "On" button call the function above while passing in a 1. When "Off" pressed, it calls the same function with a 0.
9/2/14
I have updated my code using only one toggle button. The toggle button calls a method which checks the PlayerPrefs to see whether the sound should be on or not. I still end up with an issue when I move from one scene into the scene that has the toggle button in it. When I do that, the music stops. It is for some reason calling the method that should only be called when the toggle button is pressed. Any ideas?
9/4/15
After testing what Thaina had told me (code updated above), I narrowed my issue down to the fact that if I try to set my toggle button to on, it automatically calls the function that is attached to the toggle. I only want to have the function called if it is pressed, not when setting it's saved state. Is there a way to do this?
SOLUTION
I ended up just letting the toggle.isOn call the function that it was trying to access and used a static bool to only let it access certain code when I wanted it to. Thaina's answer is what got me far enough to get to that point so credit goes to Thaina.
I suggest you should use GameObject.FindObjectOfType<MusicPlayer>(). So you can be sure that it will get MusicPlayer in the scene
I suspect that your MusicPlayer object is actually named "MusicPlayer " not "MusicPlayer". Anyway get object by its name is bad idea in my opinion. It prone to error
Related
I have four GameObjects in the game I am making and when I click on each one a dialogue box opens.
My problem is that I am using OnMouseDown and the first one always need to be clicked twice. It happens just on the OnMouseDown, I have buttons on the game that work just fine.
public static GameObject selectedClient;
public bool isBuying;
private Manager manager;
private void Start()
{
manager = GameObject.FindObjectOfType(typeof(Manager)) as Manager;
}
private void OnMouseDown()
{
if (isBuying == true)
{
selectedClient = this.gameObject;
manager.Talk();
}
else if (isBuying == false)
{
manager.NotTalk();
}
}
The Talk() and NotTalk() are as follows:
public void Talk()
{
dialogueBox.SetActive(true);
Time.timeScale = 0f;
actualClient = Client.selectedClient;
}
public void NotTalk()
{
dialogueBox.SetActive(false);
Time.timeScale = 1f;
}
Someone knows why this happens?
Thanks in advance.
Assuming manager.Talk() opens the dialog, perhaps isBuying is false initially, and is toggled when you click once?
Edit: User updated question with Talk and NotTalk methods.
Since your say that initially, only the time stops, but the dialogueBox doesn't show, by any chance, is this because it isn't populated, or correctly initialized? Can you show is code of what your expect to show up?
It may just be that dialogueBox has nothing to display, and therefore, doesn't, however, by the second click, it somehow it's correctly populated and can display.
If I understand correctly, it seems that OnMouseDown() and Talk() is being called successfully on the first mouse click, but a second mouse click is required to get the dialogueBox to show.
If dialogueBox.SetActive(true) is being called successfully, you could take a look at the Unity Scene Hierarchy window to see if it became active like you expect after the first mouse click. If it did, then you can examine that object to see why it's not appearing on screen. (As another poster mentioned, it may be related to dialogueBox initialization.)
Also, these questions might help us debug and figure out the issue:
What error do you get when you "click on one object and then on other"?
What happens if you click the buttons in a different order? If there are 4 buttons, is it the first button that is always affected, or the first button that you click?
In general, as other posters have suggested, using Debug.Log and Debug.Assert should help you narrow down the issue. Similarly, you can set breakpoints in the code and step through to see if the code is running like you expect. And as I mentioned in this post, the Unity Scene Hierarchy window is useful for debugging as well.
Add a Debug.log(isBuying); above (isBuying == true).
Then you'll know if the OnMouseDown() is being called in the first place and if it proceeds like you expect it to. After that, you should probably be able to fix it yourself, otherwise, please let me know what you found.
In case of a Unity mobile app, would it be good (at all) for performance, to deactivate the navigation through canvas objects like TextFields and Buttons?
When selecting e.g. a Button or TextField in the sceneview, the inspector shows the option "Navigation", which can be set to different things and can also be visualized. This feature is usually used to tab through input fields, like you would expect it on a website. On my mobile game I don't need this "tabbing". Would there be any sort of performance increase if I deactivated it everywhere?
Would deactivating this rather be a waste of time, or even make
performance worse for some obscure reason?
Usually there are 3 ways to show and hide UI. I will list them from worst to best.
1.Instantiate (Show), Destroy (Hide).
public GameObject uiPanelPrefab;
void Start()
{
//Show
Instantiate(uiPanelPrefab);
//Hide
Destroy(uiPanelPrefab);
}
This creates and destroys Objects each time. (Not Recommended).
2.Activate (Show) or Deactivate (Hide) the GameObject of that UI.
public GameObject uiPanelPrefab;
void Start()
{
//Show
uiPanelPrefab.SetActive(true);
//Hide
uiPanelPrefab.SetActive(false);
}
This creates garbage each time it is set to active and also cause quick freezes. You will notice this a lot when making VR apps. Fine on Desktops. Not good or recommended on mobile devices.
3.Enable (Show) or Disable (Hide) the Component of that UI.
public GameObject uiPanelPrefab;
void Start()
{
//Show
uiPanelPrefab.GetComponent<Image>().enabled = true;
//Hide
uiPanelPrefab.GetComponent<Image>().enabled = false;
}
This simply enables and disables the components. This method requires a custom function for each UI Control such as Button, Text, InputField as each Control has different components attached to them. This is the recommended method especially on mobile devices.
After long experiment with these, I came to conclusion that #3 is the best and should be used.The downside is that you have to make a function that will disable every components of each UI Control.
For example, the Button Control has more than 1 components. Simply disabling the Button component will not do it. You have to write a simple function that will disable Button, Image and Text components. Something like below:
void showButton(GameObject button, bool show, bool includeInactive = false)
{
Button bt = button.GetComponentInChildren<Button>(includeInactive);
bt.enabled = show;
Text txt = button.GetComponentInChildren<Text>(includeInactive);
txt.enabled = show;
Image img = button.GetComponentInChildren<Image>(includeInactive);
img.enabled = show;
}
Usage:
//Show
showButton(uiPanelPrefab, true);
//Hide
showButton(uiPanelPrefab, false);
I can confirm, that deactivating the navigation of UI elements has no performance influence. Behind the scenes there's just a handler, that gets triggered, when you hit "tab" on pc, or analogeously on mobile.
Looking at some of the answers in the Unity forums and Q&A site, the answers for how to make an invisible button do not work because taking away the image affiliated with the button makes it not work.
How do you get around this and keep the invisible property while allowing the button to actually work?
This is one of those weird things about Unity...
100% of real-world projects need this, but Unity forgot to do it.
Short version:
You need Touchable.cs in every Unity project:
// file Touchable.cs
// Correctly backfills the missing Touchable concept in Unity.UI's OO chain.
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Touchable))]
public class Touchable_Editor : Editor
{ public override void OnInspectorGUI(){} }
#endif
public class Touchable:Text
{ protected override void Awake() { base.Awake();} }
Use Unity's ordinary 'Create Button' editor function
As you know, the editor function adds two components for you automatically. One is a Text and one is an Image...
Simply delete them both
Drop the above script Touchable.cs on the Button
You are done. That's all there is to it.
It cannot "decay" with Unity upgrades.
You can actually "buttonize" anything in .UI by dropping Touchable on top of it.
Never again "add a transparent Image" to make a button.
Unity forgot to abstract a "touchable" concept in the OO chain.
So, us developers have to make our own Touchable class "from" Unity's classes.
This is a classic "backfilling" problem in OO.
When "backfilling" the only issue is that: it must be perfectly auto-maintaining. There is only one good solution, Touchable.cs, which everyone uses.
So in all real-world Unity projects a button looks like this:
ONE You have Unity's Button.cs
TWO you have to add Touchable.cs
Some teams make an editor function "Create Better Button" which simply makes a game object, with, Button.cs + Touchable.cs.
Important tip...
Say you may have a very complex UI panel. So it resizes or even has an animation.
In fact, you can just drop "Button+Touchable" on to anything like that, and it will work.
Just set the Button+Touchable so as to expand to fill the parent. That's all there is to it.
In this example image, "resume" and "quit" could be anything. (An animation, a complicated panel with many parts, text, sprites, something invisible, a stack - anything.)
In all cases, just drop a Button+Touchable underneath and you have a flawless button.
In fact: this approach is so simple, you'll probably use it for even simple cases.
Say your button is a trivial image. It's much easier to just have an image, and then drop a Button+Touchable on it. (Rather than use the confusing and problematic "Button" function in the editor.)
Understanding the situation...
1) Unity's Button.cs class is fantastic.
2) But the editor function "make a Button" is garbage...
3) It makes an "upside down" button,
4) i.e., it puts a text/image under Button.cs
5) "Button-ness" is something you should be able to add to anything at all. This is precisely how it works with Button+Touchable.
6) So - quite simply -
1. Have anything you want. Text, image, panel, invisible, animation - whatever.
2. Drop Button+Touchable on it - you're done.
That's how everyone does all buttons in Unity!
Historic credit: I believe Unity forum user "signalZak" was the first to think this out many, many years ago!
As a possible improvement to Fattie's answer, changing Touchable's base class to Graphic and overriding protected void UpdateGeometry() seems to work quite nicely white reducing the (admittedly minor) overhead associated with Text.
public class Touchable:Graphic
{
protected override void UpdateGeometry() { }
}
My first solution was to enable and disable the components like below:
void showButton(Button buttonToShow, bool show)
{
Image bImage = buttonToShow.GetComponent<Image>();
Text bText = buttonToShow.GetComponentInChildren<Text>(); //Text is a child of the Button
if (bImage != null)
{
bImage.enabled = show;
}
if (bText != null)
{
bText.enabled = show;
}
}
but that didn't work. If the button's image and text components are both disabled, the button click event will NOT fire. One of them MUST be enabled in able for click events to be sent.
The solution is to set the alpha of both the image and text components to 0 to hide and to 1 to show again. They will be hidden but not disabled and click events will work.
public Button button;
void Start()
{
//Show Button
showButton(button, true);
//Hide Button
//showButton(button, false);
}
void showButton(Button buttonToShow, bool show)
{
Image bImage = buttonToShow.GetComponent<Image>();
Text bText = buttonToShow.GetComponentInChildren<Text>(); //Text is a child of the Button
if (bImage != null)
{
Color tempColor = bImage.color;
if (show)
{
tempColor.a = 1f; //Show
bImage.color = tempColor;
}
else
{
tempColor.a = 0f; //Hide
bImage.color = tempColor;
}
}
if (bText != null)
{
Color tempColor = bText.color;
if (show)
{
tempColor.a = 1f; //Show
bText.color = tempColor;
}
else
{
tempColor.a = 0f; //Hide
bText.color = tempColor;
}
}
}
I fired up Gimp (that free coder graphic tool). Created new image (any size, I chose 10 pix x 10 pix), selected from advanced (in create dialog) that it's backgroud should be transparent. Saved the file. Exported it as png with save backgroud color selected. Dragged it into Unity as sprite. Put that to the button graphic. Disbaled the text-component of the button. No code required ... just don't draw anything while in Gimp (that was the hardest part).
I've been struggling with what appears to be a simple task for the past few hours. I have been successfully able to turn a GameObject's guiTexture on an off using GameObject.guiTexture.enabled as I have read in other similar questions. I do this in a script that is attached to the GameObject I wish to toggle.
The problem arises when I try to preform this same command from another script that is attached to an empty game object in my scene. When I interact with the button, simply nothing happens.
To give this some context, I am trying to create functionality to switch menus on the screen. When a user taps a certain UI button, I want to call unloadMenu() so I can wipe everything off the screen and draw the textures that belong to the appropriate menu.
//When the user lifts their finger
void OnTouchEnded ()
{
switch (this.gameObject.name)
{
//If they have selected the quiz button
case "QuizButton":
//this.guiTexture.enabled = false;
//Call the unloadMenu function which will clear the GUITextures
menuMgr.unloadMenu();
break;
The commented out section is what works fine. But now, I wish to perform this code in a function that is located in another script - menuMgr.unloadMenu()
This is what the MenuManager script looks like:
public GameObject quizButton;
void unloadMenu()
{
quizButton.guiTexture.enabled = false;
}
I have ensured to drag the quizButton gameobject from my scene into the inspector and connect it properly with the variable I have created yet nothing happens when this function is called. Does anyone have any idea why? Thanks in advanced.
How you declare MenuManager creates a new script while the reference to quizButton is in your MenuManager GameObject. That is why your menuMgr.UnloadMenu didn't do anything.
I have created something similar to yours and I found it worked fine :
Quizbutton Script:
public GameObject menuMgr; // reference to the empty game object (menu manager)
void OnMouseDown() // In your case, this is OnTouchEnded
{
// get the script of menu manager here
MenuMgrScript menuscript = menuMgr.GetComponent<MenuMgrScript>();
// and execute the function you want
menuscript.UnloadMenu();
}
Menu Manager Script:
// this script is exactly the same as yours, nothing's wrong here
// oh except add public to function UnloadMenu, so it can be called
// in the quizbutton script
public GameObject quizButton;
public void UnloadMenu() {
quizButton.guiTexture.enabled = false;
}
I have a Windows Phone 8 solution with the HTML5 template. My game is in a game.js file and I have a script in the webbrowser's body tag implementing my game. The game works as it should.
However, I have some problems with the hardware back button in Windows Phones. It closes my application, and I have tried overriding it, but I need to detect when the user is actually playing or is already in the menu screen, so I can let the app decide whether to go back to the menu or close the app.
I am thinking about using a global variable in my javascript/HTML5 game:
var IsPlaying = false
when the user clicks the start button on the main menu, IsPlaying becomes true.
When the user clicks the hardware back button, I want to evaluate the value of IsPlaying on my override method:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
bool IsPlaying = //Get value from game
if (IsPlaying == true)
{
Browser.Navigate(new Uri(MainUri, UriKind.Relative)); //Restarts the game, thus bringing the user to the main menu
e.Cancel = true; //Cancels the default behavior.
}
}
So my question is, how can I retrieve the value of IsPlaying from the game?
Thanks
You can use the WebBrowser.InvokeScript method to execute a Javascript function from your C# code and retrieve the return value. So you can just write a function in your Javascript to return the value of the IsPlaying variable, then retrieve the value on the C# side by using InvokeScript.