Limit button press in between in seconds? - c#

How to make button press function work only in between # seconds? For example, allow user to press E at any time, but execute, for example, animation every 5 seconds? I've tried Invoke but it doesnt seem to work as it should. I've also tried timestamp and StartCoroutine (waitforseconds).
Here's what I got so you can see:
void Update()
{
if (triggerIsOn && Input.GetKeyDown(KeyCode.E))
{
drinkAmin.Play("DrinkVodka");
StartCoroutine(letsWait());
}
}
And
IEnumerator letsWait(){
Debug.Log ("lets wait works!");
yield return new WaitForSeconds (5);
TakeAshot ();
}
It all works, but not by 5 seconds in between, but rather it works every 5 seconds after each button press. So, this doesn't work as it should. Can anyone help me? A bit lost here.
Thanks!

I came up with a solution for debouncing input events in Unity by using coroutines and unique identifiers for each coroutine call.
public class Behaviour : MonoBehaviour
{
private Guid Latest;
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
// start the debounced input handler coroutine here
StartCoroutine(Debounced());
}
}
private IEnumerator Debounced()
{
// generate a new id and set it as the latest one
var guid = Guid.NewGuid();
Latest = guide;
// set the denounce duration here
yield return new WaitForSeconds(3);
// check if this call is still the latest one
if (Latest == guid)
{
// place your debounced input handler code here
}
}
}
What this code does is generates a unique id for each call of the Debounced method and sets the id of the latest Debounced call. If the latest call id matches the current call id then the code is executed. Otherwise, another call happened before this one so we don't run the code for that one.
The Guid class is in the System namespace so you will need to add a using statement at the top of the file: using System;.

What you're talking about is called a 'debouncer'. There's a good SO question about this already: C# event debounce - try using one of the approaches there.

Related

Doubst about Managin player actions and cooldown time

So i got this action object witch contains a button, when i press that button the action coroutine starts, sets the cooldown of the action and enabled=false the button.
Im trying to implement a cooldown system so every time the turn ends action.currentCooldown -=1.
Im yet to implement my turn manager and i must admit im a bit clueless about it, i guess it must have a state(allyTurn, enemyTurn) and a coroutine to update all action cooldown when the turn changes.
Also what i want to do is asing a List to each unit and then display each action buttons.
here are some screenshots of the code (keep in mind its just a first draft and im still learning the basics)
Hereis the action object
here is the code for the action
I apreciate all the help i can get
So there are a couple ways to implement what you are looking for. The way that I would want to go for is using events. You could create events for each state change (i.e. AllyTurnStart and EnemyTurnStart):
public class ExampleClass : MonoBehaviour
{
bool AllyTurn = true;
UnityEvent AllyTurnStart;
UnityEvent EnemyTurnStart;
BaseAction AllyAction;
BaseAction EnemyAction;
void Start()
{
// This will setup the event listeners that get called
if (AllyTurnStart == null)
AllyTurnStart = new UnityEvent();
if (EnemyTurnStart == null)
EnemyTurnStart = new UnityEvent();
// This is for example purposes but assigning actions to the events can happen anywhere
AllyAction = new BaseAction()
EnemyAction = new BaseAction()
AllyTurnStart.AddListener(AllyAction.StartOfTurnEvents);
EnemyTurnStart.AddListener(EnemyAction.StartOfTurnEvents);
}
//This method is meant to simulate switching turns back and forth.
void NextTurn()
{
if(AllysTurn)
{
AllyTurnStart.Invoke()
}
else
{
EnemyTurnStart.Invoke()
}
//This switches whos turn it is
AllysTurn = !AllysTurn;
}
}
public class BaseAction : Monobehaviour
{
int Cooldown;
public StartOfTurnEvents()
{
// Here you can reduce the cooldown, Check if it is ready, etc.
}
}
Hope this helps.

How do I get a button that is held down to change what it is doing after a power up ends?

In the game I am trying to make. When I am holding my fire button and I pick up a power up SuperShot, it continues to fire my regular laser unless I release my fire button and press it again. The same thing happens again when the power up ends. I have researched and I cant seem to figure out a way to check on where the power up is active while holding the key down.
private void Fire()
{
if (Input.GetButtonDown("Fire1"))
{
if (SuperShotIsActive)
{
superShotFiringCoroutine = StartCoroutine(SuperShotFireContinuously());
}
else if (!SuperShotIsActive)
{
firingCoroutine = StartCoroutine(FireContinuously());
}
}
if (Input.GetButtonUp("Fire1"))
{
StopCoroutine(firingCoroutine);
StopCoroutine(superShotFiringCoroutine);
}
}
You might be able to fix this by looking away from the button itself and into the code of the actual firing of the weapon.
//psuedocode-ish example. Just for the concept
//not necessarily the best idea to use a for loop but maybe it works for you.
FireContinuously()
{
for(int foo = 0; foo < ammo; foo++)
{
if(!SuperShotIsActive)
{
//normal firing code you have goes here
}
else
{
//supershot code goes here
}
}
}
In FireContinuously() (which I assume is a loop of some sort) you can add a check before each shot to see if the player now has the powerup and the necessary logic to change to the appropriate SuperShotFireContinuously(). Do the opposite for SuperShotFireContinuously() to change to FireContinuously().

