Loop function on button press? Unity3d C# - c#

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);
}
}

Related

Using coroutines to fade in/out TextMeshPro Text Element

I am setting up a "scene intro" with some text saying 'Level 1' using TextMeshPro. I have created the text element in canvas and i am trying to find a way to make it fade in, then wait, and then fade out (Somewhat like what you see when you discover a new place in Skyrim).
So far i tried a versatile solution so i can use the same script for other uses (eg not in the start of the scene, not only fade in etc).
Using TMPro:
...
using TMPro;
...
Start and declaration:
public class IntroFade : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI textToUse;
[SerializeField] private bool fadeIn = false;
[SerializeField] private bool fadeOnStart = false;
[SerializeField] private float timeMultiplier;
private bool FadeIncomplete = false;
private void Start()
{
if (fadeOnStart)
{
if (fadeIn)
{
StartCoroutine(FadeInText(timeMultiplier, textToUse));
FadeIncomplete = true;
}
else
{
StartCoroutine(FadeOutText(timeMultiplier, textToUse));
}
}
}
...
Update in which i want to fadeout once fadein is done
private void Update()
{
if (FadeIncomplete)
{
StartCoroutine(FadeOutText(timeMultiplier, textToUse));
}
}
Corouritnes for the actual fading:
private IEnumerator FadeInText(float timeSpeed, TextMeshProUGUI text)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, 0);
while (text.color.a < 1.0f)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a + (Time.deltaTime * timeSpeed));
yield return null;
}
}
private IEnumerator FadeOutText(float timeSpeed, TextMeshProUGUI text)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, 1);
while (text.color.a > 0.0f)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a - (Time.deltaTime * timeSpeed));
yield return null;
}
}
public void FadeInText(float timeSpeed = -1.0f)
{
if (timeSpeed <= 0.0f)
{
timeSpeed = timeMultiplier;
}
StartCoroutine(FadeInText(timeSpeed, textToUse));
}
public void FadeOutText(float timeSpeed = -1.0f)
{
if (timeSpeed <= 0.0f)
{
timeSpeed = timeMultiplier;
}
StartCoroutine(FadeOutText(timeSpeed, textToUse));
}
So what happens is it either fades in OR fades out depending on the Coroutine that starts first. I am not able to make it so it fades in, stays on screen for like 2 seconds and then fades out.
I also tried fading in then creating a coroutine to waitforseconds and then call the fadeout coroutine but that didn't work either.
A Coroutine can wait for the completion of another coroutine, thinking about it this way will simplify the problem immensely. You have already created your fade in and fade out, now you just have to run them in sequence with a 2 second wait between them.
private IEnumerator IntroFade (TextMeshProUGUI textToUse) {
yield return StartCoroutine(FadeInText(1f, textToUse));
yield return new WaitForSeconds(2f);
yield return StartCoroutine(FadeOutText(1f, textToUse));
//End of transition, do some extra stuff!!
}
If you are interested, these articles are pretty insightful when it comes to learning more about coroutines.

Unity Key Press to Start and Stop [duplicate]

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);
}
}

Execute function on button press (unity3d/C#)

Basically, what I'm trying to do is when pressing Z - it executes function to spin, and X - it executes function to stop spinning. Before, I had UI buttons which worked perfectly fine, now I try doing it by button but nothing happens.
Also, if you can suggest on how to make it start spinning and stop spinning by only pressing "Space" button, that'd be great.
Heres my code so far:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class spin : MonoBehaviour
{
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);
if (Input.GetKey(KeyCode.Z)) {
FidgetSpinnerStart();
}
if (Input.GetKey(KeyCode.X)) {
FidgetSpinnerStop();
}
}
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;
}
}
}
Thanks.
There are just two problems in your code:
1.Checking the keypress in the Start() function.
The Start() will be called once while the Update() function will be called every frame.
You need to use the Update() function to constantly poll the input every frame.
2.Using Input.GetKey() function to check for keypress.
The Input.GetKey() function can return true multiple times over several frames. While you may not see any problems now, that's because the isSpinning variable is preventing possible problems but you will run into problems if you want to add more code directly inside the if (Input.GetKey(KeyCode.Z)) code because those code will execute multiple times in a frame.
You need to use the Input.GetKeyDown() function.
public class spin : MonoBehaviour
{
public float speed = 500f;
public Button starter;
public Button stopper;
bool isSpinning = false;
IEnumerator spinnerCoroutine;
void Start()
{
spinnerCoroutine = spinCOR();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Z)) {
FidgetSpinnerStart();
}
if (Input.GetKeyDown(KeyCode.X)) {
FidgetSpinnerStop();
}
}
IEnumerator spinCOR()
{
//Spin forever until 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;
}
}
}
Also, if you can suggest on how to make it start spinning and stop
spinning by only pressing "Space" button, that'd be great
You can do that with KeyCode.Space. Check if Space key is pressed then check the isSpinning variable before starting/stopping the coroutine.
Just replace the Update function above with the one below:
void Update()
{
//Start if Space-key is pressed AND is not Spinning
if (Input.GetKeyDown(KeyCode.Space) && !isSpinning)
{
FidgetSpinnerStart();
}
//Stop if Space-key is pressed AND is already Spinning
else if (Input.GetKeyDown(KeyCode.Space) && isSpinning)
{
FidgetSpinnerStop();
}
}
Your input logic is only executed once, when Start() is executed.
Put it in the Update() method to check for it every frame.
In this case remove the coroutine and put its logic (without the while-loop) into the Update() method aswell.
public class spin : MonoBehaviour
{
[SerializeField]
private float speed = 500f;
[SerializeField]
private Button starter;
[SerializeField]
private Button stopper;
[SerializeField]
bool isSpinning = false;
void Update()
{
if (Input.GetKeyDown(KeyCode.Z))
{
isSpinning = true ;
}
if (Input.GetKeyDown(KeyCode.X))
{
isSpinning = false ;
}
if( isSpinning )
{
transform.Rotate(Vector3.up, speed * Time.deltaTime)
}
}
}
Further reading

