decrease variable by button click C# - c#

I want to decrease a value by clicking a button, but it does not stay at and turns back to the value at the beginning,
here is my code, what should I add or change can somebody help me?
Thanks a lot
public class LifeShow : MonoBehaviour
{
public GameObject Life; //where the variable will be decreased from
public int Lifevalue;
public string text;
public Button returns;
// Start is called before the first frame update
void Start()
{
Lifevalue = 1;
Life.GetComponent<Text>().text = "Life : " + Lifevalue;
}
// Update is called once per frame
// clicking the button and the lifevalue variable decreases
void Update()
{
Button btn = returns.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
void TaskOnClick()
{
Lifevalue-- ;
Life.GetComponent<Text>().text = "Life : " + Lifevalue;
}
}
}

What you can do is change the local method void TaskOnClick() to a public method by putting it outside update and using public keyword and you can add OnClick in unity
now hit the '+' button and drag and drop your object that script attached to. You can then find as this
choose your function and you are ready. You don't need to do this by script.
If you want to restrict it to not go below 0 you can add a If condition in script
if(LifeValue > 0)
{
LifeValue--;
// showing text
}
It will prevent to go below 0.

OnClick + to add listener
Drag listener object to slot (Life)
Choice function from Life script (Modify)
Set argument value 1 / -1 / -99
[DisallowMultipleComponent]
public class Life : MonoBehaviour {
public event System.Action<int> Changed; // event with INT attribute
private int _amouth;
public int Amouth { // this in property
get => _amouth;
// when you set Amouth, then change _amouth and invoke "Changed" event
set {
if (_amouth != value) {
_amouth = value;
Changed?.Invoke(_amouth);
}
}
}
private void Start () {
Amouth = 1;
}
public void Modify (int number) {
Amouth += number;
}
}
[DisallowMultipleComponent]
[RequireComponent(typeof(Text))]
public class LifeView : MonoBehaviour { // Add Component to text object
[SerializeField] private Life _life; // set on inspector window
private Text _text;
public int LineAmouth { get; private set; }
private void OnEnable () {
_text = GetComponent<Text>();
UpdateText(_life.Amouth);
_life.Changed += UpdateText; // subscribes to Life event
}
private void OnDisable () {
_life.Changed -= UpdateText;
}
private void UpdateText (int amouth) {
_text.text = "Life : "+amouth;
}
}

Not really an expert of Mono but, first of all it seems that you are attaching an event handler on every frame, which is not good. The code should be:
public class LifeShow : MonoBehaviour
{
public GameObject Life; //where the variable will be decreased from
public int Lifevalue;
public string text;
public Button returns;
// Start is called before the first frame update
void Start()
{
Lifevalue = 1;
Life.GetComponent<Text>().text = "Life : " + Lifevalue;
Button btn = returns.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
}
// Update is called once per frame
void Update()
{
}
void TaskOnClick()
{
Lifevalue-- ;
Life.GetComponent<Text>().text = "Life : " + Lifevalue;
}
}
Second, it is pretty unclear what's the difference between Lifevalue and the Life GameObject, can you tell?
EDIT: As someone stated in the comments this code is not really enough to give good hints, could you show the Life object's code? That would be pretty helpful.

Related

Function not showing up in OnClick() for unity

I've been trying to get a function to return a number to change the text in a button. However, I can not use the functions I wrote with OnClick(). I won't show up.
Here is the script I've been working with.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class DiceRollerBehavior : MonoBehaviour
{
public int setRandom;
// Start is called before the first frame update
void Start()
{
setRandom = 0;
}
// Update is called once per frame
void Update()
{
}
public int roll()
{
setRandom = Random.Range(1, 10);
Debug.Log("Your new number is:" + setRandom.ToString());
return setRandom;
}
}
The OnClick() should put the roll function in this list but doesn't.
I just need to be able to trigger a function in this script using the OnClick() feature for unity.
To be able to set a callback here it must be public void, not public int.
Also, your code will not change any text. To be able to do this, you need to assign the text label you want to change to a class field. So, it will look like this:
public class DiceRollerBehavior : MonoBehaviour
{
[SerializeField]
private Text _text; // assign this field in the inspector
public int setRandom;
void Start()
{
setRandom = 0;
}
public void OnBtnClick()
{
int rollValue = roll();
_text.text = #"{rollValue}";
}
int roll()
{
setRandom = Random.Range(1, 10);
Debug.Log("Your new number is:" + setRandom.ToString());
return setRandom;
}
}

gaze always execute even after PointExit

