Counting number of clicks - unity3d

I want to use Unity to calculate the number of clicks on the page as well as the number of clicks on a coin that appears every few seconds. Can you tell me how I can get the number of clicks on the page?
each time I want the number of clicks on the coins and the page to be calculated separately.

Its not clear what you have in mind about coin. Its a UI element or a GameObejct.
I suppose its an object with RigidBody and Collider:
public class HitDetector : MonoBehaviour
{
public static int allCounter = 0;
public static int hitCounter = 0;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
allCounter++;
if (IsHit())
{
hitCounter++;
}
Debug.Log($"All: {allCounter} & hit: {hitCounter}");
}
}
private bool IsHit()
{
return Physics2D.Raycast(Camera.current.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
}
}

Related

How do I save the score every time the scene resets and make it my new highscore when the score is higher than the highscore?

I'm currently recreating Jetpack Joyride and I'm having trouble with adding a highscore. I currently track my score on where my player is on the Y position and placing the score on a canvas. So I was wondering how do I save the score every time the score is higher that the highscore and when the scene resets.
This is what I currently use to track Y position of my player:
public class Score : MonoBehaviour
{
public Transform player;
public Text scoreText;
void Update()
{
scoreText.text = player.position.x.ToString("0" + "M");
}
}
There is multiple ways to implement high score.
You can use PlayerPrefs to store data in each scene and then load the data from storage and save again if the latest score is higher than the previous one.
You can create a global object which is not destroyed when new scenes load. In that object, you can attach a high score script that will keep track of the score.
Example Script for the 2nd Option
using UnityEngine;
using System.Collections;
public class MyCustomScript : MonoBehaviour {
public int score = 0;
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("global");
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
// Update is called once per frame
void Update () {
if( score < **getScore()** ){
score = getScore();
}
}
You should be saving that value when your player lose the game look at the comments I added to understand.
public class Score : MonoBehaviour
{
public Transform player;
public Text scoreText;
public Text highScoreText;
public float score;
bool lost;
private void Start()
{
HighScoreCheck();
}
void Update()
{
score = player.position.x;
//this is storing the score
scoreText.text = score.ToString("0" + "M");
highScoreText.text = PlayerPrefs.GetFloat("HighScore").ToString();
//this is showing the highest score recorded
LoseCheck();
}
private void HighScoreCheck()
{
if (!PlayerPrefs.HasKey("HighScore"))
//checking if this key has any value saved to it
{
Debug.Log("No High Score recorded Yet");
}
else
{
Debug.Log("HighScore is : " + PlayerPrefs.GetFloat("HighScore"));
}
}
private void LoseCheck()
{
if (lost)
{
if (score> PlayerPrefs.GetFloat("HighScore"))
{
PlayerPrefs.SetFloat("HighScore", score);
//this is how you save a float/int into a key that is stored in the device
}
else
{
Debug.Log("No new high score");
}
}
}
}

Know How many Buttons of the Joystick Are pressed in unity input unity system

I recently asked a similar question with keyboard, and was able to solve it, now I'm using input unity system to do the same thing but with joystick control.
With this code it detects 1, but when I press a second button it appears as if the first one had stopped pressing it. I would like that if I press 1 it increases to 1 and if I press two it increases to two and so on.
public class JoystickInput : MonoBehaviour
{
ControlOfInputs ControlOfInputsScript;
private InputAction test;
private InputActionReference Actionreference;
public float[] Contadores;
public int BotonesPresionados;
void Awake()
{
ControlOfInputsScript = new ControlOfInputs();
// ControlOfInputsScript.Gameplay.Arriba. += ctx => Fire();
}
private void OnEnable()
{
ControlOfInputsScript.Enable();
}
private void OnDisable()
{
ControlOfInputsScript.Disable();
}
// Update is called once per frame
private void Update()
{
//Boton Arriba
if (ControlOfInputsScript.Gameplay.Arriba.WasPerformedThisFrame())
{
Debug.Log("Presionado");
BotonesPresionados++;
}
if (ControlOfInputsScript.Gameplay.Arriba.WasReleasedThisFrame())
{
Debug.Log("DejoDePresionar");
BotonesPresionados--;
}
//Boton Abajo
if (ControlOfInputsScript.Gameplay.abajo.WasPerformedThisFrame())
{
Debug.Log("Presionado");
BotonesPresionados++;
}
if (ControlOfInputsScript.Gameplay.abajo.WasReleasedThisFrame())
{
Debug.Log("DejoDePresionar");
BotonesPresionados--;
}

How do I you images as a score into my game?

I have been trying to make a game that's a parody off of minesweeper in unity. I have gotten most of the stuff done including the timer. One problem I have been having is the score count. The score count consists of 3 images. What I am looking is a way so that I can change each image based on how many mines have been caught. Here's the code for it:
public int scorecount = 0;
int i = 0;
int a;
public int amountOfMines;
public Image faceImage;
public Sprite sprite;
private Timer timer;
public ItemDrag itemDrag;
public List<Image> scoreCountImages;
public Sprite[] digitSprites;
void Start()
{
timer = GetComponent<Timer>();
}
void Update()
{
if(scorecount >= i)
{
if(scorecount == amountOfMines) //If the score equals the amount of mines, stop timer, change face sprite.
{
Debug.Log("All the Mines have been cleared");
faceImage.sprite = sprite;
timer.isStop = true;
itemDrag.thereAreStillMines = false;
}
}
}
Your question has a simple solution. Easily use multiple if, In the following code, there is a Sprite coordinator with points that you can match the desired Sprites with the desired conditions.
public void SyncSprite()
{
if (score >= amountOfMines) faceImage.sprite = faceSprites[2]; // total mines
else if (score >= 7) faceImage.sprite = faceSprites[1]; // for e.g after 7 score change to second sprite
else if (score >= 0) faceImage.sprite = faceSprites[0]; // default sprite
}
For Example, at the bottom, then click on a box and there is no mine there. The score goes higher and the sync code is executed at the end of the score calculation and syncs the sprite with the score.
public void OnClickedBox()
{
// do something...
if (noMineWasHere) score++;
SyncSprite();
}
TLDR; Your mistake was rendering 3-digits as a whole thing; You should have 3-sprite renderer, each displaying a single digit.
Set in the inspector, an array of Sprites for the digits, ordered properly.You will use the array as a Dictionary to easily access any digit-sprite by a corresponding number:
[SerializeField, Tooltip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
// ......
numberSprites[4] // will output '4' sprite;
Next, rather than have 1 big whole renderer to represent all 3 digits, we can split it into 3 renderer, and expose it to the inspector as an array:
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
Each of this renderer should only render 1 single-digit, something like this:
Now we can easily set-up a loop, to loop through each number in the score, and change the sprite based on the number.
The full result:
[SerializeField, ToolTip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
private int scoreCount;
// Converts the '123' into a {1, 2, 3}
// See: https://stackoverflow.com/questions/829174/is-there-an-easy-way-to-turn-an-int-into-an-array-of-ints-of-each-digit
public int[] NumbersIn(int value) {
var numbers = new Stack<int>();
for(; value > 0; value /= 10)
numbers.Push(value % 10);
return numbers.ToArray();
}
// Call this whenever score is updated
private void UpdateScoreUI() {
int[] arrScore = NumbersIn(scoreCount);
for (int i = 2; i > 0; --i) {
// If the number was say, 11, the list will only be { 1, 1 }
// So we will need to handle the last digit that is out-of-index.
if (arrScore.Length - 1 <= i){
int currNumber = arrScore[i];
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[currNumber];
} else {
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[0];
}
}
}
Edit
Yes, this method works with Image objects as well, since they have a sprite property, and we are only interacting with that property.

Changing card position

I am creating a card game in unity. I have 4 cards and instantiated them randomly 16 times on 4 zones when a button is clicked. Each zone contains 4 random cards. I want to know how can I know which card is in which zone and then change the position of that particular card to another zone.
This is the script attached to each of my card:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Click : MonoBehaviour
{
public GameObject Canvas;
public GameObject MyArea;
private bool isDragging=false;
private bool isOverBottomArea=false;
private GameObject BottomArea;
private GameObject startParent;
private Vector2 startPosition;
private void Awake()
{
MyArea=GameObject.Find("MyArea");
Canvas=GameObject.Find("Canvas");
}
// Update is called once per frame
void Update()
{
if (isDragging)
{
transform.position= new Vector2(Input.mousePosition.x,Input.mousePosition.y);
transform.SetParent(Canvas.transform, true);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
isOverBottomArea=true;
BottomArea=collision.gameObject;
}
private void OnCollisionExit2D(Collision2D collision)
{
isOverBottomArea=false;
BottomArea=null;
}
public void StartDrag()
{
startParent= transform.parent.gameObject;
startPosition=transform.position;
if(startParent != MyArea)
{
isDragging=false;
}
else
{
isDragging=true;
}
}
public void EndDrag()
{
isDragging=false;
if (isOverBottomArea && startParent==MyArea )
{
transform.SetParent(BottomArea.transform, false);
}
else
{
transform.position=startPosition;
transform.SetParent(startParent.transform, false);
}
}
}
And this is the script attached to the button which on clicking instantiate the 16 cards:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawCards : MonoBehaviour
{
public GameObject Card1;
public GameObject Card2;
public GameObject Card3;
public GameObject Card4;
public GameObject MyArea;
public GameObject LeftArea;
public GameObject RightArea;
public GameObject BottomArea;
List<GameObject> cards = new List<GameObject>();
void Start()
{
cards.Add(Card1);
cards.Add(Card2);
cards.Add(Card3);
cards.Add(Card4);
}
int count1 = 0;
public void OnClick()
{
count1++;
if (count1 == 1)
{
for (var i=0; i<4; i++)
{
GameObject playerCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
playerCard.transform.SetParent(MyArea.transform, false);
GameObject leftCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
leftCard.transform.SetParent(LeftArea.transform, false);
GameObject rightCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
rightCard.transform.SetParent(RightArea.transform, false);
GameObject bottomCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
bottomCard.transform.SetParent(BottomArea.transform, false);
}
}
}
}
I would consider storing the values to each of your 4 zones with card data to be utilized later. I do not exactly know what a zone is or what it means if there can be more than 4 cards to a zone, etc.
Here is a very general approach to having zones, cards and a zone manager. I can help clarify any details as to what the code does but I commented it quite extensively. I did not integrate it into your code but rather it should be used as a template by you to do so. The code I am providing is untested and should not just be copy-pasted as your question is still rather general. I can explain anything further if you need.
Card class on each moveable card
// this class is on each of your cards
public class Card : MonoBehaviour,
{
// current index of this card
private int cardIndex = -1;
// stores the current zone this card is in
private Zone currentZone = null;
// init this object with the zone it is in and the card index it has
public void InitCard(Zone zone, int idx)
{
cardIndex = idx;
currentZone = zone;
}
// I am using the same EndDrag - I am assuming you are using an editor component of EventTrigger with an
// OnBeginDrag and OnEndDrag instead of implementing the IHandlers in code
public void EndDrag()
{
// determine if we are over a zone - I am going to use the MouseInput as with a drag,
// you would be dragging the object using the cursor or a finger
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// only look for the layer of the Zone objects
// make sure to add a layer to just the zones and make sure
// it is spelled / case-sensative so exactly the same
int layer_mask = LayerMask.GetMask("ZoneLayer");
// if we hit a zone, determine if it is a new zone and to move the card
if(Physics.Raycast (ray, out hit, Mathf.Infinity, layer_mask))
{
Zone newZone = hit.gameObject.GetComponent<Zone>();
// we hit a zone, is it the same zone?
if(currentZone == newZone)
{
// handle however you want to do a case where it is the same zone
// reset the card position possibly
return;
}
// we are in a new zone, so need to remove this card from the old zone and add it to the new one
// I am not sure how you are handling max card counts in each zone, etc. so this is very basic implementation
currentZone.RemoveCard(this);
newZone.AddCard(this);
// update our local zone reference
currentZone = newZone;
}
}
// setters / getters
public int CardIndex
{
set{cardIndex = value;}
get{return cardIndex;}
}
public Zone CurrentZone
{
set{currentZone = value;}
get{return currentZone;}
}
}
ZoneManager should be on an object that manages your individual zones, so possibly the Canvas object that all of this data is on
// this class will manage each one of your zones
public class ZoneManager : MonoBehaviour
{
// list of our prefabs
[SerializeField] private List<GameObjects> allCardsPrefabList = new List<GameObjects>();
// create a list of zone data of size 4
[SerializeField] private Zone[] myZoneData = new Zone[MAX_ZONE_COUNT];
// max number of zones we can have
private const int MAX_ZONE_COUNT = 4;
// max number of cards we can have on Init
private const int MAX_INIT_CARD_COUNT = 4;
// init our zones in Start() - as I do not know if you are working with UI
// and UI components are only set before Start but not Awake
private void Start()
{
// iterate over each zone
for(int x = 0; x < MAX_ZONE_COUNT; ++x)
{
// iterate over 4 new cards
for(int y = 0; y < MAX_INIT_CARD_COUNT; ++y)
{
// I am using this overload of the Instantiate
// public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
// grab our new idx
int newCardIdx = Random.Range(0, cards.Count);
// spawn a new card childed to our zone at the new card idx
Card newCard = Instantiate(allCardsPrefabList[newCardIdx], myZoneData[x].transform, false).GetComponent<Card>();
// init our card zone / idx
newCard.Init(myZoneData[x], newCardIdx);
// add this card to a zone
myZoneData[x].AddNewCard(newCard);
}
}
}
}
The Zone class should be on each of your individual zones
// this is a script that will house each of your zones and will be used to add cards to your zone
public class Zone : MonoBehaviour
{
private const int MAX_INIT_CARDS = 4;
// list of our current cards of this zone
public List<CardData> zoneCards = new List<CardData>();
// called from when a cast is detected on a drop
public void AddNewCard(Card newCard)
{
zoneCards.Add(newCard);
}
// remove a card instance from our list as it is no longer apart of this zone
public void RemoveCard(Card oldCard)
{
zoneCards.Remove(oldCard);
}
}
Again, this is a general idea, not a straightforward answer. Use it as a template to add to your existing code.

Using a timer in conjunction with 2 push buttons from arduino

So I am using two push buttons (connected to an Arduino Uno) as an input to my game. The player has to push down both buttons at the same time for the character to move in the game. I want the player to hold down the buttons for a different amount of time in each level. I have a working Arduino and a working Unity timer and player script, but am not able to get the code to do what I want. What I basically want is that only when the player presses the buttons down, does the timer start counting down. Right now, the timer starts as soon as the scene begins. I know that I somehow have to reference the timer script to the button object, I have tried this but it still doesn't work. Note that the timer UI does have a Timer tag on it. I have also referenced the Player Controller script in the Timer script. Right now, Its giving me a range of errors. I have attached an image depicting these errors.error image
The Timer script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Timer : MonoBehaviour
{
//int startTime = 0;
public bool buttonPressed = false;
public int timeLeft;
public Text countdownText;
GameObject Character;
void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
//StartCoroutine("LoseTime");
BeginTimer();
}
void Update()
{
countdownText.text = ("Time Left = " + timeLeft);
if (timeLeft <= 0)
{
//StopCoroutine("LoseTime");
//countdownText.text = "Times Up!";
Invoke("ChangeLevel", 0.1f);
}
}
public void BeginTimer()
{
Character.GetComponent<PlayerController>().Update();
//gameObject.GetComponent<MyScript2>().MyFunction();
if (buttonPressed == true )
{
StartCoroutine("LoseTime");
}
else if (buttonPressed == false)
{
StopCoroutine("LoseTime");
}
}
IEnumerator LoseTime()
{
while (true)
{
yield return new WaitForSeconds(1);
timeLeft--;
}
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
The Player Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
public class PlayerController : MonoBehaviour
{
SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
//player == GameObject.FindWithTag("Player").GetComponent<>();
public float Speed;
public Vector2 height;
public float xMin, xMax, yMin, yMax;
public bool buttonPressed = false;
GameObject Character;
public void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
if (!sp.IsOpen)
{ // If the erial port is not open
sp.Open(); // Open
}
sp.ReadTimeout = 1; // Timeout for reading
}
public void Update()
{
if (sp.IsOpen)
{ // Check to see if the serial port is open
try
{
string value = sp.ReadLine();//To("Button"); //Read the information
int button = int.Parse(value);
//float amount = float.Parse(value);
//transform.Translate(Speed * Time.deltaTime, 0f, 0f); //walk
if (button == 0) //*Input.GetKeyDown(KeyCode.Space*/) //jump
{
buttonPressed = true;
Character.GetComponent<Rigidbody2D>().AddForce(height, ForceMode2D.Impulse);
Character.GetComponent<Rigidbody2D>().position = new Vector3
(
Mathf.Clamp(GetComponent<Rigidbody2D>().position.x, xMin, xMax),
Mathf.Clamp(GetComponent<Rigidbody2D>().position.y, yMin, yMax)
);
Timer tmr = GameObject.Find("Timer").GetComponent<Timer>();
tmr.BeginTimer();
}
}
catch (System.Exception)
{
}
}
void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
}
I think the problem may be with how I am referencing the scripts in each other.
In your timer you have a quite strange mixup of Update and Coroutine. Also note that BeginTimer is called exactly once! You also shouldn't "manually" call Update of another component.
I wouldn't use Update at all here. Simply start and stop a Coroutine.
The Timer script should only do the countdown. It doesn't have to know more:
public class Timer : MonoBehaviour
{
public int timeLeft;
public Text countdownText;
private bool timerStarted;
public void BeginTimer(int seconds)
{
// Here you have to decide whether you want to restart a timer
timeLeft = seconds;
// or if you rather want to continue counting down
//if(!timerStarted) timeLeft = seconds;
StartCoroutine(LoseTime());
}
public void StopTimer()
{
StopAllCoroutines();
}
private IEnumerator LoseTime()
{
timerStarted = true;
while (timeLeft > 0)
{
yield return new WaitForSeconds(1);
timeLeft --;
countdownText.text = $"Time Left = {timeLeft}";
}
// Only reached after the timer finished and wasn't interrupted meanwhile
// Using Invoke here is a very good idea since we don't want to interrupt anymore
// if the user lets go of the button(s) now
Invoke(nameof(ChangeLevel), 0.1f);
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
In general avoid to use Find at all. If anyhow possible already reference things in the Inspector! If needed you can use Find but only once! What you never want to do is use any of the Find and GetComponent variants repeatedly - rather store the reference the first time and re-use it - and especially not in Update no a per frame basis. They are very expensive calls!
public class PlayerController : MonoBehaviour
{
public float Speed;
public Vector2 height;
// I prefer to use Vector2 for such things
public Vector2 Min;
public Vector2 Max;
public bool buttonPressed = false;
// Already reference these via the Inspector if possible!
public Rigidbody2D Character;
public Timer timer;
public Rigidbody2D _rigidbody;
private SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
private void Awake()
{
FetchReferences();
}
// This is my "secret" tip for you! Go to the component in the Inspector
// open the ContextMenu and hit FetchReferences
// This already stores the references in the according fields ;)
[ContextMenu("FetchReferences")]
private void FetchReferences()
{
if(!Character)Character = GameObject.FindWithTag("Player"). GetComponent<Rigidbody2D>();
if(!timer) timer = GameObject.Find("Timer").GetComponent<Timer>();
}
private void Start()
{
if (!sp.IsOpen)
{
sp.Open(); // Open
}
sp.ReadTimeout = 1;
}
private void Update()
{
// I wouldn't do the serialport open check here
// your if block simply silently hides the fact that your application
// doesn't work correctly! Rather throw an error!
try
{
string value = sp.ReadLine(); //Read the information
int button = int.Parse(value);
//TODO: Since it isn't clear in your question how you get TWO buttons
//TODO: You will have to change this condition in order to only fire if both
//TODO: buttons are currently pressed!
buttonPressed = button == 0;
if (buttonPressed)
{
Character.AddForce(height, ForceMode2D.Impulse);
// The clamping of a rigidbody should always be done ine FixedUpdate!
// Pass in how many seconds as parameter or make the method
// parameterless and configure a fixed duration via the Inspector of the Timer
timer.BeginTimer(3.0f);
}
else
{
// Maybe stop the timer if condition is not fulfilled ?
timer.StopTimer();
}
}
catch (System.Exception)
{
// You should do something here! At least a Log ...
}
}
private void FixedUpdate()
{
// Here I wasn't sure: Are there actually two different
// Rigidbody2D involved? I would assume you rather wanted to use the Character rigidbody again!
Character.position = new Vector3(Mathf.Clamp(Character.position.x, Min.x, Max.x), Mathf.Clamp(Character.position.y, Min.y, Max.y));
}
// Did you mean OnApplicationQuit here?
private void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
Typed on smartphone but I hope the idea gets clear