Cancelling Coroutine when Home button presseddown or returned main menu - c#

some pretext of what I am doing ; I am currently locking down my skill buttons via setting interactable = false in coroutines. Showing text of remaning seconds via textmeshpro and setting them deactive when countdown is over. But I am having problem when home button is pressed/ returned main menu. I would like to refresh my buttons cooldowns and stop coroutines when its pressed. But it is staying in lock position.
this is my cooldown coroutine
static List<CancellationToken> cancelTokens = new List<CancellationToken>();
...
public IEnumerator StartCountdown(float countdownValue, CancellationToken cancellationToken)
{
try
{
this.currentCooldownDuration = countdownValue;
// Deactivate myButton
this.myButton.interactable = false;
//activate text to show remaining cooldown seconds
this.m_Text.SetActive(true);
while (this.currentCooldownDuration > 0 && !cancellationToken.IsCancellationRequested)
{
this.m_Text.GetComponent<TMPro.TextMeshProUGUI>().text = this.currentCooldownDuration.ToString(); //Showing the Score on the Canvas
yield return new WaitForSeconds(1.0f);
this.currentCooldownDuration--;
}
}
finally
{
// deactivate text and Reactivate myButton
// deactivate text
this.m_Text.SetActive(false);
// Reactivate myButton
this.myButton.interactable = true;
}
}
static public void cancelAllCoroutines()
{
Debug.Log("cancelling all coroutines with total of : " + cancelTokens.Count);
foreach (CancellationToken ca in cancelTokens)
{
ca.IsCancellationRequested = true;
}
}
void OnButtonClick()
{
CancellationToken cancelToken = new CancellationToken();
cancelTokens.Add(cancelToken);
Coroutine co;
co = StartCoroutine(StartCountdown(cooldownDuration, cancelToken));
myCoroutines.Add(co);
}
this is where I catch when home button pressed/returned main menu. when catch it and pop pauseMenu
public void PauseGame()
{
GameObject menu = Instantiate(PauseMenu);
menu.transform.SetParent(Canvas.transform, false);
gameManager.PauseGame();
EventManager.StartListening("ReturnMainMenu", (e) =>
{
Cooldown.cancelAllCoroutines();
Destroy(menu);
BackToMainMenu();
EventManager.StopListening("ReturnMainMenu");
});
...
I also stop time when game on the pause
public void PauseGame() {
Time.timeScale = 0.0001f;
}

You are using CancellationToken incorrectly in this case. CancellationToken is a struct that wraps a CancellationTokenSource like this:
public bool IsCancellationRequested
{
get
{
return source != null && source.IsCancellationRequested;
}
}
Because it's a struct, it gets passed around by value, meaning the one you store in your list is not the same instance as the one that your Coroutine has.
The typical way to handle cancellation is to create a CancellationTokenSource and pass its Token around. Whenever you want to cancel it, you simply call the .Cancel() method on the CancellationTokenSource. The reason for it being this way is so that the CancellationToken can only be cancelled through the 'source' reference and not by consumers of the token.
In your case, you are creating a token with no source at all so I would suggest making the following changes:
First of all, change your cancelTokens list to be a:
List<CancellationTokenSource>
Next, change your OnButtonClick() method to look like this:
public void OnButtonClick()
{
// You should probably call `cancelAllCoroutines()` here
cancelAllCoroutines();
var cancellationTokenSource = new CancellationTokenSource();
cancelTokens.Add(cancellationTokenSource);
Coroutine co = StartCoroutine(StartCountdown(cooldownDuration, cancellationTokenSource.Token));
myCoroutines.Add(co);
}
And lastly, change your cancelAllCoroutines() method to this:
public static void CancelAllCoroutines()
{
Debug.Log("cancelling all coroutines with total of : " + cancelTokens.Count);
foreach (CancellationTokenSource ca in cancelTokens)
{
ca.Cancel();
}
// Clear the list as #Jack Mariani mentioned
cancelTokens.Clear();
}
I would suggest reading the docs on Cancellation Tokens or alternatively, as #JLum suggested, use the StopCoroutine method that Unity provides.
EDIT:
I forgot to mention that it is recommended that CancallationTokenSources be disposed of when no longer in use so as to ensure no memory leaks occur. I would recommend doing this in an OnDestroy() hook for your MonoBehaviour like so:
private void OnDestroy()
{
foreach(var source in cancelTokens)
{
source.Dispose();
}
}
EDIT 2:
As #Jack Mariani mentioned in his answer, multiple CancellationTokenSources is overkill in this case. All it would really allow you to do is have more fine-grained control over which Coroutine gets cancelled. In this case, you are cancelling them all in one go, so yeah, an optimisation would be to only create one of them. There are multiple optimisations that could be made here, but they are beyond the scope of this question. I did not include them because I felt like it would bloat this answer out more than necessary.
However, I would argue his point about CancellationToken being 'mostly intended for Task'. Pulled straight from the first couple of lines in the MSDN docs:
Starting with the .NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. This model is based on a lightweight object called a cancellation token
CancellationTokens are lightweight objects. For the most part, they are just simple Structs that reference a CancellationTokenSource. The 'overhead' that is mentioned in his answer is negligible and, in my eyes, totally worth it when considering readability and intention.
You could pass a load of booleans around with indices or subscribe to events using string literals and those approaches would work.
But at what cost? Confusing and difficult-to-read code? I would say not worth it.
The choice is ultimately yours though.

MAIN ISSUE
In your question you just use the bool cancellationToken.IsCancellationRequested and no other functionalities of Cancellation Token.
So, following the logic of your method, you might just want to have a List<bool> cancellationRequests and pass them using ref keyword.
Still, I would not go ahead with that logic nor with the logic proposed by Darren Ruane, because they have one main flaw.
FLAW => these solutions keep adding things to 2 lists cancelTokens.Add(...) and myCoroutines.Add(co), without ever clearing them.
public void OnButtonClick()
{
...other code
//this never get cleared
cancelTokens.Add(cancelToken);
...other code
//this never get cleared
myCoroutines.Add(co);
}
If you want to go down this way you could remove them manually, but it's tricky, because you never know when they will be required (the Coroutine can be called many frames after CancelAllCoroutines method).
SOLUTION
Use a static event instead of a list
To remove the list and make the class even more decoupled you might use a static event, created and invoked in the script where you call the PauseGame method.
//the event somewhere in the script
public static event Action OnCancelCooldowns;
public void PauseGame()
{
...your code here, with no changes...
EventManager.StartListening("ReturnMainMenu", (e) =>
{
//---> Removed ->Cooldown.cancelAllCoroutines();
//replaced by the event
OnCancelCooldowns?.Invoke();
...your code here, with no changes...
});
...
You will listen to the static event in your coroutine.
public IEnumerator StartCountdown(float countdownValue, CancellationToken cancellationToken)
{
try
{
bool wantToStop = false;
//change YourGameManager with the name of the script with your PauseGame method.
YourGameManager.OnCancelCooldowns += () => wantToStop = true;
...your code here, with no changes...
while (this.currentCooldownDuration > 0 && !wantToStop)
{
...your code here, with no changes...
}
}
finally
{
...your code here, with no changes...
}
}
EASIER SOLUTION
Or you might just stay simple and use MEC Coroutines instead of Unity ones (they are also more performant ).
MEC Free offers the tag functionality that solves the problem entirely.
SINGLE COROUTINE START
void OnButtonClick() => Timing.RunCoroutine(CooldownCoroutine, "CooldownTag");
STOP ALL COROUTINE with a specific tag
public void PauseGame()
{
...your code here, with no changes...
EventManager.StartListening("ReturnMainMenu", (e) =>
{
//---> Removed ->Cooldown.cancelAllCoroutines();
//replaced by this
Timing.KillCoroutines("CooldownTag");
...your code here, with no changes...
});
...
Further notes on tags (please consider that MEC free has only tags and not layers, but you require just tags in your use case).
EDIT:
After some thought I just decided to remove the details of the bool solution, it might confuse the answer and it was going beyond the scope of this question.

Unity has StopCoroutine() specifically for ending Coroutines early.
You will want to create a function that you can call on every skill button object when you want to reset them all like this:
void resetButton()
{
StopCoroutine(StartCountdown);
this.currentCooldownDuration = 0;
this.m_Text.SetActive(false);
this.myButton.interactable = true;
}

Related

Is there a way to await a flag change in a function?

I've attempted to make a simple step mode for an algorithm I'm running, and here is how it looks like:
public async Task<bool> AStarAlgorithmAsync(PFSquare curr = null)
{
// some algorithm code here
foreach(var square in Sorroundings)
{
if (SteppedMode)
{
await Task.Run(Pause);
}
if (await AStarAlgorithmAsync(square))
{
return true;
}
}
}
In my application, I have a Boolean called SteppedMode that decides if the algorithm should run one iteration per click event.
Pause() looks like this:
private void Pause()
{
while (!ContinueStep) { }
ContinueStep = false;
return;
}
And in another part of my (GUI) application I have an event which sets the boolean ContinueStep to true which in theory should end the while loop and continue the algorithm function. Currently this bit of code locks my GUI thread up and I'm almost certain there is a better way to do this.
I'm trying to get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm. Is there an easier and cleaner way to do this?
(This is a GUI application, not a console application.)
Your property is moonlighting as a method.
It makes no sense to set a property, to then have that property revert back to its original state immediately. As a consumer, I would be majorly confused by that behavior. Think about this code:
var myObj = new MyObject();
myObj.MyBoolean = true;
Console.WriteLine(myObj.MyBoolean); // FALSE!?
It just doesn't make sense.
The only effect you want to trigger by setting this property is to execute some code. That's exactly what methods are supposed to be used for:
public void ContinueStep()
{
Console.WriteLine("I did some work");
}
So instead of this:
myObj.ContinueStep = true;
you should be doing this:
myObject.ContinueStep();
This doesn't lock up your UI thread, while also being a lot more sensical to your consumer. The method suggests that some action will be taken (which may or may not lead to state changes in the object - that's a contextual expectation).
Infinite recursion
As an aside; based on your code, AStarAlgorithmAsync is a recursive function, and seemingly infinitely so. There doesn't seem to be an ending condition.
Every recursive level will interate over the first surrounding and then trigger the next level, which again will interate over the first surrounding and then trigger the next level, which again ...
That can't be right, but it's unclear to me how to fix it as the bigger picture is not explained in your question
A simple implementation
What I'm trying to do is get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm, is there an easier and cleaner way to do this?
A simple example of such a thing:
private int _index = 0;
private List<object> _myList = ...; // assume this list contains some elements
public void ProcessNextObject()
{
if(_index < _myList.Length)
{
Process(_myList[_index]);
_index++;
}
}
private void Process(object o)
{
Console.WriteLine("Processing this object!");
}
You can then hook up your click event to call ProcessNextObject().
Note that in this example, the list is processed once and cannot be processed again. By manipulating the index value, you can change that behavior as you like.

What Unity API's are not allowed in async callbacks?

I saw somewhere that within a Thread in Unity, you couldn't use Unity API's.
I'm wondering if this is also the case for async callbacks in general (for example a function assigned to WebSocket.OnMessage when using WebSocketSharp), and if so then is there a way to know what is allowed and what isn't (i.e. what are "Unity API's")?
As an example, when using WebSocketSharp's WebSocket.OnMessage, I put this in my Start function of a MonoBehavior:
// ws is a WebSocketSharp.WebSocket
// displayText is a UnityEngine.UI.Text
ws.OnMessage += (sender, evt) =>
{
// 1
boo = UnityEngine.Random.Range(1, 1000).ToString();
// 2
displayText.text = "Heyoo";
};
The line under 1 errors (no logs just beyond it) but no error message shows. Whereas when that line is not inside this callback (top-level of Update for example), I can see its result no problem.
As for the line under 2, the Inspector in Unity shows the updated text, but the Play screen does not, until I update an attribute in the Inspector, as if the text field did get updated, but when it needed to use a Unity API to update the screen, it failed, so it's not until a separate update happens that it actually appears.
That's my hypothesis for these odd behaviors, so please let me know if that is correct, and if there's a succinct (or documented) way to describe what I'm describing.
async in general means exactly this: Not in the main thread.
It is hard to answer what is supported and what not in other threads then the mainthread ... short: Most things are not supported.
The UnityEngine.Random.Range(1, 1000).ToString(); should work. But be carefull with assignments!
A known workaround is to create like a callback worker and pass Actions to execute back to the main thread like e.g.:
public class MainThreadWorker : MonoBehaviour
{
// singleton pattern
public static MainThreadWorker Instance;
// added actions will be executed in the main thread
ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private void Awake()
{
if (Instance)
{
this.enabled = false;
return;
}
Instance = this;
}
private void Update()
{
// execute all actions added since the last frame
while (actions.TryDequeue(out var action))
{
action?.Invoke();
}
}
public void AddAction(Action action)
{
if(action != null) actions.Enqueue(action);
}
}
Having this in your scene somewhere you can now pass an action back to the main thread like
ws.OnMessage += (sender, evt) =>
{
MainThreadWorker.Instance.AddAction(()=>
{
// 1
boo = UnityEngine.Random.Range(1, 1000).ToString();
// 2
displayText.text = "Heyoo";
});
};

Waiting while KeyPressed in XNA

I'm trying to learn XNA game programming, now i would like to wait while a Key is Pressed
I have Test it with:
while (IsKeyPressed = Keyboard.GetState().IsKeyDown(key));
But IsKeyPressed is also true when the key was released
That code is effectively a spin lock. Don't use spin-locks.
The bug you are seeing is likely because you are using a spin-lock, its not getting a chance to update properly.
Instead, you should read the key being pressed, and set state in whatever class is relevant to stop processing (likely a simple if check in the Update function). Then, when you detect the release, you change the state so the if check will pass.
Something like:
//Main Update
if (Keyboard.GetState().IsKeyDown(key))
myObject.Wait();
else
myObject.Continue();
//Other object
public void Wait()
{
waiting = true;
}
public void Continue()
{
waiting = false;
}
public void Update()
{
if (!waiting)
{
//Update state
}
}
You could always check previous state to avoid calling Wait and Continue repeatedly, but thats going to be something of a micro-optimization with the code provided.

Run event once during Update() XNA / C#

this is the best way I can think of doing this. Could you give me so hints as to whether this is the correct way or if there is a more efficient way of doing it.
My situation is:
Each time the frame is Update()'ed (Like in XNA) I want to check if something has happened.. Like if the timer for that screen has been running for over 2000 milliseconds. But I only want it to fire once, not every time the frame is updated. This would cause a problem:
if(totalMilliseconds > 2000)
{
this.Fader.FadeIn();
}
So I came up with this method that I have implemented in the GameScreen class that looks like this:
public bool RunOnce(string Alias, bool IsTrue)
{
if (!this.Bools.ContainsKey(Alias))
this.Bools.Add(Alias, false);
if (IsTrue && !this.Bools[Alias])
{
this.Bools[Alias] = true;
return true;
}
else
return false;
}
This basically checks if the passed if statement boolean is true, if it is then it fires once and not again unless the Bool["Alias"] is set back to false. I use it like this:
if(this.RunOnce("fadeInStarted", totalMilliseconds > 2000))
{
this.Fader.FadeIn();
}
This will then only run one time and I think is quite easily readable code-wise.
The reason I have posted this is for two reasons.. Firstly because I wanted to show how I have overcome the problem as it may be of some help to others who had the same problem.. And secondly to see if I have missed an obvious way of doing this without creating a manual method for it, or if it could be done more efficiently.
Your method is interesting, I don't see a problem with it, you've essentially created a new programming construct.
I haven't encountered this situation a lot so what I have done in this situation is always start with the untidy approach:
bool _hasFadedIn = false;
.
if(totalMilliseconds > 2000 && !_hasFadedIn)
{
this.Fader.FadeIn();
_hasFadedIn = true;
}
And 90% of the time I leave it like that. I only change things if the class starts growing too big. What I would do then is this:
_faderControl.Update(totalMilliseconds);
Put the logic for fader control into a separate class, so:
class FaderControl
{
bool _hasFadedIn=false;
public void Update(int totalMilliseconds)
{
if (_hasFadedIn)
return;
if (totalMilliseconds <= 2000)
return;
this.Fader.FadeIn();
_hasFadedIn=true;
}
}
It can be modified to make it configurable, like reseting, setting "start", "end", fadein time, or also controlling fadeout too.
Here's how I would approach this problem.
These are your requirements:
You have arbitrary pieces of logic which you want to execute inside of your Update().
The logic in question has a predicate associated with it which determines whether the action is ready to execute.
The action should execute at most once.
The core concept here is "action with an associated predicate," so create a data structure which represents that concept:
public class ConditionalAction
{
public ConditionalAction(Action action, Func<Boolean> predicate)
{
this.Action = action;
this.Predicate = predicate;
}
public Action Action { get; private set; }
public Func<Boolean> Predicate { get; private set; }
}
So now, your example becomes
var action = new ConditionalAction(
() => this.Fader.FadeIn(),
() => totalMilliseconds > 2000);
In your Update() you need something that can execute these conditional actions:
public void Update(GameTime time)
{
// for each conditional action that hasn't run yet:
// check the action's predicate
// if true:
// execute action
// remove action from list of pending actions
}
Because their predicates are probably unrelated, actions don't necessarily run in order. So this isn't a simple queue of actions. It's a list of actions from which actions can be removed in arbitrary order.
I'm going to implement this as a linked list in order to demonstrate the concept, but that's probably not the best way to implement this in production code. Linked lists allocate memory on the managed heap, which is generally something to be avoided in XNA. However, coming up with a better data structure for this purpose is an exercise best left for another day.
private readonly LinkedList<ConditionalAction> pendingConditionalActions =
new LinkedList<ConditionalAction>();
public void Update(GameTime time)
{
for (var current = pendingConditionalActions.First; current != null; current = current.Next)
{
if (current.Value.Predicate())
{
current.Value.Action();
pendingConditionalActions.Remove(current);
}
}
}
public void RegisterConditionalAction(ConditionalAction action)
{
pendingConditionalActions.AddLast(action);
}
Registered actions will wait until their predicates become true, at which point they will be executed and removed from the list of pending actions, ensuring that they only run once.

C# Subscribe dynamically anonymous method

Hello yeah I'm asking this question a second time, sorry about that but I don't know how to bump previous question. I'll explain more in depth my problem in a more completed example.
Instead of writing like 300+ Event classes in 300 class files which I may have to do if this doesn't work, so they can do little timed jobs like this example job below in a server project.
What i'm trying to avoid is writing a bunch of classes and simply just write everything more compacted in structure of whatever i'm working on.
To sum it up, i'm mixing 90% functional programming and want to give some function some delayed timed event, without creating the new timed event in a separate class then running back and forth through the files looking how everything is linked up, but this way everything can be seen so you can find bugs and whatnot much faster as everything is right in front of you, kinda like writing loop code, but with delay.
All I have right now is one thread which processes events, deletes events which have been stopped, keeps re-running events which don't stop after one cycle and of course waiting until some events can be ran.
If anyone knows a better way to do what i'm trying to do maybe some built-in C# Event system? Which is preferably simple.
class Event {
private Action action;
private bool stopped;
public Event(long tick, Action action) {
this.tick = tick;
this.action = action;
this.lastRun = Environment.TickCount;
}
public void stop() {
stopped = true;
}
public bool canRun() { //blah ignore just showing what I plan to do
if (stopped)
return false;
return (Environment.TickCount - lastRun) > tick;
}
public void run() {
this.lastRun = Environment.TickCount;
action();
}
//... other methods
}
class Test {
string t;
public void setT(string t) {
this.t = t;
}
public void stuff() {
Console.WriteLine(a);
}
}
class ImportantWork {
public static void Main(string[] args) {
someDeepMethod();
}
void someDeepMethod() {
Test t = new Test();
t.setT("secondTime");
//Here is where the problem occurs.
Server.registerEvent(new Event(5000, () => {
this.stop(); //<-- Error how I call this from this new Event instance.
stop(); //<-- Also error
//Event.stop(); //<-- haha may work if it was static but thats stupid
t.stuff();
Console.WriteLine("thirdTime");
}));
t.setT("firstTime");
t.stuff();
}
}
Expected output:
firstTime
...waits 5 seconds...
secondTime
thirdTime
I don't know how you'd be able to do that inline like that. Why can't you use some kind of set-function and make it two lines?
MyEvent newEvent;
Server.registerEvent((newEvent = new MyEvent(5000)));
newEvent.setAction(() => {
newEvent.stop();
t.stuff();
Console.WriteLine("thirdTime");
});
It seems to me like there's some kind of structural issue with your design. I'm assuming that the example you provided was not actually what you were working with, just a simple example to address the problem you're having. If it is, however, the example you're working with why don't you just add a boolean flag in the constructor to tell the instance whether or not to call this.stop() on itself - instead of requiring it specified in the Action?
Best of luck!

Categories