I have a problem about my gaze on VR . What I am trying to do is that gaze upon the button I want to select then hide the first gameobject parent then show the second gameobject parent . Now the second gameobject parent will be shown and when I try to gaze upon the back button it will show the first gameobject parent and hide the second gameobject parent . The problem occurs here, when I am trying to nothing and don't gaze on the buttons it automatically show my second gameobject parent and go back to first parent gameobject and hide and show and hide and show always.
public float gazeTime = 2f;
private float timer;
private bool gazedAt;
public Setting setting;
private void Start()
{
}
public void Update()
{
if (gazedAt)
{
timer += Time.deltaTime;
if (timer >= gazeTime)
{
// execute pointerdown handler
ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerDownHandler);
timer = 0f;
}
else
{
return;
}
}
else
{
return;
}
}
public void PointerEnter()
{
gazedAt = true;
Debug.Log("PointerEnter");
}
public void PointerExit()
{
gazedAt = false;
Debug.Log("PointerExit");
}
//Method for going to setting
public void Setting()
{
setting.ActivateSetting();
}
//Method for going back to main menu
public void GoBack()
{
setting.GoBackToMainMenu();
}
Here's how my Setting code is setup
public GameObject setting;
public GameObject back;
public void ActivateSetting()
{
setting.SetActive(false);
back.SetActive(true);
}
public void GoBackToMainMenu()
{
back.SetActive(false);
setting.SetActive(true);
}
What I want is that it will only show the gameobject parent if I gaze upon it.
After calling the click once you reset the timer but you didn't reset gazedAt
=> the Update method did still run the timer and call the click again.
It seems that your PointerExit is not called at all and therefore the button never reset.
Instead of the EventTrigger I would strongly recommend to use the interfaces IPointerEnterHandler and IPointerExitHandler like
public class YourClass : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
//...
public void OnPointerEnter()
{
}
public void OnPointerExit()
{
}
I would actually not use Update at all but prefer a Coroutine. Also don't use the complex call of ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerDownHandler); instead use either Getcomponent<Button>().onClick.Invoke(); or call the method directly
private Button _button;
private void Awake()
{
// Get the reference only once to avoid
// having to get it over and over again
_button = GetComponent<Button>();
}
private IEnumerator Gaze()
{
// wait for given time
yield return new WaitForSeconds(gazeTime);
// call the buttons onClick event
_button.onClick.Invoke();
// or as said alternatively directly use the methods e.g.
setting.ActivateSetting();
}
public void OnPointerEnter()
{
Debug.Log("PointerEnter");
// start the coroutine
StartCoroutine(Gaze());
}
public void OnPointerExit()
{
Debug.Log("PointerExit");
// stop/interrupt the coroutine
StopCoroutine(Gaze());
}
As you can see there is no need at all for the timer and gazedAt values so you can't forget to reset them somewhere. It also avoids that the method is called repeatedly.
If you don't want to use a Button at all you could also add your own UnityEvent like
// add callbacks e.g. in the Inspector or via script
public UnityEvent onGazedClick;
// ...
onGazedClick.Invoke();

Unity 3D Attaching Score Display Script to prefab

I was following Unity 3d tutorial on the Learn Unity website, but here is the thing I wanted to do things a bit differently. It worked out well at start but in the end this turned out to be a bad decision and now I manually need to attach the script to every pickable object.
Here is my code:
Note: What it does is rotate the Pickups and display the score when the pickups collide with player ball.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PickUps : MonoBehaviour {
public Vector3 Rotate;
private int Score;
public Text ScoreGUI;
private void Start()
{
Rotate = new Vector3(0, 25, 0);
Score = 0;
DisplayScore();
}
void Update () {
transform.Rotate(Rotate*Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Ball"))
{
Destroy(this.gameObject);
Score = Score + 1;
DisplayScore();
}
}
void DisplayScore()
{
ScoreGUI.text = "SCORE " + Score.ToString();
}
}
Problem:
It works yes but I need to manually attach the text (under canvas) to every pickup object which is exhausting and not a good thing to do.
What I want To achieve:
Like in the tutorials mostly they use prefabs in this kind of work (I think), problem is I can attach the text to the pickups (objects/biscuits) in the current scene but I cannot drag and attach the text To the prefab of biscuits I made the text just wont attach in its blank for "Text".
You shouldn't change the score Text directly. Use a Controller to make the bridge instead. I would do something like this:
Put this script somewhere in your scene:
public class ScoreManager : Singleton<ScoreManager>
{
private int score = 0;
// Event that will be called everytime the score's changed
public static Action<int> OnScoreChanged;
public void SetScore(int score)
{
this.score = score;
InvokeOnScoreChanged();
}
public void AddScore(int score)
{
this.score += score;
InvokeOnScoreChanged();
}
// Tells to the listeners that the score's changed
private void InvokeOnScoreChanged()
{
if(OnScoreChanged != null)
{
OnScoreChanged(score);
}
}
}
This script attached in the Text game object:
[RequireComponent(typeof(Text))]
public class ScoreText : MonoBehaviour
{
private Text scoreText;
private void Awake()
{
scoreText = GetComponent<Text>();
RegisterEvents();
}
private void OnDestroy()
{
UnregisterEvents();
}
private void RegisterEvents()
{
// Register the listener to the manager's event
ScoreManager.OnScoreChanged += HandleOnScoreChanged;
}
private void UnregisterEvents()
{
// Unregister the listener
ScoreManager.OnScoreChanged -= HandleOnScoreChanged;
}
private void HandleOnScoreChanged(int newScore)
{
scoreText.text = newScore.ToString();
}
}
And in your PickUps class:
void DisplayScore()
{
ScoreManager.Instance.SetScore(Score); // Maybe what you need is AddScore to not
// reset the value everytime
}
A simple singleton you can use (you can find more complete ones on the internet):
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = (T)FindObjectOfType(typeof(T));
if (instance == null) Debug.LogError("Singleton of type " + typeof(T).ToString() + " not found in the scene.");
}
return instance;
}
}
}
But be careful, the singleton pattern can be a shot in the foot if not used correctly. You should only it them moderately for managers.