How to start a timer when there's input?

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 ^";
}
}

Unity2D C# Reloading progress bar not working propertly

So I'm making a top-down tank shooter game and I want to make a better reloading system than it was before. So I came to the idea that I need some king of progress bar. I knew how to make it so I started doing it. The problem is that it doesn't work properly. As I show in the .gif above, the progress bar don't go down when you shoot second time. Because I'm new to unity, I still don't know everything very good. So I came here, maybe someone could help.
EDIT:
I just found another problem and maybe an answer why I have this problem. The second time my script tries to reload, my "needTimer" bool is false, thus the progress bar is not going down when it's false. The new question would be why it becomes false instead of true?
My reloading script:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
public bool alreadyReloading;
public bool needTimer;
void Start () {
alreadyReloading = false;
}
IEnumerator needtimertime(){
yield return new WaitForSeconds (6.5f);
needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
alreadyReloading = false;
}
void Update () {
if (needTimer == true) {
timer ("");
}
if (ammo < 5) {
if(alreadyReloading == false){
needTimer = true;
StartCoroutine(uztaisyt());
alreadyReloading = true;
}
}
if (progress.fillAmount <= 0) {
progress.fillAmount = 1.0f;
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
StartCoroutine (needtimertime ());
}
}
When you start the uztaisyt() as a coroutine after shooting the first time (in the animation), needTimer is set to true and in the next Update() call, the needtimertime() coroutine will start. Since both the uztaisyt() and needtimertime() have identical 6.5 second waits, they will not both return on the same frame update because needtimertime() will always be started in the next frame after uztaisyt(). And, since there is no guarantee of the time interval between Update() calls, (see Time and Frame Managment), this interval may be more than expected and needtimertime() could return false in the frame right after uztaisyt() is called after firing the second time.
To ensure that the needtimertime() is always started (if not already running) immediately following a call for uztaisyt() (and called within the same frame update), you could try the following update to Reload script, (basically changes to the Update() method and when/how _isTimerRunning is set).
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
private bool _alreadyReloading;
private bool _isTimerRunning;
void Start () {
_alreadyReloading = false;
_isTimerRunning = false;
}
IEnumerator needtimertime(){
yield return new WaitForSeconds (6.5f);
_needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
_alreadyReloading = false;
}
void Update () {
if (ammo < 5) {
if(_alreadyReloading == false){
StartCoroutine(uztaisyt());
_alreadyReloading = true;
//this will check for and start the progress bar timer in the same udate call
//so both coroutines finish on the same frame update
if(!_isTimerRunning){
_isTimerRunning = true;
timer ("");
}
}
}
if (progress.fillAmount <= 0) {
progress.fillAmount = 1.0f;
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
StartCoroutine (needtimertime ());
}
}
I found my problem and fixed it.
The problem was that needTimer was becoming false. So I found where and removed it.
my new code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
public bool alreadyReloading;
public bool needTimer;
void Start () {
alreadyReloading = false;
needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
alreadyReloading = false;
}
void Update () {
if (ammo < 5.0f) {
if(alreadyReloading == false){
progress.fillAmount = 1.0f;
needTimer = true;
StartCoroutine(uztaisyt());
alreadyReloading = true;
}
if (needTimer == true) {
timer ("");
}
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
}
}

Categories

Resources