Computer freezing when while loop is ran

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By TempSchoolAcc#1

So in my game I have an enemy set up. When something enters the enemies attack range, if it is the player, I want the enemy to attack the player. I do this by running a function on the player which deals damage to it. However, this damage only gets applied once. To combat this, I’ve made a variable that triggers when the player enters the enemies attack range and then constantly runs every few seconds to attack the player until it leaves the enemies attack area. The issue with this is that when the while loop is activated, everything freezes.

My Code:

func _on_EnemyCollisionDetection_area_entered(area):
enemyInRange = true
if not area.is_in_group("player"):
	pass
if area.is_in_group("player"):
	while enemyInRange == true:
		if get_node("AttackDelay").is_stopped() == true:
			get_node("AttackDelay").start()
			rng.randomize()
			var randomNumber = rng.randf_range(2.0, 7.0)
			var damageDealt = int(round(randomNumber))
			area.takeDamage(damageDealt)
			yield(get_tree(), "idle_frame")

func _on_EnemyCollisionDetection_area_exited(area):
	enemyInRange = false

Everything works fine and I get no errors in the console, however once the while loop is activated the game and my computer freeze and the only way to get out of it (the way that I’ve discovered and seems to work) is to press ctrl+alt+delete and just cancel from there (my school computer has task manager blocked.)

I see no reason for this to happen? It is an older school computer although it should run smoothly still.

Thank you for any help :slight_smile:

:bust_in_silhouette: Reply From: jgodfrey

Yeah, you don’t want that while loop in there - that’s what’s hanging your system. That looks like an infinite loop. Here’s what’s happening…

  • You set enemyInRange to true
  • You enter your while enemyInRange == true: loop
  • Nothing inside that loop ever recalculates the value of enemyInRange
  • So, it’s value is always true, and you never exit the loop

Essentially, nothing else processes in your code, as you’re permanently stuck in that loop.

So, you’ll need to find a different way to process that. Instead, you probably want to wire both the area_entered and area_exited signals. Then, set a a boolean flag in the area_entered callback when your player is detected and unset the same flag when your player is exits the area (in area_exited).

Then, _process() (which fires once per frame) if your flag is true to do the things you have in your original loop, but without the while loop.

So, specifically, something like this (untested):

var enemyInRange = false

func _on_EnemyCollisionDetection_area_entered(area):
    if area.is_in_group("player"):
        enemyInRange = true

func _on_EnemyCollisionDetection_area_exited(area):
    if area.is_in_group("player"):
        enemyInRange = false

func _process(delta):
    if enemyInRange:
        if get_node("AttackDelay").is_stopped() == true:
            get_node("AttackDelay").start()
            rng.randomize()
            var randomNumber = rng.randf_range(2.0, 7.0)
            var damageDealt = int(round(randomNumber))
            area.takeDamage(damageDealt)
            yield(get_tree(), "idle_frame")

jgodfrey | 2020-11-05 03:20

Awesome! This works great, thank you. The only issue I had before was that I couldn’t transfer over the area.takeDamage function because the area variable only came with the function. For anyone following along I’ve just made another variable that the function sets its area to. If that doesn’t make sense here is the code I have now:

func _on_EnemyCollisionDetection_area_entered(area):
	if not area.is_in_group("player"):
		pass
	if area.is_in_group("player"):
		enemyInRange = true
		areaExported = area

func _on_EnemyCollisionDetection_area_exited(area):
	if not area.is_in_group("player"):
		pass
	if area.is_in_group("player"):
		enemyInRange = false
		areaExported = area

func _process(delta):
	if enemyInRange == true:
		if get_node("AttackDelay").is_stopped() == true:
			get_node("AttackDelay").start()
			rng.randomize()
			var randomNumber = rng.randf_range(2.0, 7.0)
			var damageDealt = int(round(randomNumber))
			areaExported.takeDamage(damageDealt)
			yield(get_tree(), "idle_frame")

Thanks for your help, Jgodfrey! Enjoy your day/night :slight_smile:

TempSchoolAcc#1 | 2020-11-05 03:40