An Unity3D button appears to be null while trying to disable it

What I'm trying to do is disable the button when the time is up. But I get an error while I set the button to be disable: it appears to be null.
The following script (RightButton.cs) includes a function called DisableButton that is invoked by the main script (TappingEngine.cs).
The statement that hangs inside the DisableButton function is btn.interactable = false;.
/* RightButton.cs */
using UnityEngine;
using UnityEngine.UI;
public class RightButton : MonoBehaviour {
Button btn;
// Use this for initialization
void Start () {
Debug.Log("Right button ready.");
}
public void Tap()
{
Debug.Log("Tap right! tot: " + TappingEngine.te.nTaps);
TappingEngine.te.Tap(TappingEngine.tapType_t.right);
}
public void DisableButton()
{
btn = this.GetComponent<Button>();
btn.interactable = false;
}
}
The following code is a part of the main script (which is quite long). However, someone advised me to post the full version for clarity.
(Focus on the two functions Update () and DisableButtons().)
/* TappingEngine.cs */
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class TappingEngine : MonoBehaviour {
public static TappingEngine te;
public int nTaps = 0;
public int GlobalTotal;
public int RightTaps;
public int LeftTaps;
public string FirstTap;
public int LastTap;
public int MaxChain;
public int FastestTwoTaps;
public int FastestTwoChainedTaps;
public GlobalTotalTaps GlobalTotalTaps_script;
public TotalRightTaps RightTotalTaps_script;
public TotalLeftTaps LeftTotalTaps_script;
public FirstTap FirstTap_script;
public RightButton RightButton_script;
const float TimeSpan = 5F;
public float Chrono;
public bool ChronoStart = false;
public bool ChronoFinished = false;
public float ElapsedPercent = 0F;
public enum tapType_t { right, left };
// Use this for initialization
void Start () {
GlobalTotal = 0;
te = this;
GlobalTotalTaps_script = FindObjectOfType(typeof(GlobalTotalTaps)) as GlobalTotalTaps;
RightTotalTaps_script = FindObjectOfType(typeof(TotalRightTaps)) as TotalRightTaps;
LeftTotalTaps_script = FindObjectOfType(typeof(TotalLeftTaps)) as TotalLeftTaps;
FirstTap_script = FindObjectOfType(typeof(FirstTap)) as FirstTap;
RightButton_script = FindObjectOfType(typeof(RightButton)) as RightButton;
}
// Update is called once per frame
void Update () {
if(ChronoStart) {
ElapsedPercent = (Time.time - Chrono) * 100 / TimeSpan;
if(ElapsedPercent >= 100F) {
DisableButtons();
//SceneManager.LoadScene("Conclusion", LoadSceneMode.Single);
}
}
Debug.Log("Elapsed time: " + (Time.time - Chrono));
}
private void DisableButtons()
{
RightButton_script.DisableButton();
}
public void OnTap() {
Debug.Log("Tap!");
}
public void Tap(tapType_t tapType) {
Manage_GlobalTotalTaps();
if(GlobalTotal == 1) {
Manage_FirstTap(tapType);
ChronoStart = true;
Chrono = Time.time;
}
Manage_LeftRight(tapType);
}
private void Manage_FirstTap(tapType_t tapType)
{
FirstTap = tapType.ToString();
FirstTap_script.FastUpdate();
}
private void Manage_LeftRight(tapType_t tapType)
{
if (tapType.Equals(tapType_t.left))
{
LeftTaps++;
LeftTotalTaps_script.FastUpdate();
}
else
{
RightTaps++;
RightTotalTaps_script.FastUpdate();
}
}
public void Manage_GlobalTotalTaps() {
GlobalTotal++;
GlobalTotalTaps_script.FastUpdate();
}
}
Below are a couple of screenshots that I hope will be useful.
Until now I have not yet figured out where the problem is. Moreover, the script of the button is very similar to that of the various Text objects that represent the statistics (the list on the right side of the screen).
With Text objects it works, with the Button object not.
I asked the same question on Unity3D Forum and I got the solution here.
One the game object where I attached the RightButton.cs there is a variable on line 24 Button btn; that needs to be set to public, so public Button btn;.
This makes appearing a new entry in the Inspector of the Right Button object (see the image below) and so in Unity3D drag and drop the Right Button GameObject into Right Button (Script)'s proper field (Btn in this case).