How to stop the execution of the code until I read a value from PlayerPref in Unity

Currently I am using Toggles to select a list of images and rather ran into an interesting problem.
I am using a Toggle Group called Radio Group and have 3 toggles under it. Each time when a toggle is selected the command
PlayerPrefs.SetInt("SaveToggleId", id);
is run. In this the id number is 0 for toggle 1, 1 for toggle 2 and so on.
So when I try to read this data the next time , the following set of code always reads 0 when used in Start and the correct value when used in Awake
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
SelectStartingToggle(toggleGroupId);
When I used this code in conjuction with the Debug.log() statements in various places what I found is when used in Start , it first reads from the function associated when the first toggle is selected and therby stores 0 . But when I use it in Awake it reads the right value stored in PlayerPrefs and selects the correct initial value
My explanation would be that because Awake is executed before Start , it has ample time to read from PlayerPrefs which gives the correct value. Also when I used only the number in the Start() as follows
SelectStartingToggle(3);
it correctly selected the right toggle whereas when I used PlayerPref instead of number ,it chose the wrong value.
Is my explanation correct or am I missing something?Also how to make sure the code execution is halted until the data from PlayerPref is read. Here is the full code:
public class RadioButtonSystem : MonoBehaviour
{
ToggleGroup toggleGroupObject;
private int toggleGroupId;
// Start is called before the first frame update
private void Awake()
{
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
Debug.Log("SaveToggleId........" + toggleGroupId);
SelectStartingToggle(toggleGroupId);
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void onSelectedToggle1()
{
SaveToggleId(0);
}
public void onSelectedToggle2()
{
SaveToggleId(1);
}
public void onSelectedToggle3()
{
SaveToggleId(2);
}
public void SelectStartingToggle(int id)
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
toggles[id].isOn = true;
}
public void SaveToggleId(int id)
{
PlayerPrefs.SetInt("SaveToggleId", id);
Debug.Log("SaveToggleId........saving..........." + PlayerPrefs.GetInt("SaveToggleId"));
}
/* Toggle GetSelectedToggle()
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
foreach (var t in toggles)
if (t.isOn) return t; //returns selected toggle
return null; // if nothing is selected return null
}*/
}
Playerprefs are saved upon OnApplicationQuit(). If you want to save it immediately, call PlayerPrefs.Save(). After PlayerPrefs.SetInt().
Btw, from the unity scripting api:
This function will write to disk potentially causing a small hiccup, therefore it is not recommended to call during actual gameplay.
In the edit/project settings there is a tab called script execution order, where you can set the order in which your scripts will be executed. For example, you have a "LoadManager" script, you set its priority to 1, and you set everything that relys on it to a greater number like 10.
If you fo this, nothing eill start executing until your manager script finished.

Is there an easy way to get out of this IF loop?

I'm learning how to program and I feel that I'm always trapped with this kind of loops problems.
the question is, what would be the best way to get out of the if when the grouped bool is always true on Update (every frame). I need to execute the EUREKA only once.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroupedWordsEyes : MonoBehaviour
{
void Update()
{
//Check if words are grouped
bool grouped = CompareTags_Word1.colliding == true;
if (grouped)
{
Debug.Log("EUREKAAAA!");
//get out of the loop!
}
}
}
It sounds like you want that Update is only called until the condition matches the first time.
You can simply disable that component by setting enabled to false.
This way Update is no longer called by Unity.
public class GroupedWordsEyes : MonoBehaviour
{
void Update()
{
//Check if words are grouped
if (CompareTags_Word1.colliding)
{
Debug.Log("EUREKAAAA!");
enabled = false;
}
}
}
It seems like you want the code within the if to execute only when "colliding" changes.
Then you need to remember the previous value and check for changes:
EDIT changed code based on comments by OP
public class GroupedWordsEyes : MonoBehaviour
{
private bool previousGrouped = false; // assume that at first there is no collision
void Update()
{
//Check if words are grouped
bool grouped = CompareTags_Word1.colliding == true;
if (grouped != previousGrouped )
{
// always remember a change
previousGrouped = grouped;
// only when just collided
if (grouped)
{
Debug.Log("EUREKAAAA!");
}
}
}
}
EDIT: Updating answer as I misunderstood the question.
You've got several options and it entirely depends on how you've architected.
One option is to deactivate (or even completely destory) the component. After you've done the one thing you need to do, call this.enabled = false - this will disable the component and no scripts will be run. This depends on architecture as this will also disable any other functions or child components attached to this one. You can mitigate this by ensuring this script only contains specific pieces that you want to be able to disable.
Create a flag that indicates whether the action has already been done.
if (grouped && !hasDoneEureka)
{
// do the thing
hasDoneEureka = true;
}
Note that this will ensure you only run what you need to once, but probably isn't optimal since while it won't "do the thing" every time, it will CHECK every frame.

