Clicking button game logic not working properly - c#

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.

Related

How to call a method before another from 2 different scripts?

I am trying to call a method from the script Dice and after that method is executed the value of the variable diceValue will change. This is the value that I want to take and use in the method from the script Levizja.
Levizja.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Levizja : MonoBehaviour
{
public Transform[] lojtaret;
public GameObject zari;
private int numer = 0;
public Dice vleraEzarit;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GetComponent<Rigidbody>().AddForceAtPosition(new Vector3(Random.Range(0, 500), Random.Range(0, 500) * 10, Random.Range(0, 500)), new Vector3(0, 0, 0), ForceMode.Force);
Debug.Log("U hodh zari me numer: " + Dice.getValue());
}
}
}
Dice.cs
using System.Collections.Generic;
using UnityEngine;
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded, thrown;
Vector3 initPosition;
[SerializeField] private static int diceValue;
private static int numriLojtareve;
//public int diceValue;
public DiceSides[] diceSides;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Reset();
RollDice();
}
if (rb.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
rb.useGravity = false;
rb.isKinematic = true;
SideValueCheck();
}
else if (rb.IsSleeping() && hasLanded && diceValue == 0)
RollAgain();
}
void SideValueCheck()
{
diceValue = 0;
foreach(DiceSides side in diceSides)
{
if (side.OnGround())
{
diceValue = side.getValue();
Debug.Log(diceValue + " has been rolled!");
}
}
}
public static int getValue()
{
return diceValue;
}
}
Some of the methods are not included just to address only the issue.
I want to execute the Update method in Dice.cs which will call SideValueCheck method. This way the variable diceValue will be updated. After that I want the Update method in Levizja.cs to execute this way the new value will be stored there.
What happens is the first time I get the value 0 and the next run I get the last value that dice had. So if first time it landed 3 it shows 0. Next time it lands 2 it shows 3 and so on.
You could adjust this in the Script Execution Order and force a certain order of the executions of the same event message type (Update in this case).
However, this won't be enough. You rather want to wait until the dice has a result.
So before/instead of touching the execution order I would rather rethink the code structure and do something else. E.g. why do both your scripts need to check the user input individually?
Rather have one script call the methods of the other one event based. There is also no reason to have things static here as you already have a reference to an instance of a Dice anyway in vleraEzarit
public class Dice : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private DiceSides[] diceSides;
[Header("Debug")]
[SerializeField] private int diceValue;
private bool hasLanded, thrown;
private Vector3 initPosition;
private int numriLojtareve;
// if you still also want to also provide a read-only access
// to the current value
public int DiceValue => diceValue;
// Event to be invoked everytime there is a new valid dice result
public event Action<int> OnHasResult;
private void Awake()
{
if(!_rigidbody)
{
_rigidbody = GetComponent<Rigidbody>();
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Reset();
RollDice();
}
if (_rigidbody.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
_rigidbody.useGravity = false;
_rigidbody.isKinematic = true;
SideValueCheck();
}
else if (_rigidbody.IsSleeping() && hasLanded && diceValue == 0)
{
RollAgain();
}
}
void SideValueCheck()
{
foreach(DiceSides side in diceSides)
{
if (side.OnGround())
{
diceValue = side.getValue();
Debug.Log(diceValue + " has been rolled!");
// Invoke the event and whoever is listening to it will be informed
OnHasResult?.Invoke(diceValue);
// also I would return here since i makes no sense to continue
// checking the other sides if you already have a result
return;
}
}
// I would move this here
// In my eyes it is clearer now that this is the fallback case
// and only happening if nothing in the loop matches
diceValue = 0;
}
}
And then make your Levizja listen to this event and only act once it is invoked like e.g.
public class Levizja : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private Transform[] lojtaret;
[SerializeField] private GameObject zari;
[SerializeField] private Dice vleraEzarit;
private int numer = 0;
private void Awake()
{
if(!_rigidbody)
{
_rigidbody = GetComponent<Rigidbody>();
}
// Attach a callback/listener to the event
// just out of a habit I usually remove it first to make sure it can definitely only be added once
vleraEzarit.OnHasResult -= HandleDiceResult;
vleraEzarit.OnHasResult += HandleDiceResult;
}
private void OnDestroy()
{
// make sure to remove callbacks once not needed anymore
// to avoid exceptions
vleraEzarit.OnHasResult -= HandleDiceResult;
}
// This is called ONCE everytime the dice has found a new result
private void HandleDiceResult(int diceValue)
{
_rigidbody.AddForceAtPosition(new Vector3(Random.Range(0, 500), Random.Range(0, 500) * 10, Random.Range(0, 500)), new Vector3(0, 0, 0), ForceMode.Force);
Debug.Log("U hodh zari me numer: " + Dice.getValue());
}
}
Ideally the diceValue should be returned by the method and Dice should not have a state. If you absolutely need Dice to have a state then it should not be static.

