i++ won't work, it just stays at 0

for some reason, when my enemy shoots a projectile 5 times it doesnt set the canBarrage to false and so keeps on firing even though the for loop says that it should only be done barrageNumber of times (which i set to 5 in the inspector)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurretEnemy : Log
{
    public GameObject projectile;
    public float fireDelay;
    private float fireDelaySeconds;
    public float barrageDelay;
    private float barrageDelaySeconds;
    public bool canFire = true;
    public int barrageNumber;
    public bool canBarrage = true;

    private void Update()
    {
        fireDelaySeconds -= Time.deltaTime;
        if(fireDelaySeconds <= 0)
        {
            canFire = true;
            fireDelaySeconds = fireDelay;
        }
        barrageDelaySeconds -= Time.deltaTime;
        if(barrageDelaySeconds <= 0)
        {
            canBarrage = true;
            barrageDelaySeconds = barrageDelay;
        }
    }

    public override void CheckDistance()
    {
        if (Vector3.Distance(target.position,
                            transform.position) <= chaseRadius
           && Vector3.Distance(target.position,
                               transform.position) > attackRadius)
        {
            if (currentState == EnemyState.idle || currentState == EnemyState.walk
                && currentState != EnemyState.stagger)
            {
                if (canBarrage)
                {
                    for (int i = 0; i < barrageNumber; i++)
                    {
                        if (canFire)
                        {
                            Vector3 tempVector = target.transform.position - transform.position;
                            GameObject current = Instantiate(projectile, transform.position, Quaternion.identity);
                            current.GetComponent<Projectile>().Launch(tempVector);
                            canFire = false;
                            ChangeState(EnemyState.walk);
                            anim.SetBool("wakeUp", true);
                            Debug.Log(i);
                        }
                        else
                        {
                            return;
                        }
                    } 
                    canBarrage = false; 
                }
                
            }
        }
        else if (Vector3.Distance(target.position,
                           transform.position) > chaseRadius)
        {
            anim.SetBool("wakeUp", false);
        }
    }
}

Firstly, I hope you don’t mind but I’ve converted your reply from an Answer to a Comment. You’re not proposing an answer in it and we prefer that this kind of material is not put in an answer. Only answers (or potential answers) should be posted as such.

Anyway, I can see what’s going on.

i = 0: canFire is true so you instantiate but you also set CanFire to false.

i = 1: canFire is false so the code goes through to else statement and returns control to whatever called this code - a higher level method. The loop never goes any further

Other than this diagnosis, I don’t think I can really help since the function is what’s needed by your game. Should you perhaps only set canFire to false after instantiating all the projectiles? That’s your game decision, of course…

To offer a bit of clarification, let’s break down what your function is currently doing:

if(canBarrage)
{
	for(int i = 0; i < barrageNumber; i++)
	{
		if(canFire) // Checked once per barrage?
		{
			// First pass through the loop,
			// assuming "canFire" is true
			canFire = false;
		}
		else
		{
			// Second pass through the loop
			return; // Exit the CheckDistance() function altogether
		}
	}
	canBarrage = false; // TYPICALLY NOT REACHABLE
}

Even if this could make more than one pass, however, there’s more confusing logic before you reach that point.

“fireDelaySeconds” and “barrageDelaySeconds” both cycle independently and regardless of their current state. An enemy could fire, then fire again a single frame later if it has perfect timing.

Additionally, the AND (&&) operator has priority over the OR (||) operator. It’s hard to tell your exact intent here, since we don’t have information about the “currentState” variable, but that piece of logic is:

if(idle || (walk && !stagger)) // if idle or (walking & not staggering)

Considering that fire/barrage activity is making them walk ( ChangeState(EnemyState.walk) ), are they supposed to be able to stagger when not already walking?

Anyway, back to the main point here…

If you’re looking to progress the loop instead of breaking out of the entire function, you could change:

return; // Exit function
break; // Exit current loop
continue; // Skip to next loop iteration

However, as has been noted, you would still never fire more than one shot right now, since you MUST be able to “barrage” in order to “fire”, you MUST be able to “fire” in order to “barrage”, and you lose the ability to "fire "upon the first shot. Currently, however, if you can “barrage” when you can’t “fire”, you also still exit the function without ever reaching the canBarrage = false; line unless “barrageNumber <= 0” (but then no shots would ever be fired).

Also, as an aside: “TurretEnemy” derives from “Log”? What does that even mean?


The two concurrent timers makes it seem like you intend to fire individual shots at regular intervals, with occasional bursts of shots (always starting with a burst). With this in mind, a significant change to your implementation would make that possible:
bool canFire
{
	get
	{
		return fireDelaySeconds <= 0f;
	}
}

bool canBarrage
{
	get
	{
		return barrageDelaySeconds <= 0f;
	}
}

