Slow down State.Cooloff?

In my game, I am triggering damage when the projectile makes contact with the enemy. How can I modify this script:

    private void NextState()
    {
        switch(state)
        {
            case State.Aiming:
                state = State.Shooting;
                float shootingStateTime = 0.1f;
                stateTimer = shootingStateTime;
                break;
            case State.Shooting:
                state = State.Cooloff;
                float cooloffStateTime = 0.1f;
                stateTimer = cooloffStateTime;
                break;

            case State.Cooloff:
                ActionComplete();
                break;
        }
    }

…so that the ActionComplete() doesn’t until the projectile sends a signal to say that collision has occurred? This is what I have so far on my Projectile:

        if(other.tag == "Unit")
        {
            if (other.TryGetComponent<Unit>(out var unit))
            {
                if (other.TryGetComponent<HealthSystem>(out var healthSystem))
                {
                    healthSystem.Damage(projectileDamage);
                   //I thought this line below would work, but it is causing issues with my BaseAction
                    OnActionCompleteScriptTrigger?.Invoke(this, EventArgs.Empty);
                }
                
                OnAnyActionCompleted?.Invoke(this, EventArgs.Empty);
            }
        } 
        else
        {
            Debug.Log("The projectile didn't hit an enemy.  You better look at your code");
        }
    }

Any help is appreciated :slight_smile:

The state transition is triggered when the timer has run out. In your case, you want to manually trigger the state so a simple solution is to set the stateTimer to something that will never be reached by the game (like float.MaxValue) when transitioning to State.Shooting and then, when you get the signal that the projectile has hit the target, you manually set that to 0 (or -1) and the state will transition to State.Cooloff


Edit
I personally don’t like switch clauses, and especially state machines running on them, so in my own project I have a different type of state machine. Here is a simple example:

Create a method for each state - note that they all have the same signature (the TODO’s are addressed a little later)

private void AimingState(float deltaTime)
{
    Vector3 aimDir = (targetUnit.GetWorldPosition() - unit.GetWorldPosition()).normalized;
    
    float rotateSpeed = 10f;
    transform.forward = Vector3.Lerp(transform.forward, aimDir, deltaTime * rotateSpeed);

    // Moved this out of Update
    stateTimer -= deltaTime;
    if (stateTimer <= 0f)
    {
        float shootingStateTime = 0.1f;
        stateTimer = shootingStateTime;
        // TODO: Transition to ShootingState
    }
}
private void ShootingState(float deltaTime)
{
    if (canShootBullet)
    {
        Shoot(); // target damage has been removed from Shoot()
        canShootBullet = false;
    }

    // Moved this out of Update
    stateTimer -= deltaTime;
    if (stateTimer <= 0f)
    {
        float waitingStateTime = 0.1f;
        stateTimer = waitingStateTime;
        // TODO: Transition to WaitForHitState
    }
}
private void WaitForHitState(float deltaTime)
{
    // Note that is a 'limbo'. We're just waiting for the projectile to reach the target
}
private void CoolDownState(float deltaTime)
{
    ActionComplete();
}

Now, we create a variable to hold the current state

private System.Action<float> currentState;

When the action starts, we’ll set ‘aiming’ as the current state

public override void TakeAction(GridPosition gridPosition, Action onActionComplete)
{
    targetUnit = LevelGrid.Instance.GetUnitAtGridPosition(gridPosition);

    float aimingStateTime = 1f;
    stateTimer = aimingStateTime;
    currentState = AimingState;

    canShootBullet = true;

    ActionStart(onActionComplete);
}

As mentioned earlier, we need to transition states. This is simply done by replacing the current state with another

// TODO: Transition to ShootingState
currentState = ShootingState;

// TODO: Transition to WaitForHitState
currentState = WaitForHitState;

Now we need Update to run the current action

private void Update()
{
    if (!isActive)
    {
        return;
    }

    currentAction?.Invoke(Time.deltaTime);
}

And lastly, we need to react when the projectile hits the target. I will go with the assumption that we subscribed to a OnHitTarget event on the projectile

private void ProjectileOnHitTarget(object sender, EventArgs e)
{
    targetUnit.Damage(40);
    currentState = CoolDownState;
}

That’s about it. States will transition when their timers run out, but the wait state will only transition when the event is triggered.

We technically don’t need the WaitForHitState because the ShootingState won’t do anything after it has run once except countdown its timer, so if you want to go that route, it will look like this

private void ShootingState(float deltaTime)
{
    if (canShootBullet)
    {
        Shoot(); // target damage has been removed from Shoot()
        canShootBullet = false;
    }
}

No timer countdown here 'cos we will stay here until the bullet hits the target

1 Like

Solved! Thank you so much!!!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms