I can’t stop a specific instance, as “StopCoroutine” requires a string… and I now have two or three coroutines with that same string in the water and racing fast…
This is a fader that fades out the color.a on an object. I have a pool of objects and when I need to fade one out, I call the coroutine and include the object to be faded. Fire and Forget.
Unless I need to stop that object from fading or remove it immediately.
With this coroutine I can fade any object I pass to it.
IEnumerator FadeObject (Object myObject) {
while (SomeFadingTest) {
// SomeFadingCode()
yield return null;
}
// Put object back in the pool code
}
You should create a new Class for the Fading, put a bool in it to see if you want it to fade or not and just set it to false to quit the while-loop in the Coroutine and let it finish.
Just stopping the Coroutine might lead to some unpredictable behaviour, might happen that the Object isn’t “put back in the pool”.
That way you can also Lock the Object you are fading and prevent Multiple Fades at the same time.
I came to the same conclusion as vanjones (is that your real name or a political statement?). Anyway, basically, you want to put your behavior in another class. And put the co-routine on that. Then you can stop each one individually with no trouble.
The reason for the delegate as opposed to just a boolean is that is is passed by reference that way and can be modified and tweaked somewhat on the fly.
You can then pass in variables of any scope and do it by reference (delegates will box up the bool value type, so it’s a proper reference. You can use local scope, and then pass the same bool to two coroutines (and do a sort of first finished cancelling operation) or pass in class variables or whatnot. It’s a bit messy sometimes to debug, but it does get the job done. You can even use it to build more complex comparers within the delegate itself.
This makes sense. I can feel the theory, and it feels good. Now I’ll have to spend some time trying to visualize it and get it to work.
TBH I’m not sure how I’d do this. If I can’t grab the coroutine itself, I’m not sure how I can ask the object to break out of it… but this could be my ignorance on the details of coroutines. I understand a tiny bit about what they are doing under the hood, but certainly not enough.
Ya, “what I said to VanJones”. I think I get it… but I’ll have to work thru it.
I’m seeing if I can get my head 'round this one, and it’s benefits/ramifications. Again, I’m “feeling” the use of a delegate over a boolean variable more than I’m truly “grokking” it.
I think I’ll need a play-around.
And yes, ultimately, I’ll need to get that object back into the pool…
Sorry for the Offtopic!
I’m from germany and when I came up with that Alias some Years back I didn’t know about VanJones, then I registered to Twitter and wondered why everybody is following me
My name is Jonathan and VanJones just seemed cool…
And it’s not like the linked Van Jones is (afaict) an unpleasant person to share a name with - like say Jim Jones or something. And I’d no clue who Van Jones was, but I’ve been out of the country (US) for a few years.
In the end, I realized while discussing the issue here and in chat that I was approaching this incorrectly.
I was trying to approach this like a conventional reference, but finally realizing that I have the reference to the object being processed by the coroutine, I could break the coroutine from the inside. Tho’ I appreciate the power of the delegate solution, in the end I used a boolean on the object being processed that I could set with the existing reference.
Hey, thanks a lot BDev for your approach ! It really helped !
I kind of tweaked it even more so that in a single function, I’m able to create a unique coroutine with multiple parameters :
Create a static Dictionary under a super class (let’s call it “Model”), which will store all your new coroutines :
_coroutines = new Dictionary<string, UniqueCoroutine>();
Tweak your CCoroutine class a bit (renamed it UniqueCoroutine here) :
public class UniqueCoroutine : IEnumerator {
public bool stop;
public bool _moveNext;
string _name;
IEnumerator enumerator;
MonoBehaviour behaviour;
public readonly Coroutine coroutine;
public UniqueCoroutine(MonoBehaviour behaviour, IEnumerator enumerator, string _refName)
{
this.behaviour = behaviour;
this.enumerator = enumerator;
this.stop = false;
this._name = _refName;
this.coroutine = this.behaviour.StartCoroutine(this);
}
public object Current { get { return enumerator.Current; } }
public bool MoveNext() {
_moveNext = enumerator.MoveNext();
if (!_moveNext || stop)
return !Model.StopUCoroutine(_name);
else
return _moveNext;
}
public void Reset() { enumerator.Reset(); }
}
So here, I put in bold what I added.
I’m just adding a reference name (string _name) for later use in our freshly created UniqueCoroutine Dictionary (under Model).
I’m also calling a destruction function whenever MoveNext is false (= coroutine is done).
(I suppressed the last two lines, Dispose and YieldStatement because they’re not a part of IEnumerator interface)
Create a function under Model that create a new UniqueCoroutine :
public static void UCoroutine(MonoBehaviour behaviour, IEnumerator enumerator, string _name){
StopUCoroutine(_name);
_coroutines.Add(_name, new UniqueCoroutine(behaviour, enumerator, _name));
}
It safely stops and replaces any existing coroutine under the same name by a new one. No more “dirty” coroutines left.
And the StopUCoroutine function, still under Model, which has to return a bool for usage under UniqueCoroutine’s MoveNext :
public static bool StopUCoroutine(string _name){
if (_coroutines.ContainsKey(_name)){
_coroutines[_name].stop = true;
_coroutines.Remove(_name);
return true;
}
return false; //failed to stop coroutine because it doesn't exist
}
Et voilà.
Now, you can create or destroy any coroutine at will, it will never clone itself, and will accept multiple parameters aswell.
Basically, it’s a StopCoroutine() allowing multiple parameters.
Works like a charm.
Now I just have one last doubt : even if the UniqueCoroutine reference is removed from the Dictionary, and the MoveNext stopped because of stop = false, is the thread really removed from memory ?
It looks pretty good except for the fact that creating a new Job every time you want to start a Coroutine seems a little dangerous for the heap memory and I’m worried it will slow the game down because of the Garbage collector.
when you use enumerator.Reset it will give a “NotSupportedException: Operation is not supported.” i.e. the Reset method from the interface IEnumerator is not implemented in Unity