void Update()
{
	fireDelaySeconds -= Time.deltaTime;
	barrageDelaySeconds -= Time.deltaTime;
}

public override void CheckDistance()
{
	if([close enough])
	{
		if([viable state])
		{
			if(canFire)
			{
				fireDelaySeconds = fireDelay;
				Fire(1);
				//StartCoroutine(Fire(1));
			}
			if(canBarrage)
			{
				barrageDelaySeconds = barrageDelay;
				Fire(barrageNumber);
				//StartCoroutine(Fire(barrageNumber));
			}
		}
	}
}

void Fire(int shotsToFire)
{
	for(int i = 0; i < shotsToFire; i++)
	{
		// Create projectiles here instead
	
		// If "barrage" is intended to occur over several frames, this could
		// instead be a Coroutine to spread the shots out over a period of time
	
		// If "barrage" occurs over several frames, but you want it to
		// be a consistent, smooth pattern of shots, you'll want to
		// write smarter logic for creating sub-frame timing
	}
}

Okay! Now we’re getting to it. We’ve struggled to answer this because we didn’t know that you want to introduce a wait between firing. Have a look at this code:

using System.Collections;
using UnityEngine;

public class Test1 : MonoBehaviour
{
    bool canFire = true;

    private void Update()
    {
        if (canFire)
        {
            print("Fire!");
            canFire = false;
            StartCoroutine(wait(1));
        }
    }

    IEnumerator wait(float waitTime)
    {
        yield return new WaitForSeconds(waitTime);
        canFire = true;
    }
}

What it does is fire when it can and then wait for 1 second before canFire is set to true again. It uses a programming technique called Coroutines. A full explanation is a bit too much to put into this reply. However, you can change the wait time in the StartCoroutine statement to whatever you want and that will be the gap between successive firings. Shouldn’t be too hard to adapt and put in your code.

Coroutines don’t stop the program - everything else carries on as per normal but it’s a bit like setting an alarm for some point in the future. When the alarm goes off, canFire is set to true and you are ready for the next instantiation.

Well, I’ve just spent 10 minutes looking at the code and I have to say I don’t understand it. There are so many ifs in it that it beats me. When I was first paid to program, I was told that code should have single entry point and single exit point (SESE). Hence, lots of nested if-then-else constructs. Many people now discredit that in favour of “Fail Fast” and they often code:

if (Vector3.Distance(target.position, transform.position) > chaseRadius)
     return;
if (Vector3.Distance(target.position, transform.position) <= attackRadius)
     return; etc...

This mechanism is called a Guard Clause or Guard Statement or a Guard Block. It allows following code to be expressed more simply. It strikes me you could use a few of these to remove all the edge cases from your code.

I know that you don’t mean that i++ isn’t working. You clearly mean that the code is not being called and I think simplification is in order to help diagnose. Well that and a good dose of Debug.Log to test what’s being called and what’s not…

Alternatively, a good sit-down with a chapter on State Machines but that’s a different answer altogether…

@SurreyMuso
basically the only part that matters is here, instead of setting canBarrage to false i think what’s happening is when i use the return statement, it resets the for loop, so the for loop was never completed and thus canBarrage can never be set to false, is there a way that i can recheck the if statement if it’s not true? btw when i use debug.log on i it says that it’s 0, i might’ve put it in the wrong place though.

if (canBarrage)
                 {
                     for (int i = 0; i < barrageNumber; i++)
                     {
                         if (canFire)
                         {
                             Vector3 tempVector = target.transform.position - transform.position;
                             GameObject current = Instantiate(projectile, transform.position, Quaternion.identity);
                             current.GetComponent<Projectile>().Launch(tempVector);
                             canFire = false;
                             ChangeState(EnemyState.walk);
                             anim.SetBool("wakeUp", true);
                             Debug.Log(i);
                         }
                         else
                         {
                             return;
                         }
                     } 
                     canBarrage = false; 
                 }

@SurreyMuso
basically the only part that matters is here, instead of setting canBarrage to false i think what’s happening is when i use the return statement, it resets the for loop, so the for loop was never completed and thus canBarrage can never be set to false, is there a way that i can recheck the if statement if it’s not true? btw when i use debug.log on i it says that it’s 0, i might’ve put it in the wrong place though.

if (canBarrage)
                 {
                     for (int i = 0; i < barrageNumber; i++)
                     {
                         if (canFire)
                         {
                             Vector3 tempVector = target.transform.position - transform.position;
                             GameObject current = Instantiate(projectile, transform.position, Quaternion.identity);
                             current.GetComponent<Projectile>().Launch(tempVector);
                             canFire = false;
                             ChangeState(EnemyState.walk);
                             anim.SetBool("wakeUp", true);
                             Debug.Log(i);
                         }
                         else
                         {
                             return;
                         }
                     } 
                     canBarrage = false; 
                 }