Hi @Demigiant
I know this was mentioned on the Unity forums but I was wondering if it would be possible to implement the YieldInstruction such as WaitForCompletion as CustomYieldInstruction instead?
this would then make DOTween compatible with MEC (More Efficient Coroutines) since yield return Timing.WaitUntilDone can take a CustomYieldInstruction as an argument.
I'm guessing it's more complicated than that though as you need to override the keepWaiting bool with a condition
https://docs.unity3d.com/ScriptReference/CustomYieldInstruction.html
I'm assuming it would relate to MoveNext on a coroutine returning false etc?
thanks
J
Ahoy!
That is very interesting. I have to admit I'm not very familiar with CustomYieldInstruction, but I'll study the subject, hopefully this weekend. If you have any interesting links let me know :)
I couldn't work out what the implementation would be within DOTween, you need to give it a condition for continuing or not. (keepWaiting)
a simple version of CustomYieldInstruction can be found here
http://chriskugler.com/2016/01/25/unity-customyieldinstruction/
a more complex example:
https://jacksondunstan.com/articles/3411
_(background for anyone interested in (More Effective Corutines aka MEC: http://trinary.tech/category/mec/
it uses an IEnumerator<float> for its coroutines you you can't yield the standard return from DOTween)_
But here is my current workaround solution, although it doesn't allow you to do much, in terms of controlling the coroutine etc but it suits my basic purpose currently. It also requires a Monobehavior to be passed in the arguments for the extra coroutine to run on
(I'm quite new to C# & Unity so please forgive eveything I'm doing wrong here!)
essentially this allows me to run eg the MEC coroutine version of a yield wait by spawning an extra coroutine in the CustomYieldInstruction
thanks
J.
Use like this:
``C#
// MEC Coroutine, standard DOTween YieldInstruction
Tween myTween = transform.DOMoveX(45, 1);
yield return Timing.WaitUntilDone(
new WaitForStandardYieldInstruction(this, myTween.WaitForCompletion())
);
```C#
using System.Collections;
using UnityEngine;
using DG.Tweening;
using System;
public class WaitForStandardYieldInstruction : CustomYieldInstruction
{
bool isRunning;
private MonoBehaviour mono;
private Coroutine coroutine;
private YieldInstruction yieldInstruction;
// in case we need to do anything with it
public Coroutine Coroutine
{
get
{
return coroutine;
}
}
// we need a standard Coroutine running for DOTween's YieldInstruction to work
// so we invoke it here, running on the monobehaviour we passed as argument
public WaitForStandardYieldInstruction(MonoBehaviour mono, YieldInstruction yieldInstruction)
{
this.mono = mono;
this.yieldInstruction = yieldInstruction;
this.coroutine = mono.StartCoroutine(IEWaitForYieldInstruction());
}
// this is where the original YieldInstruction is called, to halt the flow until complete
// in the example case we are waiting on `myTween.WaitForCompletion()`
public IEnumerator IEWaitForYieldInstruction()
{
isRunning = true;
yield return yieldInstruction;
isRunning = false;
}
public override bool keepWaiting
{
get
{
return isRunning;
}
}
}
_(I also have an almost identical class WaitForStandardCoroutine that instead of a YieldInstruction takes an IEnumerator as an argument so we can then use MEC to yield wait on standard Unity coroutines. Again it's very basic and is probably missing a heap of required functionality eg if you StopAllCoroutines() it doesnt affect MEC coroutines so you'd also have to yield break; for the MEC yield to complete etc)_
```C#
//MEC coroutine waiting on Unity standard coroutine
private IEnumerator
{
yield return Timing.WaitUntilDone(
new WaitForStandardCoroutine(this, SomeStandardUnityCoroutine())
);
}
private IEnumerator SomeStandardUnityCoroutine() { ... }
```
Your links and explanations and examples (and formatting) were great! I read it all and I think I know how to do it. I hope to find some time tomorrow morning and see if what I have in mind really works :)
great, glad it's of help and looking forward to an update.
usually I just use this, but it'd be great to be able to use the (Custom)YieldInstruction version to avoid having to use that pattern all the time
```C#
_renderer.DOFade(1f, 1).OnComplete(FadeComplete);
fading = true;
while (fading)
{
yield return 0f; // or null depending on MEC or Standard coroutine
}
private void FadeComplete()
{
fading = false;
}
```
regards
J.
for reference here is my class for WaitForStandardCoroutine as a CustomYieldInstruction
as you can see it is almost exactly the same (and will likely come with all the same issues when you try stopping coroutines etc!)
```C#
using System.Collections;
using UnityEngine;
using System;
public class WaitForStandardCoroutine : CustomYieldInstruction
{
private bool isRunning;
private MonoBehaviour mono;
private IEnumerator ie;
private Coroutine coroutine;
public Coroutine Coroutine
{
get
{
return coroutine;
}
}
public WaitForStandardCoroutine(MonoBehaviour mono, IEnumerator ie)
{
this.mono = mono;
this.ie = ie;
this.coroutine = mono.StartCoroutine(IEWaitForCoroutine());
}
public IEnumerator IEWaitForCoroutine()
{
isRunning = true;
yield return mono.StartCoroutine(ie);
isRunning = false;
}
public override bool keepWaiting
{
get
{
return isRunning;
}
}
}
```
Done! :) This is just a test for now, but before continuing I'd like you to check it out and confirm that it does exactly what you meant. Get it here.
Run the setup as usual after importing, and then you'll have a new Tween extension method called WaitForCompletionCY() which, contrary to the older WaitForCompletion(), returns a CustomYieldInstruction instead of a YieldInstruction.
Example (I know you know it, but let me write it anyway):
yield return myTween.WaitForCompletionCY()
If you confirm it's good I'll do the others. I added the CY suffix because I can't remove the older YieldInstructions like WaitForCompletion, since CustomYieldInstructions are only available since Unity 5.3 and I like to keep compatibility with older version too, so every method will have that suffix. If you have a better idea let me know :)
Great thanks, I shall look at this later this evening.
Might be worth me asking the people on the MEC forum post, but i鈥檓 wondering if an optional parameter might be worth it
yield return Timing.WaitUntilDone(myTween.WaitForCompletion(customYield: true))
It鈥檚 longer to write but more obvious to read
I guess there鈥檚 no way to cast the input, or overload , like
yield return Timing.WaitUntilDone((CustomYieldInstruction)myTween.WaitForCompletion())
yield return Timing.WaitUntilDone(myTween.WaitForCompletion<CustomYieldInstruction>())
etc
I dont know about the second one, but im assuming the first one isnt readable from within the WaitForCompletion method?
I think CY suffix is fine, but worth asking the opinion of other developers
Originally I was hoping we could avoid changing DOTween and just do a lambda something like
yield return Timing.WaitUntilDone(myTween.WaitForCompletion() => () => something)
Where something is one of the allowable return types for MEC, but I don鈥檛 know how to implement lambdas in that sort of situation
http://trinary.tech/waituntildone/
Regards
J
Ah, I like the WaitForCompletion(true) idea. That would allow a fake-overload to work and it's neater indeed, thank for the suggestion. Gonna try it immediately
Success (here it is)! Seems very nice like this to me. Gonna implement the others, but if you have some other feedback let me know.
Aaaand here's an updated version with all WaitFor CustomYieldInstructions present :)
great thanks... will take a look now
personally I'm tempted to say this is better because the named optional parameter is more future-proof
myTween.WaitForCompletion(customYield: true)
by just passing true you'll be tied into that API in future... that said what else could it need to return anyway? i'm sure it's probably fine.
ah ok... can just do that anyway presumably...
yield return Timing.WaitUntilDone(myTween.WaitForCompletion(returnCustomYieldInstruction: true));
It's true that the named parameter is more future proof, but the con is that it makes the writing too long, and it can't be enforced. I was thinking of using something short like "customYield" for the parameter name, but while it's shorter to write, "returnCustomYieldInstruction" makes for clearer intellisense hints.
On a secondary note, I wouldn't trust myself nor anyone else not to rename their parameters (though I'll try not to) :P
looking good so far thanks!
I have tested this for example
C#
myTween = transform.DOMoveX(10f, 10f);
// this will cause the Tween to complete early (eg after 100 frames)
Timing.RunCoroutine(TestDOTweenEarlyComplete());
yield return Timing.WaitUntilDone(myTween.WaitForCompletion(true));
and both DOTween.KillAll, myTween.Complete() and DOTween.Kill(myTween.target); act correctly in my early complete function.. the Timing.WaitUntilDone fires early as expected.
Great! :) Will release it publicly in the next days then.
great work. thank you for implementing this so quickly!