Making a certain Element of an array interactable using a PlayerPrefs float while all of the +1 elements remain locked

So basically I have a Score/Highscore system and I also have an array which contains some of the buttons which change the color of the player's character. So my question is that if I have highscoreCount >= 20, I want a certain button to become interactable.
ScoreManager Script
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public Transform player;
public Text scoreText;
public Text highScoreText;
public float scoreCount;
public float highscoreCount;
public float pointsPerSecond;
public bool scoreIncreasing;
public Button[] CustomizeButtons;
void Start()
{
if (PlayerPrefs.HasKey("Highscore"))
{
highscoreCount = PlayerPrefs.GetFloat("Highscore");
}
int CustomizationButtonReached = PlayerPrefs.GetInt("CustomizationButtonReached", 1);
{
for (int i = 0; i < CustomizeButtons.Length; i++)
{
if (i + 1 > CustomizationButtonReached)
CustomizeButtons[i].interactable = false;
}
}
}
void Update()
{
if (scoreIncreasing)
{
scoreCount += pointsPerSecond * Time.deltaTime;
}
if(scoreCount > highscoreCount)
{
highscoreCount = scoreCount;
PlayerPrefs.SetFloat("Highscore", highscoreCount);
}
scoreText.text = "Score: " + Mathf.Round(scoreCount);
highScoreText.text = "Highscore: " + Mathf.Round(highscoreCount);
}
}
CustomizeColors Script
using UnityEngine;
public class CustomizeColors : MonoBehaviour
{
public Color[] Colors;
public Material Mat;
public void Start()
{
if (PlayerPrefs.HasKey("HeadColor"))
{
Mat.color = Colors[PlayerPrefs.GetInt("HeadColor")];
}
}
public void ChangeColor(int colorIndex)
{
Mat.color = Colors[colorIndex];
PlayerPrefs.SetInt("HeadColor", colorIndex);
PlayerPrefs.Save();
}
}
Essentially this is the code that I have made (in the ScoreManager) to make an array and to disable all of the buttons. I just want to get a specific element to be interactable when highscoreCount >= "number"
public Button[] CustomizeButtons;
void Start()
{
int CustomizationButtonReached = PlayerPrefs.GetInt("CustomizationButtonReached", 1);
{
for (int i = 0; i < CustomizeButtons.Length; i++)
{
if (i + 1 > CustomizationButtonReached)
CustomizeButtons[i].interactable = false;
}
}
}
You could go multiple ways about this, for example storing a reference for that specific button and when the condition you mentioned is true just set that one button to be interactable
[SerializeField] Button button; //Set this in the inspector
public void AddToScore(int score)
{
//Do your score adding stuff
if (highscoreCount >= number)
{
button.interactable = true
}
}

Accessing a variable from player prefs in another scene

