Here is the code I am working with. I have been able to figure out that var health is null but I cant figure out why since it should be set to max_health on ready (and yes the max health export is populated). Any and all help is appreciated, thanks!

signal health_changed
@export var max_health : int
var health

func _ready():
health = max_health
emit_signal('health_changed', health * 100 / max_health)

func take_damage(amount):
health -= amount
emit_signal('health_changed', health * 100 / max_health)
if health <= 0:
explode()
From a different script that should initiate the take_damage function
func _on_body_entered(body):
print("bullet hit")
explode()
if body.has_method('take_damage'):
body.take_damage(damage)

  • xyz replied to this.
  • crabrangyoon Oh, I get it. This is base class script. Inherited scripts won't call base class _ready(). You need to do it explicitly by calling super._ready() from inherited _ready()

    crabrangyoon What happens in health_changed signal handler(s)?
    Also, declaring var health: int may help you catch the error more easily as the compiler will report if you're trying to assigning to it something that you shouldn't.

      xyz

      Declaring "var health : int" was helpful. I can see now that the issue is health = max health in _ready() is not resolving properly for non-player characters, it seems to work for the player though which is just as confusing to me.
      I don't believe I even make it far enough to kick off the health_changed signal. Can you expand more on what you mean by signal handlers (sorry very new still)

      • xyz replied to this.

        xyz

        Ah I don't have that set up yet I don't think. But even when commenting that out and just leaving myself with print(health) in its place it returns null (or 0 with var health : int)

        • xyz replied to this.

          crabrangyoon Print the value of max_health and health in _ready(). And print the value of amount in take_damage().

            xyz

            Printing max_health and health in _ready() output 100 for both (which is the player value) but nothing that seemed to represent the enemy values. Printing amount in take_damage() output the proper amount of damage. Im not sure if this will make a difference but this script is being inherited by both the player and enemy scripts. Could that be causing any issues?

            • xyz replied to this.

              crabrangyoon output the proper amount of damage.

              Each and every time it's called? Even when the error is thrown? How so if you stated in your initial post that you've been able to determine that health is null. Btw which script line is throwing that error?

                xyz

                yes it seems to output the proper amount of damage each time its called. I was able to determine health was returning null by commenting out the following in the take_damage function:
                health -= amount
                emit_signal('health_changed', health * 100 / max_health)
                if health <= 0:
                explode()

                and replacing it with:
                print(amount)
                print(health)
                print(max_health)

                When an enemy shoots the player the output looks like this:
                10
                100
                100
                When the player shoots an enemy the error Invalid operands 'Nil' and 'int' in operator '-'. occurs
                However, when I declared var health : int, it will output the following on an enemy being hit:
                10
                0
                100
                This amount and max health are correct, but the health is incorrect.

                • xyz replied to this.

                  crabrangyoon Some other script may be nullifying health in the enemy object. So search for all occurrences of assignment to health throughout the project.

                    xyz

                    I couldn't find any other reference to health in my other scripts. I tried sticking the health = max_health in the enemy script in addition to the original script in _ready() and it appears to be working as intended now. I'm still unsure why it wasn't working the way I thought it would earlier (or how this is really any different) but as long as this isn't just a terrible band-aid fix I think its all good now

                    • xyz replied to this.

                      crabrangyoon Oh, I get it. This is base class script. Inherited scripts won't call base class _ready(). You need to do it explicitly by calling super._ready() from inherited _ready()