0 votes

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 :)

in Engine by (28 points)

1 Answer

+1 vote
Best answer

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.

by (10,914 points)
selected by

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")

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 :)

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.