I'm managing my HighScore in my scene "Standard Game" by using playerprefs
I need to access my variable HighScore in my other script "SaveTest" which is in a different scene called "Main Menu" So i can destroy objects tagged DestroyUI when a highscore is reached.
Ive been googling for a bit but im not sure what im doing wrong.
Save Test
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SaveTest : MonoBehaviour
{
public ButtonReposition buttonrepositionscript;
void Start()
{
DontDestroyOnLoad(gameObject);
GameObject obj = GameObject.Find("ButtonReposition");
}
// Update is called once per frame
void Update()
{
if (GetComponent<ButtonReposition.highscore <= 100)
Destroy(GameObject.FindWithTag("DestroyUI"));
}
}
HighScore script
public class ButtonReposition : MonoBehaviour
{
public Button prefab;
public GameObject loseObject;
public int count;
public int highscore;
public Text countText;
public Text Highscoretext;
public Image lineimagestandard;
// Use this for initialization
void Start()
{
PlayerPrefs.GetInt("scorePref");
highscore = PlayerPrefs.GetInt("scorePref");
SetHighscoretext();
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);
}
void Update()
{
if (Highscoretext.name == "highscoretext")
{
Highscoretext.text = "highscore" + highscore;
}
PlayerPrefs.SetInt("scorePref", highscore);
}
put this on your Save Test script highscore = PlayerPrefs.GetInt("scorePref");
and that's it.

Unity run code on touch with a UI Image - c#

I'm a new user to Unity3D and need a hand.
I have some code which I want to be activated when the UI image is pressed and to end once the press has been released. The game is going to run on both Android and IOS so needs to be supported by both platforms.
Here is my code:
public class RPB : MonoBehaviour {
public Transform LoadingBar;
public Transform TextIndicator;
[SerializeField] private float currentAmount;
[SerializeField] private float speed;
[SerializeField] private float numSec;
// Update is called once per frame
void Update () {
if (currentAmount < 100) {
currentAmount += speed * Time.deltaTime;
} else if (currentAmount > 100){
currentAmount = 0;
numSec += 1;
}
LoadingBar.GetComponent<Image>().fillAmount = currentAmount /100;
TextIndicator.GetComponent<Text> ().text = ((int)numSec).ToString () + "s";
}
}
How would I be able to do this?
Thank you
In regards to your issue with the touch screen, I would recommend using Unity's UI button system. It works very well with both Android and iOS platforms. I recommend watching these videos to understand how to write a function that the interface will call for you: https://unity3d.com/learn/tutorials/topics/user-interface-ui/ui-button
On another note, running GetComponent<>() every frame is incredibly taxing on a mobile system.
I recommend the following change:
using UnityEngine;
using UnityEngine.UI;
public class RPB : MonoBehaviour {
public Image LoadingBar;
public Text TextIndicator;
// other variables redacted //
void Start () {
LoadingBar = GetComponent<Image>();
TextIndicator = GetComponent<Text>();
}
void Update () {
// other functions redacted //
LoadingBar.fillAmount = currentAmount / 100;
TextIndicator.text = ((int)numSec).ToString () + "s";
}
public void TouchFunction() {
// Do the thing here. //
}
}
I have some code which I want to be activated when the UI image is
pressed and to end once the press has been released
I would have have advised you to use the Button component too but it doesn't have click down and up events. It only has onClick event which means you can only detect when a there is a Button click not when pressed and released.
This can be done with the Image component. Implement IPointerDownHandler and IPointerUpHandler then override the OnPointerDown and OnPointerUp functions. These functions will be called when there is a mouse click and release on your Image. Attach the script to the Image and that should do the job assuming that the current code you have is logically correct.
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class YourScript : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public Transform LoadingBar;
public Transform TextIndicator;
[SerializeField]
private float currentAmount;
[SerializeField]
private float speed;
[SerializeField]
private float numSec;
bool isPressed = false;
public void OnPointerDown(PointerEventData eventData)
{
// someCode();
Debug.Log("Mouse Down");
isPressed = true;
}
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Mouse Up");
isPressed = false;
}
void Update()
{
if (isPressed)
{
someCode();
}
}
void someCode()
{
if (currentAmount < 100)
{
currentAmount += speed * Time.deltaTime;
}
else if (currentAmount > 100)
{
currentAmount = 0;
numSec += 1;
}
LoadingBar.GetComponent<Image>().fillAmount = currentAmount / 100;
TextIndicator.GetComponent<Text>().text = ((int)numSec).ToString() + "s";
}
}

