In my game I want to have a timer start when a user left clicks. Here is the code I have so far:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Countdown : MonoBehaviour
{
float timeLeft = 30.0f;
public Text text;
public Text scoretext;
public Text finalscore;
public AudioSource ping;
public GameObject ball;
// Use this for initialization
void Start ()
{
finalscore.text = "";
}
void countdownfunction()
{
timeLeft -= Time.deltaTime;
text.text = "Time Left: " + Mathf.Round(timeLeft) + " seconds";
}
// Update is called once per frame
void Update ()
{
countdownfunction();
if (timeLeft < 0)
{
ping = GetComponent<AudioSource>();
text.text = "Time's up!";
ping.Play();
ball.SetActive(false);
finalscore.text = "Final score ^";
}
}
}
As you can see, a timer starts as soon as the game starts, but I'd like to have it start when a user left-clicks, please let me know if there's a way to do this, thanks.
It's good to separate your timer from the Update function. Make into another function. Coroutine is perfect for this kind of stuff because you can easily use it to wait for some time then resume operation. Also, cache components if you are going to use them more than once. You will will be using the ping variable a lot so it makes sense to cache is in the Awake or Start function.
void Start()
{
finalscore.text = "";
ping = GetComponent<AudioSource>();
}
void Update()
{
//Check if left mouse button is clicked
if (Input.GetMouseButton(0))
{
StartCoroutine(startTimer(30));
}
}
IEnumerator startTimer(float timeLeft)
{
while (timeLeft > 0)
{
timeLeft -= Time.deltaTime;
text.text = "Time Left: " + Mathf.Round(timeLeft) + " seconds";
yield return null;
}
text.text = "Time's up!";
ping.Play();
ball.SetActive(false);
finalscore.text = "Final score ^";
}
In your Update Function, use the Input.GetMouseButtonDown(0) function to check if user left clicked.
Your code will look like:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Countdown : MonoBehaviour
{
float timeLeft = 30.0f;
public Text text;
public Text scoretext;
public Text finalscore;
public AudioSource ping;
public GameObject ball;
bool timerStarted = false;
// Use this for initialization
void Start ()
{
finalscore.text = "";
}
void countdownfunction()
{
timeLeft -= Time.deltaTime;
text.text = "Time Left: " + Mathf.Round(timeLeft) + " seconds";
}
// Update is called once per frame
void Update ()
{
if(Input.GetMouseButtonDown(0))
timerStarted = true;
if(timerStarted)
countdownfunction();
if (timeLeft < 0)
{
ping = GetComponent<AudioSource>();
text.text = "Time's up!";
ping.Play();
ball.SetActive(false);
finalscore.text = "Final score ^";
}
}
}
You need to use the onMouseDown event
Add the following to your existing code:
Create a boolean variable, set it to false, make it true onMouseDown and start timer only if it's set to true:
private bool mouseClicked=false;
void OnMouseDown(){
mouseClicked = true;
}
void Update () {
if (mouseClicked){
countdownfunction();
}
if (timeLeft < 0)
{
ping = GetComponent<AudioSource>();
text.text = "Time's up!";
ping.Play();
ball.SetActive(false);
finalscore.text = "Final score ^";
}
}
Related
I am currently working on a videogame and a problme I'm currently running into is the fact that when I'm testing the game I often get double or triple increases in the score when I catch one Star. Does anyone know why this might be happening? Below you will find the script that handles score increases. Thanks in advance
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StarCollision : MonoBehaviour
{
int Score;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("White Ball")) // Change this from an update method that runs every frame to a method that only runs when things change (Score script score display method)
{
Score = ScoreScript.scoreValue;
ScoreScript.scoreValue += 1;
StartCoroutine(ChangeColor());
Score = ScoreScript.scoreValue;
if (Score == ScoreScript.scoreValue)
{
Debug.Log("My instance: " + GetInstanceID());
Debug.Log("Other instance: " + other.gameObject.GetInstanceID());
}
}
}
private IEnumerator ChangeColor()
{
ScoreScript.score.color = Color.yellow;
yield return new WaitForSeconds(0.1f);
ScoreScript.score.color = Color.white;
gameObject.SetActive(false);
}
}
The score should only increase by 1 for each star that is catched
One option is to disable collision on the star immediately after a collision with the ball occurs. To use this on a pooled star, you would need to re-enable the collision when the star is enabled again:
Collider2D myCollider;
private void Awake()
{
myCollider = GetComponent<Collider2D>();
}
private void OnEnable()
{
myCollider.enabled = true;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("White Ball"))
{
// Disable this collider immediately to prevent redundant scoring, sound cues, etc.
myCollider.enabled = false;
ScoreScript.scoreValue += 1;
StartCoroutine(ChangeColor());
}
}
If you decide you need collision on the star while the coroutine is occurring, you can add a field to StarCollision that ensures that the score will only increase once. For a pooled star, again, you would need to ensure that it is reset in OnEnable:
private bool alreadyScored = false;
private void OnEnable()
{
alreadyScored = false;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("White Ball"))
{
if (!alreadyScored)
{
ScoreScript.scoreValue += 1;
StartCoroutine(ChangeColor());
alreadyScored = true;
}
}
}
So, I have an object. When I press Spin Button, I want it to spin. When I press Stop button, I want it to stop.
It spins fine when its in void Update, but when its in its own function, it does it just once. I tried using loop but still no luck. Can anyone help me please?
Code C#:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class spin : MonoBehaviour
{
public float speed = 500f;
public Button starter;
public Button stopper;
int testing = 200;
void Start () {
Button btn = starter.GetComponent<Button> ();
Button butn = stopper.GetComponent<Button> ();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
void FidgetSpinnerStart ()
{
for (int i = 0; i < testing; i++) {
transform.Rotate (Vector3.up, speed * Time.deltaTime);
Debug.Log ("Test: " + i);
}
}
void FidgetSpinnerStop ()
{
transform.Rotate (Vector3.up, Time.deltaTime);
}
}
Thanks in advance!
The for loop isn't working as expected because you are not waiting for a frame. Basically, it will do all the spinning in one frame and you won't see the changes until the final spin. Waiting for a frame can the done with yield return null; and that requires a coroutine function.
This is better done with a coroutine. You can use boolean variable with a coroutine or you can just use StartCoroutine and StopCoroutine. Start coorutine that spins the Object when the start Button is clicked and then stop the coroutine when the stop Button is clicked.
public float speed = 500f;
public Button starter;
public Button stopper;
bool isSpinning = false;
IEnumerator spinnerCoroutine;
void Start()
{
//The spin function
spinnerCoroutine = spinCOR();
Button btn = starter.GetComponent<Button>();
Button butn = stopper.GetComponent<Button>();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
IEnumerator spinCOR()
{
//Spin forever untill FidgetSpinnerStop is called
while (true)
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
//Wait for the next frame
yield return null;
}
}
void FidgetSpinnerStart()
{
//Spin only if it is not spinning
if (!isSpinning)
{
isSpinning = true;
StartCoroutine(spinnerCoroutine);
}
}
void FidgetSpinnerStop()
{
//Stop Spinning only if it is already spinning
if (isSpinning)
{
StopCoroutine(spinnerCoroutine);
isSpinning = false;
}
}
Following is a simple class that start and stops spinning an object using two buttons, I hope it makes a starting point of what you are trying to achieve.
public class TestSpin : MonoBehaviour
{
public float speed = 500f;
public Button starter;
public Button stopper;
bool IsRotating = false;
void Start()
{
Button btn = starter.GetComponent<Button>();
Button butn = stopper.GetComponent<Button>();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
void FidgetSpinnerStart()
{
IsRotating = true;
}
void FidgetSpinnerStop()
{
IsRotating = false;
}
void Update()
{
if (IsRotating)
transform.Rotate(0, speed, 0);
}
}
So, I have an object. When I press Spin Button, I want it to spin. When I press Stop button, I want it to stop.
It spins fine when its in void Update, but when its in its own function, it does it just once. I tried using loop but still no luck. Can anyone help me please?
Code C#:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class spin : MonoBehaviour
{
public float speed = 500f;
public Button starter;
public Button stopper;
int testing = 200;
void Start () {
Button btn = starter.GetComponent<Button> ();
Button butn = stopper.GetComponent<Button> ();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
void FidgetSpinnerStart ()
{
for (int i = 0; i < testing; i++) {
transform.Rotate (Vector3.up, speed * Time.deltaTime);
Debug.Log ("Test: " + i);
}
}
void FidgetSpinnerStop ()
{
transform.Rotate (Vector3.up, Time.deltaTime);
}
}
Thanks in advance!
The for loop isn't working as expected because you are not waiting for a frame. Basically, it will do all the spinning in one frame and you won't see the changes until the final spin. Waiting for a frame can the done with yield return null; and that requires a coroutine function.
This is better done with a coroutine. You can use boolean variable with a coroutine or you can just use StartCoroutine and StopCoroutine. Start coorutine that spins the Object when the start Button is clicked and then stop the coroutine when the stop Button is clicked.
public float speed = 500f;
public Button starter;
public Button stopper;
bool isSpinning = false;
IEnumerator spinnerCoroutine;
void Start()
{
//The spin function
spinnerCoroutine = spinCOR();
Button btn = starter.GetComponent<Button>();
Button butn = stopper.GetComponent<Button>();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
IEnumerator spinCOR()
{
//Spin forever untill FidgetSpinnerStop is called
while (true)
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
//Wait for the next frame
yield return null;
}
}
void FidgetSpinnerStart()
{
//Spin only if it is not spinning
if (!isSpinning)
{
isSpinning = true;
StartCoroutine(spinnerCoroutine);
}
}
void FidgetSpinnerStop()
{
//Stop Spinning only if it is already spinning
if (isSpinning)
{
StopCoroutine(spinnerCoroutine);
isSpinning = false;
}
}
Following is a simple class that start and stops spinning an object using two buttons, I hope it makes a starting point of what you are trying to achieve.
public class TestSpin : MonoBehaviour
{
public float speed = 500f;
public Button starter;
public Button stopper;
bool IsRotating = false;
void Start()
{
Button btn = starter.GetComponent<Button>();
Button butn = stopper.GetComponent<Button>();
butn.onClick.AddListener(FidgetSpinnerStop);
btn.onClick.AddListener(FidgetSpinnerStart);
}
void FidgetSpinnerStart()
{
IsRotating = true;
}
void FidgetSpinnerStop()
{
IsRotating = false;
}
void Update()
{
if (IsRotating)
transform.Rotate(0, speed, 0);
}
}
what i am doing is on my level the timer counts down from 50 to 0 using Time.deltaTime this works great. now on the PlayerControl Script when i pickup a coin i want the timer to Add on 3 seconds. My code is below.
//this is PlayerControl when i pick up PlusOrb Object.
public class PlayerCTRL : MonoBehaviour
{
public Text timerText;
public TimerCount timerScript;
public Image DamagedOverlay;
public float speed;
public Text CountText;
private int count;
public GameObject TakeDamage;
public Text scoreText;
public TEMPLE_score scoreScript;
public TEMPLE_GlobalSettings globalSettings;
void Start()
{
CountText.text = CoinCounter.ToString();
playerLife = 3;
count = 0;
SetCountText();
playerAS = GetComponent<AudioSource>();
timerScript = GetComponent<TimerCount>();
damaged = false;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("PlusOrb"))
{
Destroy(other.gameObject);
count = count + 1;
SetCountText();
//score
scoreScript.myscore += 2;
scoreText.text = scoreScript.myscore.ToString();
//timer
timerScript.timer + 3f;// this is the problem i am having
PlayerPrefs.SetInt("CoinCount", PlayerPrefs.GetInt("CoinCount") + 1);
}
}
}
//This is the Timer Script.
public class TimerCount : MonoBehaviour
{
public Text TimeText;
public float timer1 = 50;
float SceneTimer = 0;
TEMPLE_GlobalSettings globalSettings;
public Sprite lives0;
public GameObject Gore;
public PlayerCTRL PlayerController;
int ouch;
void Start()
{
timer1 = 50;
}
void Update()
{
this.GetComponent<Text>().text = timer1.ToString("F0");
timer1 -= Time.deltaTime;
print(timer1);
if (timer1 < 1)
{
timer1 = 0;
PlayerController.playerLife = 0;
SceneTimer += Time.deltaTime;
//if (SceneTimer > 2)
//{
//SceneManager.LoadScene("TEMPLE");
//}
}
}
void GameOver()
{
GameObject thisGore = Instantiate(Gore, transform.position, transform.rotation) as GameObject;
thisGore.GetComponent<ParticleSystem>().Play();
GameObject.Find("Lives").GetComponent<Image>().sprite = lives0;
Destroy(gameObject);
}
}
Replace
timerScript.timer + 3;// this is the problem i am having
with
timerScript.timer += 3f;
Or
timerScript.timer = timerScript.timer + 3f;
This is a basic C# stuff. You should learn C# before working with Unity. There are many of tutorials out there.
EDIT:
With the updated script in your question, there is no variable named timer in your TimerCount script. A similar variable name in your TimerCount script is named timer1. You should either rename it to timer or timerScript.timer1 += 3f;.
At the end of the day, this means that you need to learn basic C#. Please don't take this as an insult. It necessary to understand basic C# or you will be asking more similar questions like this one. You are accessing a variable is is not declared.
I build the shooting game by C# and Unity.
I use GameController and GameStatus to show the score and time.
At first scene, I have no problem. It can run smoothly.
But the second scene, I copy scene from first and make new GameController for second scene.
It's work but running game slower.
I try to make new project by using same code, but it's slow even it's my first scene.
I don't know cause of this happen.
Below is my code, it's work.
using UnityEngine;
using System.Collections;
public class MyGameController2 : MonoBehaviour
private Gun gun;
public GUISkin mySkin2;
private GameStatus gameStatus;
public float countDownTime2;
private float scoreTime2;
private float menuTime2;
// Use this for initialization
void Start () {
countDownTime2 = 60.0f;
scoreTime2 = countDownTime2+3;
menuTime2 = countDownTime2+5;
gameStatus = (GameStatus)GetComponent(typeof(GameStatus));
}
// Update is called once per frame
void Update () {
countDownTime2 -= Time.deltaTime;
if(gameStatus.score >= 300){Application.LoadLevel("MainScene2");}
if(countDownTime2 <= 0.0f)
{gameStatus.isGameOver = true;
countDownTime2 = 0.0f;
gameStatus.score +=0;
}
scoreTime2 -= Time.deltaTime;
menuTime2 -= Time.deltaTime;
}
void OnGUI()
{
float sw = Screen.width;
float sh = Screen.height;
GUI.skin = mySkin2;
int mScore = gameStatus.score;
if(countDownTime2 > 0.0f){
GUI.Label (new Rect(50,0,sw/2,sh/2), "Score : " + mScore.ToString(),"scoreStyle");
GUI.Label (new Rect(400,0,0,0), "Time : " + countDownTime2.ToString("000") ,"timeStyle");
}
if(gameStatus.isGameOver)
{GUI.Label (new Rect(120,100,sw/2,sh/4),"Game Over","messageStyle");}
if (scoreTime2 <= 0.0f)
{
GUI.Label (new Rect(130,50,0,0), "Your Score is " + mScore.ToString(),"scoreStyle2");
}
if(menuTime2 <= 0.0f){
// Make the first button. If it is pressed, Application.Loadlevel (1) will be executed
if(GUI.Button(new Rect(100,220,80,20), "Retry")) {
Application.LoadLevel("MainScene");}
if(GUI.Button(new Rect(300,220,80,20), "Menu")) {
Application.LoadLevel("TitleScene");}
}
}
}