I have a UI MENU where the player can set the volume of the game music.
Through a slider it can change the volume. I'm trying to save these values, and retrieves them whenthe game is open again, but without success so far.
When we change the value of the Slider, the sound also changes, but these values are not being saved.
My code looks like this:
Ps: 0 warning in Unity console, and C# answer if possible =]
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class PauseCanvas : MonoBehaviour
{
public Button resumeButton;
public Button backToMainMenuButton;
public Button exitGameButton;
public Canvas gameCanvas;
public Canvas mainMenuCanvas;
public Slider slider;
public float startVolume = 1f;
public float currentVolume;
string newVolume;
void Awake () {
slider.value = GetComponent<AudioSource> ().volume;
currentVolume = slider.value;
}
void Start () {
startVolume = PlayerPrefs.GetFloat (newVolume, 1);
}
void UpdateVolume() {
if (currentVolume < startVolume ) {
PlayerPrefs.SetFloat (newVolume, currentVolume);
PlayerPrefs.Save ();
}
}
void OnEnable ()
{
//Register Button Events
resumeButton.onClick.AddListener (() => buttonCallBack (resumeButton));
backToMainMenuButton.onClick.AddListener (() => buttonCallBack (backToMainMenuButton));
exitGameButton.onClick.AddListener (() => buttonCallBack (exitGameButton));
}
private void buttonCallBack (Button buttonPressed)
{
//Resume Button Pressed
if (buttonPressed == resumeButton) {
Time.timeScale = 1;
//Hide this Pause Canvas
gameObject.SetActive (false);
//Show Game Canvas
gameCanvas.gameObject.SetActive (true);
}
//Back To Main Menu Button Pressed
if (buttonPressed == backToMainMenuButton) {
//Hide this Pause Canvas
gameObject.SetActive (false);
//Show Main Menu Canvas
Score.Inicializar ();
SceneManager.LoadScene ("Menu");
}
//Exit Game Button Pressed
if (buttonPressed == exitGameButton) {
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
void OnDisable ()
{
//Un-Register Button Events
resumeButton.onClick.RemoveAllListeners ();
backToMainMenuButton.onClick.RemoveAllListeners ();
exitGameButton.onClick.RemoveAllListeners ();
}
}
There are 2 problems in your code.
1.UpdateVolume() is not being called anywhere in the script so there is no way to save the Slider value.
2.string newVolume; newVolume is not initialized. So doing PlayerPrefs.SetFloat(newVolume, currentVolume); is thesame as PlayerPrefs.SetFloat(null, currentVolume); which is bad.
The key that is passed in to SetFloat should not be null or empty.
PlayerPrefs.SetFloat("something",currentVolume); or string newVolume = "something"; PlayerPrefs.SetFloat(newVolume, currentVolume); are both fine.
You need a way of detecting when the slider value changes so that you can save the score. You can do with slider.onValueChanged.AddListener(delegate { sliderCallBack(slider); });
Your whole code should like something below.
public class PauseCanvas : MonoBehaviour
{
public Button resumeButton;
public Button backToMainMenuButton;
public Button exitGameButton;
public Canvas gameCanvas;
public Canvas mainMenuCanvas;
public Slider volumeSlider;
public float startVolume = 1f;
public float currentVolume;
string newVolume = "VOLUME_SLIDER"; //Can change to whatever you want as long as it is not null or empty
void Start()
{
//Load volume from prevois saved state. If it does not exist, use 1 as default
volumeSlider.value = PlayerPrefs.GetFloat(newVolume, 1);
currentVolume = volumeSlider.value;
}
void UpdateVolume(float _volume)
{
currentVolume = _volume;
PlayerPrefs.SetFloat(newVolume, _volume);
PlayerPrefs.Save();
}
void OnEnable()
{
//Register Button Events
resumeButton.onClick.AddListener(() => buttonCallBack(resumeButton));
backToMainMenuButton.onClick.AddListener(() => buttonCallBack(backToMainMenuButton));
exitGameButton.onClick.AddListener(() => buttonCallBack(exitGameButton));
//Register Slider Events
volumeSlider.onValueChanged.AddListener(delegate { sliderCallBack(volumeSlider); });
}
private void sliderCallBack(Slider sliderMoved)
{
//Volume Slider Moved
if (sliderMoved == volumeSlider)
{
UpdateVolume(sliderMoved.value);
}
}
private void buttonCallBack(Button buttonPressed)
{
//Resume Button Pressed
if (buttonPressed == resumeButton)
{
Time.timeScale = 1;
//Hide this Pause Canvas
gameObject.SetActive(false);
//Show Game Canvas
gameCanvas.gameObject.SetActive(true);
}
//Back To Main Menu Button Pressed
if (buttonPressed == backToMainMenuButton)
{
//Hide this Pause Canvas
gameObject.SetActive(false);
//Show Main Menu Canvas
//Score.Inicializar();
SceneManager.LoadScene("Menu");
}
//Exit Game Button Pressed
if (buttonPressed == exitGameButton)
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
void OnDisable()
{
//Un-Register Button Events
resumeButton.onClick.RemoveAllListeners();
backToMainMenuButton.onClick.RemoveAllListeners();
exitGameButton.onClick.RemoveAllListeners();
//Un-Register Slider Events
volumeSlider.onValueChanged.RemoveAllListeners();
}
}
Related
I'm trying to use Unity's UI system .
What I want is that I have a button #1 as a card image and I have button 2 as a spade image. First I will click spade image and it's indicator will turn on. After that I will click the card image to make the Spade Icon image disappear. I managed to get variables from script to another script but whatever I do the first click of the Spade Button, the indicator lights up and spadeselected bool changes in the inspector. While spadeselected is true, the first click of the Card image doesn't do a thing. After clicking twice and so on everything works fine. What could cause this problem?
My main script for card button:
public class main : MonoBehaviour
{
private bool ace = false;
spadestatement _spadestatement;
public GameObject spadebutton;
public GameObject spade_icon;
private void Awake()
{
_spadestatement = spadebutton.GetComponent<spadestatement>();
}
private void Start()
{
gameObject.GetComponent<Button>().onClick.AddListener(turnoff);
}
private void turnoff()
{
ace ^= true;
if (_spadestatement.spadeselected == true)
{
spade_icon.SetActive(ace);
}
}
}
My spadestatement script for spade button:
public class spadestatement : MonoBehaviour
{
public bool spadeselected;
public GameObject indicator;
void Start()
{
gameObject.GetComponent<Button>().onClick.AddListener(select);
}
public void select()
{
spadeselected ^= true;
indicator.SetActive(spadeselected);
}
}
And here is a picture to make it more understandable:
When spadeselected is true, the first time you call turnoff() :
private void turnoff()
{
ace ^= true; //first time : after the assignment, ace is true
if (_spadestatement.spadeselected == true)
{
spade_icon.SetActive(ace); // since ace is true, set spade_icon active
}
}
The second time you call turnoff(), ace become false, thus spade_icon is deactive.
If you want to turn on/off the spade_icon, maybe try this one:
private void turnoff()
{
ace ^= true; //first time : after the assignment, ace is true
if (_spadestatement.spadeselected == true)
{
spade_icon.SetActive(!ace); // since ace is true, set spade_icon deactive
}
}
Or, if you don't want to bind the active/deactive behaviour of spade_icon with ace, you can:
private void turnoff()
{
if (_spadestatement.spadeselected == true)
{
spade_icon.SetActive(!spade_icon.activeSelf);
}
}
I have got displayed cards in a scene, they are gameobjects with this script:
public class CardClick : MonoBehaviour
{
DeckBuildManager deckManager;
// Start is called before the first frame update
void Start()
{
deckManager = FindObjectOfType<DeckBuildManager>();
}
// Update is called once per frame
public void AddToDeck()
{
if (deckManager.DeckCards.Count <= 9)
{
if (!deckManager.DeckCards.ContainsValue(this.gameObject.GetComponent<CardValues>().dataCard.cardName))
{
deckManager.DeckCards.Add(this.gameObject.GetComponent<CardValues>().dataCard, this.gameObject.GetComponent<CardValues>().dataCard.cardName);
}
}
}
}
Cards got a button component which use the AddToDeck function when clicked but there are impossible to click.
What can i do to click them?
I use panel to display those cards so its impossible to delete the panel in case it was the error.
resolv
public void ClickOnCard()
{
if (deckManager.DeckCards.Count <= 9)
{
if (!deckManager.DeckCards.ContainsValue(this.gameObject.GetComponent<CardValues>().dataCard.cardName))
{
deckManager.DeckCards.Add(this.gameObject.GetComponent<CardValues>().dataCard, this.gameObject.GetComponent<CardValues>().dataCard.cardName);
}
}
}
There are 3 buttons present. Clicking a button will change its color. The task here is to match every color of the buttons to a shade of green. Now, I have the buttons programmed to cycle through 4 colors, the same palette. It works, of course.
What I want to implement now is, to make a text appear once all the buttons match colors. I'm working on Unity and the c# script is on Visual Studio.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ChangeColor : MonoBehaviour
{
[SerializeField] int count = 0;
[SerializeField] Color[] colorArray;
public void button ()
{
if(count < colorArray.Length)
{
gameObject.GetComponent<Image>().color = new Color(colorArray[count].r, colorArray[count].g, colorArray[count].b);
if(count == colorArray.Length - 1)
{
count = -1;
}
count += 1;
}
}
}
First of all your button method can be simplified a lot.
And then I would use a callback that checks your colors everytime one of the buttons was pressed:
public class ChangeColor : MonoBehaviour
{
[SerializeField] int count = 0;
[SerializeField] Color[] colorArray;
[SerializeField] private Image _image;
public Color CurrentColor => _image.color;
public event Action OnChangedColor;
private void Awake ()
{
if(!_image) _image = GetComponent<Image>();
}
public void button ()
{
var count = (count + 1) % colorArray.Length;
var nextColor = colorArray[Count];
_image.color = nextColor;
OnChangedColor?.Invoke();
}
}
And now in a central controller script you have references to all your buttons ChangeColor components and their according target Color like e.g.
[Serializable]
public class ButtonColorPair
{
public ChangeColor changeColor;
public Color targetColor;
// Returns true if a ChangeColor is referenced and the colors are matching
public bool IsMatching => changeColor && changeColor.CurrentColor == tatgetColor;
}
public class ColorChecker : MonoBehaviour
{
public ButtonColorPair[] buttonColorPairs;
private void Awake ()
{
// Register a callback which is called every time one of the buttons
// has changed the color
foreach(var pair in buttonColorPairs)
{
// It is save to remove a callback even if it wasn't added before
// but it makes sure there is only one single callback
pair.changeColor.OnChangedColor -= HandleChangedColor;
pair.changeColor.OnChangedColor += HandleChangedColor;
// Here you would also set the targetColor if you want to do it via code e.g. like
pair.targetColor = Color.green;
// or to be save e.g. pick a random value from the color array etc
// Maybe this class would even be responsible for telling the buttons which colors are
// available at all
}
}
private void OnDestroy()
{
// As a good practice always remove callbacks as soon as you don't need them anymore
foreach(var pair in buttonColorPairs)
{
pair.changeColor.OnChangedColor -= HandleChangedColor;
}
}
// Finally check if the color matches for all buttons everytime one of them was changed
private void HandleChangedColor ()
{
// This is Linq and require "using System.Linq;" on top of your file
if(buttonColorPairs.All(p => p.IsMatching)
{
Debug.Log("All buttons are matching their target color");
}
else
{
Debug.Log("Nope not all matching yet");
}
// it basically equals doing something like
//bool allMatch = true;
//foreach(var p in buttonColorPairs)
//{
// if(!p.IsMatching)
// {
// allMatch = false;
// break;
// }
//}
//if(allMatch)
//{
// Debug.Log("All buttons are matching their target color");
//}
//else
//{
// Debug.Log("Nope not all matching yet");
//}
}
}
Ill try to explain my issue. I need to do for my school project simple 2D escape room. I've got most of the scripts ready, when I click an item he add to the inventory list, and then I can see it on my inventory bar. The problem is , I want that the buttons of the UI Bar inventory, to know after I click the item , to know what item I've been clicking on. the way I did worked half way because he always give the key item. Ive got State Machine Interface. that inheritance to 2 more scripts (that open or close the other interactive stuff (door drawer). got KeyItems script and Interactable Script and UImanager - Singleton.
is there to it in a generic way?
this is the PlayerCast Script that I add the items I click on to the inventory, thought the button need to be here
public enum item {none , paperclip , key }
public class PlayerCast : MonoBehaviour
{
public Camera cam;
public List<item> inventory;
public item activeItem;
private void Start()
{
inventory = new List<item>();
}
// Update is called once per frame
void Update()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.CompareTag("Interact"))
{
Interactable interactable = hit.collider.gameObject.GetComponent<Interactable>();
interactable.CLickMe(activeItem);
}
else if(hit.collider.CompareTag("Key"))
{
KeyItems hitItem = hit.collider.gameObject.GetComponent<KeyItems>();
hitItem.PickMeUp();
}
}
}
}
public void ButtonSelect() //this is the generic button im trying to do
{
this.activeItem = item.key;
Debug.Log("Clicked");
}
You need event.
public enum ItemTypes { None , Paperclip , Key }
[DisallowMultipleComponent]
public class Foo : MonoBehaviour {
// Action its void delegate
public event System.Action<ItemTypes> Selected;
public ItemTypes ActiveItem { get; private set; }; // property
public void ButtonSelect () {
ActiveItem = ItemTypes.Key;
Selected?.Invoke(ActiveItem); // invoke event
}
}
[DisallowMultipleComponent]
public class YourUIBar : MonoBehaviour {
[SerializeField] private Foo _foo; // link on inspector
private void OnEnable () {
ItemChange(Foo.ActiveItem); // setup current
Foo.Selected += ItemChange; // subscribe to event
}
private void OnDisable () {
Foo.Selected -= ItemChange;
}
private void ItemChange (ItemTypes item) {
// your code
}
}
public void application() // function when the mouse only find the ui canvas button
{
// when the mouse find only canvas button and when clicked then do something else stop
if (Input.GetButton("Fire1"))
{
print("button pressed");
}
else {
print("button released");
}
}
When dealing with the UI system, you should use the IPointerXXXX interfaces:
public class MyClass: MonoBehaviour, IPointerUpHandler{
public void OnPointerUp(PointerEventData eventData)
{
throw new NotImplementedException();
}
}