public void Slow()
{
if (slowed != null)
StopCoroutine (slowed);
slowed = Slowed();
if (!isSlowed) {
StartCoroutine (slowed);
}
}
IEnumerator Slowed()
{
isSlowed = true;
float tmpSpeed = Speed;
Speed /= 2;
yield return new WaitForSeconds (1f);
Debug.Log ("These Lines Are not Called");
Speed = tmpSpeed; // !
isSlowed = false; // !
}
StartCoroutine needs exact instructions. “slowed” is lower case in brackets, but your IEnumerator is upper case.
The coroutine won’t get Started.
Calling Slowed() runs all the code up to the first yield immeditately. That sets isSlowed to true. You’re only starting the coroutine if isSlowed is set to false.
not sure if i follow, i thought by coroutine is meant “IEnumator Slowed”. This is intended behaviour:
I call slow from somewhere, it should immediately slowdown and after 1 second go back to normal.
So until the first yield it’s working,
also i want to call it only if isSlowed is false otherwise i’d keep slowing and slowing each time by half.
The intent is clear, but there’s a misunderstanding.
The line
slowed = Slowed();
calls the IEnumerator method as if it was a normal method. So it won’t go further than the first [yield] return.
That is, the first part of it executes immediately and sets the flags value, when it resumes the flag has just been set and won’t allow you to start the actual coroutine.
Instead, you can do the following:
private Coroutine _coroutine;
public void Slow()
{
if (_coroutine != null)
{
StopCoroutine(_coroutine);
}
_coroutine = StartCoroutine(Slowed());
}
private IEnumerator Slowed()
{
Debug.Log("before yieling");
yield return new WaitForSeconds(1f);
Debug.Log("after yielding");
_coroutine = null;
}
Note that you can simply store the coroutine instance that is returned by StartCoroutine.
Also note that I removed the boolean, because your code “as is” wouldn’t allow to start another coroutine if you stop a coroutine prior to completion, unless you reset the boolean manually. The version I posted also resets the _coroutine field, so that you do not attempt to stop a coroutine that has run to completion.
if slowed = Slowed(); also calls it, that clears a lot of things…
But the thing is, Slow() can get called multiple times if there is no check, say , this is a tower in a tower defense and it has rate of fire and slows anything that’s near periodically. with enemy speed 10, it goes 5 then 2.5 then1.25 etc each pulse… i need to check if they are already slowed
Oh, I see. That detail of stacking the effect was missing.
But also, with the initial implementation you would not have had the chance to slow it down again when you stopped the routine once, because it would not reset the boolean in this case.
Anyway, what you’re looking for can be done.
There’s one thing to clarify though.
Suppose you call it 3 times, let’s say every 200 ms and the initial speed of 10 is halved with every call.
- 10/2 => 5 at 200ms
- 5/2 = 2.5 at 400ms
- 2.5/2 = 1.25 at 600ms
When would the duration of the first call end? Would it have its own timer, i.e. would it end 400ms after the third call? Or does each call reset “the timer”, i.e. each call extend the duration of the total effect? It’s just a small detail, but it
if its 10 at start check if the dude is already slowed , if he is then no more slowing.
if not, slow him down by half. no more slowing after that. you can kinda see the logic i was going for in the original post.
it just didn’t work. : - )
oh,… this logic sits on enemy, tower just calls it foreach via overlapCircle
So there’s no stacking, my bad…
Same question, would you ignore another call and let it complete the current slow-effect, or would the effects timer start all over again?
If it runs out, no matter how often you call it, it’d be even shorter
private Coroutine _coroutine;
public void Slow()
{
if (_coroutine == null)
{
_coroutine = StartCoroutine(Slowed());
}
}
private IEnumerator Slowed()
{
Debug.Log("before yieling");
yield return new WaitForSeconds(1f);
Debug.Log("after yielding");
_coroutine = null;
}
Now it can only be started if it is currently not running.
If you want to reset the time of the effect, it’d be similar:
private Coroutine _coroutine;
private float _effectTimer;
private float _effectDuration = 1f;
public void Slow()
{
_effectTimer = 0;
if (_coroutine == null)
{
_coroutine = StartCoroutine(Slowed());
}
}
private IEnumerator Slowed()
{
Debug.Log("before yieling");
while (_effectTimer < _effectDuration)
{
_effectTimer += Time.deltaTime;
yield return null;
}
Debug.Log("after yielding");
_coroutine = null;
}
I should have been clearer on the details : - )
And Yes, that’s working : - ))) Thank You