C# WinForms: Waiting for a button press inside an infinite loop

I'm making a simple Guess-The-Number game with a GUI. I need to wait on a loop waiting for the user to input a number in a text box and press "OK". How do I wait for an event inside a loop?
Note: I don't want message boxes. This is done in the main window, hence the need to wait for input.
EDIT: I should have explained myself better. I know that there's a loop inside the GUI. What I want is another loop inside a method. Maybe there's a better way to do this. I could code stuff inside the button's event handler, now that I think about it. Although I'd need global variables. Whataver, I'll think about it, but I hope my question is clearer now.
EDIT 2: Sorry that my question wasn't clear and the edit didn't do much help. First of all, the code is too big to be posted here. I'd probably have to post a screenshot of the GUI, so it wouldn't be of much use. Basically, I have two fields, "Max number" and "Number of allowed guesses". The user enters these two and clicks "Play". A new panel becomes available, with a text box and a "Guess" button. The user enters a guess, and the program checks to see if it's correct.
The purpose of the second infinite loop is to avoid global variables. See, each time the user clicks "Play", the game has to generate a new random number as the correct guess. If everything is done inside a method, no problem. But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
I'd also have to keep track of the remaining number of guesses outside of the method. Again, it's no big deal. I just want to avoid globals if I can.
Again, I'm sorry that my question wasn't too clear. I'm kind of tired, and I didn't feel like writing too much. If this still isn't clear, then don't bother. I'll think of something.
C# automatically loops infinitely waiting for events until your form is closed. You just need to respond to the button click event.
Jason Down's suggestion is wise, create a new GuessingGame class and add it to your project. I know you're worried about "global variables" (which everyone is taught in school never to use unless you absolutely have to), but think about your design specifications for a minute.
But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
As an alternative, store an instance of your GuessingGame class in the form. This is not a global variable! You said so yourself, the point of the game is keep track of the guesses and generate new numbers to guess every time "Play" is clicked. If you store an instance of the game in the form then open another form (e.g. a Help or About box), then the game's instance would not be available (thus, not global).
The GuessingGame object is going to look something like:
public class GuessingGame
{
private static Random _RNG = new Random();
private bool _GameRunning;
private bool _GameWon;
private int _Number;
private int _GuessesRemaining;
public int GuessesRemaining
{
get { return _GuessesRemaining; }
}
public bool GameEnded
{
get { return !_GameRunning; }
}
public bool GameWon
{
get { return _GameWon; }
}
public GuessingGame()
{
_GameRunning = false;
_GameWon = false;
}
public void StartNewGame(int numberOfGuesses, int max)
{
if (max <= 0)
throw new ArgumentOutOfRangeException("max", "Must be > 0");
if (max == int.MaxValue)
_Number = _RNG.Next();
else
_Number = _RNG.Next(0, max + 1);
_GuessesRemaining = numberOfGuesses;
_GameRunning = true;
}
public bool MakeGuess(int guess)
{
if (_GameRunning)
{
_GuessesRemaining--;
if (_GuessesRemaining <= 0)
{
_GameRunning = false;
_GameWon = false;
return false;
}
if (guess == _Number)
{
_GameWon = true;
return true;
}
else
{
return false;
}
}
else
{
throw new Exception("The game is not running. Call StartNewGame() before making a guess.");
}
}
}
This way, all the data related to the game is encapsulated within the class. Hooking up the events is easy in the codebehind of the form:
GuessingGame game = new GuessingGame();
private void btnPlay_Click(object sender, EventArgs e)
{
int numberOfGuesses = Convert.ToInt32(txtNumberOfGuesses.Text);
int max = Convert.ToInt32(txtMax.Text);
game.StartNewGame(numberOfGuesses, max);
}
private void btnGuess_Click(object sender, EventArgs e)
{
int guess = Convert.ToInt32(txtGuess.Text);
bool correct = game.MakeGuess(guess);
if (correct)
lblWin.Visible = true;
if (game.GameEnded)
{
// disable guess button, show loss label
}
}
You should probably look for a book to actually learn windows programming.
The very basics:
1) There is already an infinite loop deep down in the windows code somewhere. Any windows program is constantly looping and scanning for input.
2) Once input is found, this loop fires off an Event.
3) Your mission, should you choose to accept it, is to write event handlers to handle those events.
you are most likely doing it wrong as it has already been pointed out, but you can use this
Application.DoEvents();
to process events when you are on an actual loop
to do it the right way
- don't use a loop
- use an edit box for the input, then a button
- implement the button onclick event
Yes, and What if I am waiting for Speech events, it could happen anytime event when a function is running, I need to handle that without recursively call a function

Categories

Resources