I have two scripts that will do mostly the same thing, which is call a coroutine, and slowly regen stamina. Thing is in the second script, it instantly fills the stamina up to max, when the other increases it by 5 every second
This doesn't make any sense cause I basically copied the second script part from the first, and the first works perfectly. I have no idea why
Has all my code, but the important stuff is this:
(Not working)
void StopSprinting()
{
isSprinting = false;
moveSpeed = 5;
CallStam();
}
async void CallStam()
{
await Task.Delay(2500);
player.SetStamBool();
}
(Works perfectly)
void Melee()
{
Vector2 attackDetectionPosition = attackDetection.position;
Vector2 attackDirection = attackDetectionPosition movementDirection;
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(attackDirection, attackRange, enemyLayers);
foreach (Collider2D enemy in hitEnemies)
{
Debug.Log("We hit " enemy.name);
}
if (player.CurrentStamina > 10)
{
player.CurrentStamina -= 10;
//Deal Damage
player.StamRegen = false;
CallStam();
}
else if (player.CurrentStamina > 0)
{
//Deal half damage
player.StamRegen = false;
CallStam();
}
}
async void CallStam()
{
await Task.Delay(2500);
player.SetStamBool();
}
public void SetStamBool()
{
StamRegen = true;
StartCoroutine(RegenerateStamina());
}
IEnumerator RegenerateStamina()
{
while (StamRegen == true && CurrentStamina < MaxStamina)
{
yield return new WaitForSeconds(1);
CurrentStamina = 5;
}
if (CurrentStamina > MaxStamina)
{
int subtractionAmount = CurrentStamina - MaxStamina;
CurrentStamina -= subtractionAmount;
}
}
Sorry if I don't respond for a while
CodePudding user response:
Your problem is most likely that the StopSprinting
function is being called over and over again which makes WaitForSeconds
redundant. You need to find a way to only trigger the coroutine function once, and here is my suggestion:
public void SetStamBool()
{
if(!StamRegen){
StamRegen = true;
StartCoroutine(RegenerateStamina());
}
}
Running should obviously set StamRegen
to false for this logic to work.
On a side note, you should know that you can use a boolean on its own as a condition. So StamRegen == true
is the very same as StamRegen
on its own. Also, please keep the same casing convention for all your variables not to mix them with function names or classes, so StamRegen
should be camelCase stamRegen
to match the variable casing that you are following.
CodePudding user response:
First of all: These are not doing the same! In the one hat is working you set
player.StamRegen = false;
in the other one you forgot that so it is possible that another routine is still running at the same time.
In general you could being end up with multiple parallel runnin routines!
Either way your mixing in of the async calls is not good! I would rather explicitly interrupt any running Coroutine and simply put the initial delay into the routine itself!
In the player have
// holds the currently running routine
private Coroutine _currentStaminaRegenaration;
public void RegenerateStamina(float initialDelay)
{
// if there is already a routine running stop it
if(_currentStaminaRegenaration != null)
{
StopCoroutine(_currentStaminaRegenaration);
}
_currentStaminaRegenaration = StartCoroutine(RegenerateStaminaRoutine(initialDelay));
}
IEnumerator RegenerateStaminaRoutine(float initialDelay)
{
// before starting the regeneration first run your initial delay
yield return new WaitForSeconds(initialDelay);
while (CurrentStamina < MaxStamina)
{
yield return new WaitForSeconds(1);
CurrentStamina = 5;
}
// Instead of your complex check and subtraction simply use
CurrentStamina = Mathf.Min(CurrentStamina, MaxStamina);
// never sure if this is needed but just in case reset
_currentStaminaRegenaration = null;
}
and now everywhere instead of
player.StamRegen = false;
CallStam();
all you need to do is calling
player.RegenerateStamina(2.5f);
if the 2.5f
seconds is the same delay everywhere anyway you ca as well code it directly into the Coroutine itself then you don't even need to pass it in as parameter.