Clicking button game logic not working properly

In my script whenever you click a "Button" it adds 1 to your score. When you miss the button 10 times total it loads a new scene and you lose the game. When you click the button it resets the total back to 0. The problem im having is that if you miss the button 9 times and then click it on the 10th time it still counts to 10 and it loads the new screen and you lose.
What it should be doing is resetting it back to 0 and you can continue to play.
loseObject sets the amount to 0 when the game begins and when the button is pressed.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
[RequireComponent(typeof(AudioSource))]
public class ButtonReposition : MonoBehaviour
{
public Button prefab;
public GameObject loseObject;
private int count;
public Text countText;
// Use this for initialization
void Start()
{
count = 0;
SetCountText();
float x = Random.Range(325f, -600f);
float y = Random.Range(250f, -450f);
Debug.Log(x + "," + y);
prefab.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, y);
loseObject.GetComponent<Lose>().amount = 0;
}
public void Move()
{
float x = Random.Range(325f, -600f);
float y = Random.Range(250f, -450f);
Debug.Log(prefab.transform.position.x + "," + prefab.transform.position.y);
prefab.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, y);
loseObject.GetComponent<Lose>().amount = 0;
count = count + 1;
SetCountText();
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
}
}
And on get mouse button down it adds 1 to the total and loads the next scene.
using UnityEngine.Audio;
using UnityEngine.UI;
public class Lose : MonoBehaviour
{
public GameObject Button;
public GameObject Target;
public GameObject loseObject;
public int amount;
public ButtonReposition script;
public void ChangeScene(int changeTheScene)
{
SceneManager.LoadScene(changeTheScene);
}
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
amount += 1;
Debug.Log(amount);
if (amount == 10)
{
SceneManager.LoadScene(2);
Destroy(Button);
}
}
}
}
In other to get this right, you have to find a way to determine when the moue button is down and Button is clicked. You are not doing that now. What you are doing now is checking when left mouse Button is clicked with if (Input.GetMouseButtonDown(0)).
This is one of the cases, where using the Image component is better than using the Button component. Unless you really need the Button component here, the Image component should be used.
This can be done with IPointerDownHandler, IPointerUpHandler interface with their OnPointerDown and OnPointerUp functions.
Disable your ButtonReposition script for now. Use the script below and see if that's doing what you expected. If everything is fine you can then enable your ButtonReposition script and remove the unnecessary stuff inside it.
Attach the ButtonDetector to your Image Component:
public class ButtonDetector : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public bool clicked = false;
public void OnPointerDown(PointerEventData eventData)
{
clicked = true;
//Debug.Log("Pointer Down!");
}
public void OnPointerUp(PointerEventData eventData)
{
//Reset
clicked = false;
// Debug.Log("Pointer Up!");
}
// Use this for initialization
void Start()
{
}
}
Your new Lose script:
public class Lose : MonoBehaviour
{
public GameObject Button;
public GameObject Target;
public GameObject loseObject;
public int missedAmount = 0;
public int clickedAmount = 0;
const int MISSED_MAX_AMOUNT = 10;
//public ButtonReposition script;
public void ChangeScene(int changeTheScene)
{
SceneManager.LoadScene(changeTheScene);
}
private ButtonDetector buttonImage;
void Start()
{
GameObject obj = GameObject.Find("ButtonImage");
buttonImage = obj.GetComponent<ButtonDetector>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (buttonImage.clicked)
{
Debug.Log("Mouse Clicked on Button!");
clickedAmount++;
//Reset missedAmount to 0
missedAmount = 0;
}
else
{
Debug.LogWarning("Mouse Click on SOMETHING ELSE");
missedAmount++;
}
if (missedAmount == 10)
{
Debug.LogWarning("GameOver!");
Debug.LogWarning("Missed 10 times in a row!");
SceneManager.LoadScene(2);
//Destroy(buttonImage.gameObject);
}
}
}
}
I think the easiest fix for you would be to make sure your loseObject script executes after your first script. You can do that with the script execution order. If you go to Edit -> Project Settings -> Script Execution Order you can add your loseObject script and draw it below "Default". Then your other script will execute first and set the amount to 0 as you would expect. However, after it gets set to 0, loseObject will still increment by one, so what you actually want to do is set amount to -1.

Categories