Basic purchase system in Unity3d

In my game exists a opportunity to obtain a COIN, with a certain amount it is possible to release new skins.
Currently the coin score, are being stored correctly.
I have UI canvas where there skins options, I want to know how to do to be purchased these skins if the player has enough coins, or that nothing happens if it does not have enough.
Follow the codes below.
CoinScore
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class BeeCoinScore: MonoBehaviour
{
public static BeeCoinScore instance;
public static int coin = 0;
public int currentCoin = 0;
string highScoreKey = "totalCoin";
Text CoinScore; // Reference to the Text component.
void Awake ()
{
// Set up the reference.
CoinScore = GetComponent <Text> ();
}
void Start(){
//Get the highScore from player prefs if it is there, 0 otherwise.
coin = PlayerPrefs.GetInt(highScoreKey, 0);
}
public void AddBeeCoinScore (int _point) {
coin += _point;
GetComponent<Text> ().text = "Bee Coins: " + coin;
}
void Update ()
{
// Set the displayed text to be the word "Score" followed by the score value.
CoinScore.text = "Bee Coins: " + coin;
}
void OnDisable(){
//If our scoree is greter than highscore, set new higscore and save.
if(coin>currentCoin){
PlayerPrefs.SetInt(highScoreKey, coin);
PlayerPrefs.Save();
}
}
}
Script to add points to CoinScore
using UnityEngine;
using System.Collections;
public class BeeCoin : MonoBehaviour {
public int point;
private float timeVida;
public float tempoMaximoVida;
private BeeCoinScore coin;
AudioSource coinCollectSound;
void Awake() {
coin = GameObject.FindGameObjectWithTag ("BeeCoin").GetComponent<BeeCoinScore> () as BeeCoinScore;
}
// Use this for initialization
void Start () {
coinCollectSound = GameObject.Find("SpawnControllerBeeCoin").GetComponent<AudioSource>();
}
void OnCollisionEnter2D(Collision2D colisor)
{
if (colisor.gameObject.CompareTag ("Bee")) {
coinCollectSound.Play ();
coin.AddBeeCoinScore (point);
Destroy (gameObject);
}
if (colisor.gameObject.tag == "Floor") {
Destroy (gameObject, 1f);
}
}
}
My UI canvas SHOP It's pretty basic, it has 4 images related skins, with price: 100, 200, 300 and 400 coins, 4 buttons to buy below each image, and a button to leave.
C# if possible.
I solve my problem.
On the "buy" button have attached the script BuySkin
And the script BeeCoinScore i added TakeBeeScore,
deleted: if (coin> current Coin) {}, into the void OnDisable
Now it is working perfectly.
BuySkin Script.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class BuySkin : MonoBehaviour {
public int price;
public void OnClick()
{
if (BeeCoinScore.coin >= price) {
BeeCoinScore.coin -= price;
Debug.Log ("New skin added");
}
if (BeeCoinScore.coin < price) {
Debug.Log ("Need more coins!");
}
}
}
BeeCoinScore script.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class BeeCoinScore: MonoBehaviour
{
public static BeeCoinScore instance;
public static int coin = 0;
public int currentCoin = 0;
string totalCoinKey = "totalCoin";
Text CoinScore; // Reference to the Text component.
void Awake ()
{
// Set up the reference.
CoinScore = GetComponent <Text> ();
}
public void Start(){
//Get the highScore from player prefs if it is there, 0 otherwise.
coin = PlayerPrefs.GetInt(totalCoinKey, 0);
}
public void AddBeeCoinScore (int _point) {
coin += _point;
GetComponent<Text> ().text = "Bee Coins: " + coin;
}
public void TakeBeeCoinScore (int _point) {
coin -= _point;
GetComponent<Text> ().text = "Bee Coins: " + coin;
}
void Update ()
{
// Set the displayed text to be the word "Score" followed by the score value.
CoinScore.text = "Bee Coins: " + coin;
}
void OnDisable(){
PlayerPrefs.SetInt(totalCoinKey, coin);
PlayerPrefs.Save();
}
}

Categories