- Home /
Can't figure out While Loop crash?
I am creating a turn-based grid combat system for my dungeon crawler and have run into an issue with the enemy turns. Each enemy has "x" amount of total ap, they use it each time they move. I have created a While(enemy.apCur > 0) loop but for some reason, it crashes/freezes the game. I have tried running it without the while loop and the movement properly subtracts 1ap when the enemy moves, but for some reason, there is a crash somewhere along the way when it is in a while loop. The reason I want to do this in a while loop is because I want the current enemy to exhaust all of its AP before moving onto the next enemy's turn... currently, all the enemies take their turns in unison without the While loop.
There is a bunch of systems working within my scripts so I will try and cut out the info that is unnecessary within the scripts for ease of readability. First, my combat manager's ChooseNextInLine() function is called to choose the next enemy/player in the turn order (which is determined when the combat is initiated):
public void ChooseNextInLine()
{
GameObject curEntity = _turnOrder.Dequeue();
if (curEntity.GetComponent<PlayerManager>() != null) //PLAYER
{
_turnOrder.Enqueue(curEntity);
PlayerCombatTurn();
}
else if (curEntity.GetComponent<ClassEnemy>() != null) //ENEMY
{
ClassEnemy curEnemy = curEntity.GetComponent<ClassEnemy>();
_turnOrder.Enqueue(curEntity);
EnemyCombatTurn(curEnemy);
}
}
The enemy combat is then called:
enemy.apCur = enemy.apMax;
while (enemy.apCur > 0)
{
if (!enemy.isMoving)
{
enemy.apCur--;
bool tileSelected = false;
Vector3[] possibleTiles = enemy.surroundingTiles.ToArray();
//RANDOMIZE THE ARRAY ORDER
for (int q = 0; q < enemy.surroundingTiles.Count; q++)
{
int rand = RandomNumberGenerator.Instance.rndCombat.Next(enemy.surroundingTiles.Count);
Vector3 temp = possibleTiles[rand];
possibleTiles[rand] = possibleTiles[q];
possibleTiles[q] = temp;
}
//LOOK FOR A TILE TO MOVE TO
foreach (Vector3 tile in possibleTiles)
{
if (!combatOccupiedTiles.Contains(tile) && combatActiveTiles.Contains(tile))
{
tileSelected = true;
enemy.targetPos = tile;
enemy.MovingTurn();
break;
}
}
if (!tileSelected)
{
print("No tile selected");
}
}
}
enemy.isMoving = false; //Failsafe bool assignment
ChooseNextInLine();
The enemy.MovingTurn() function runs a coroutine that Lerps the enemy from its current position to the targetPos. At the beginning of the coroutine enemy.isMoving is set to true, and at the end it is set to false. I believe it is somewhere in here that there is an issue, but I can't figure out where. Here is the appropriate script:
public IEnumerator MoveToPosition(Vector3 targetPos)
{
float time = 0f; //Reset timer
_parentEnemy.isMoving = true;
//MOVE THE PLAYER
while (time < 1)
{
time += Time.deltaTime / (GameManager.Instance.enemyTurnTime); //How much time has passed relatively
transform.position = Vector3.Lerp(curPos, targetPos, time);
yield return null;
}
transform.position = targetPos;
AddSurroundingTiles();
}
public void AddSurroundingTiles()
{
//SET VECTORS
curPos = transform.position;
_parentEnemy.curPos = curPos;
_parentEnemy.surroundingTiles.Clear();
//FIND NEW SURROUNDING TILES
_parentEnemy.surroundingTiles.Add(new Vector3(curPos.x, curPos.y + 1, curPos.z)); //UP
_parentEnemy.surroundingTiles.Add(new Vector3(curPos.x, curPos.y - 1, curPos.z)); //DOWN
_parentEnemy.surroundingTiles.Add(new Vector3(curPos.x + 1, curPos.y, curPos.z)); //RIGHT
_parentEnemy.surroundingTiles.Add(new Vector3(curPos.x - 1, curPos.y, curPos.z)); //LEFT
_parentEnemy.isMoving = false;
}
Any help would be greatly appreciated, thanks!
Answer by SirPaddow · Jun 17, 2019 at 05:41 PM
Your while is actually locking the current frame because it will only end once the ap is 0 (but it will not happen in the current frame). Coroutines only play once per frame too, so your "MoveToPosition" function will never continue after the first time it "yield return"s. To fix it you need to move your while in a coroutine too, and let it go as long as the enemy is moving, something like this:
public void ChooseNextInLine()
{
// player stuff ...
// if enemy
ClassEnemy curEnemy = curEntity.GetComponent<ClassEnemy>();
_turnOrder.Enqueue(curEntity);
StartCoroutine(EnemyCombatTurn(curEnemy));
}
public IEnumerator EnemyCombatTurn(ClassEnemy enemy)
{
enemy.apCur = enemy.apMax;
while (enemy.apCur > 0)
{
enemy.apCur--;
// Get the tile and start moving there
while (enemy.isMoving)
{
yield return null; // < here we let the game loop go to the next frame
}
}
enemy.isMoving = false; //Failsafe bool assignment
ChooseNextInLine();
}
I see that clears it up a bunch! Thanks for the answer and the explanation.
Cheers!