- Home /
question about if statement order of execution
I think my understanding of how if statements function is incorrect. The code attached below may highlight my misconception.
I thought that if a condition was true, then the program will run through the if block until the condition is no longer true. At that point, it will move down the block and see if the next if statement is true. If this is not true, it will go back to the beginning again if the main if statement is still true. At this point, a new bool value will be assigned based on the method Initiative() and the code will move down again.
With the code, I am trying to create a very basic combat initiative system. If the player gets a higher initiative roll, then player attacks. If not, player blocks. The attacking and blocking should last for as long as the attackTimer is > than 0. The problem is that the player just stands there and jitters as the code rapidly alternates between true and false.
I suspect the problem has something to do with where I put the bool "value" but I'm not sure how to fix it. It's as if the program doesn't just move politely down the block once value has been initialized, but jumps up again constantly to see if value has changed and, since value is established randomly, when the program goes to see if value has changed, it has!
private void AttackEnemy()
{
if(Vector3.Distance(playerTransform.position, enemyTarget.transform.position) < attackRange)
{
bool value = Initiative();
attackTimer = 21;
attacking = true;
if(value == true && attacking == true)
{
Debug.Log(value + " Should be true");
animation.Play(attack.name);
while (attackTimer > 0 && attacking == true)
{
attackTimer -= Time.deltaTime;
if(attackTimer < 0)
{
attackTimer = 0;
}
if(attackTimer == 0)
{
enemyVitals.AdjustCurrentBeastHealth(-damage);
attacking = false;
}
}
}
else if(value == false && attacking == true)
{
Debug.Log(value + " Should be false");
animation.Play(playerBlock.name);
while (attackTimer > 0 && attacking == true)
{
attackTimer -= Time.deltaTime;
if(attackTimer < 0)
{
attackTimer = 0;
}
if(attackTimer == 0)
{
enemyVitals.AdjustCurrentBeastHealth(0);
attacking = false;
}
}
}
}
}
public bool Initiative()
{
int playerRoll = Random.Range(1,20);
int enemyRoll = Random.Range(1,20);
if(playerRoll > enemyRoll)
{
return true;
}
else
{
return false;
}
}
I would recommend using coroutines ins$$anonymous$$d, which makes ordering sequences of events far simpler.
Answer by Kiwasi · Sep 26, 2014 at 02:05 AM
Code executes from top to bottom. Anything inside an if will only execute if the conditions are true. Then execution goes to the very next statement after the end of the if {}.
while works slightly differently. In a while loop the code inside is executed continually until the condition is true. Its quite common to see infinite while loops, where the condition is not changed by the code inside the braces. Unity will "freeze" without any error messages.
This is all complicated by the system being run by Unity. So everything listed above happens each frame. Your entire while loop executes in a single frame
To solve this you should convert it to a coroutine. Coroutines are Unity's way of running a function across multiple frames. Here is some pseudo code
void Update (){
if(...){
StartCoroutine(AttackEnemy());
}
}
IEnumerator AttackEnemy(){
while (playerHasInitiative) {
performPlayerAttack;
yield return null
}
while (enemyHasInitiative) {
performEnemyAttack;
yield return null
}
}
The code is heavy pseudo code. But the general idea is to put a yield inside a while loop to allow the while loop to execute across multiple frames.
Ok, I've spent sometime researching coroutines and I fear that I've come to exactly the same result! I may not have followed your pseudo code very well but I was basing the structure I used on a Unity tutorial. Can you see why my code is still making my player rapidly oscillate between true and false?`
private void AttackEnemy()
{
if(Vector3.Distance(playerTransform.position, enemyTarget.transform.position) < attackRange)
{
bool value = Initiative();
if(value == true)
{
StartCoroutine(Fight());
}
else if(value == false)
{
StartCoroutine(Defend());
}
}
}
public bool Initiative()
{
int playerRoll = Random.Range(1,20);
int enemyRoll = Random.Range(1,20);
if(playerRoll > enemyRoll)
{
return true;
}
else
{
return false;
}
}
public IEnumerator Fight()
{
float startTime = Time.time;
while(Time.time < startTime + 2)
{
animation.Play(attack.name);
yield return new WaitForEndOfFrame();
}
}
public IEnumerator Defend()
{
float startTime = Time.time;
while(Time.time < startTime + 2)
{
animation.Play(playerBlock.name);
yield return new WaitForEndOfFrame();
}
}
Almost there. But you are still calling AttackEnemy every frame. Hence starting a new coroutine every frame.
$$anonymous$$ey points to note
Only call StartCoroutine once.
$$anonymous$$ake AttackEnemy a coroutine. It should yield the result of the Fight and Defend coroutines.
Initiative should only be called after the fight and defend are finished.
Answer by devstudents · Sep 27, 2014 at 09:52 AM
I finally realised what I was doing wrong with the if statement. The Initiative() method had to be called in the if statements when the timer reached zero. This is the only way I could change the value of "value" in Update without it messing up the if statements. The only problem is that value needs to be initialised elsewhere which means the very first attack or defend is not randomly determined. Here is the fixed code:
public bool value;
public float attackTimer;
public float attackLength;
public AnimationClip attack;
public AnimationClip playerBlock;
void Start()
{
attackTimer = 1;
attackLength = 1;
}
void Update ()
{
AttackEnemy();
}
private void AttackEnemy()
{
if(value == true && attackTimer >= 0)
{
attackTimer -= Time.deltaTime;
animation.Play(attack.name);
if(attackTimer <= 0)
{
value = Initiative();
attackTimer = attackLength;
}
}
else if(value == false && attackTimer >= 0)
{
attackTimer -= Time.deltaTime;
animation.Play (playerBlock.name);
if(attackTimer <= 0)
{
value = Initiative();
attackTimer = attackLength;
}
}
}
public bool Initiative()
{
int playerRoll = Random.Range(1,20);
int enemyRoll = Random.Range(1,20);
if(playerRoll > enemyRoll)
{
return true;
}
else
{
return false;
}
}