How do I grab a variable from another node outside it's tree

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

I’m trying to grab the players HP, then whenever the enemy collides with the player’s HitBox (an Area2D node) it’ll grab the players current HP value. However no matter what I do, I can’t get the script to locate the player. I’ve thought about using get_node() to get the exact path to it, however whenever I would switch scenes, wouldn’t the path get messed up?

The only other solution I could find was something called a simpleton, but I don’t really know how to set one up, and anywhere I’ve found talking about them has told me that I should avoid them at all costs.

extends StaticBody2D

var att = 1
func dealDamage(att):
	$Player.currHP - att

func _on_HurtBox_area_entered(area):
	dealDamage(att)
func _on_HurtBox_area_entered(area):
           //dealDamage(att)
          if area.name == "Player":
                  area.function_name_in_player() // 
           

ramazan | 2022-03-22 10:05

To do a singleton, make a new script and call it Global or something. Then in the project settings, have it loaded in the Autoload option along with a “Main” Node. You declare variables in this script. Then you call this variable anytime in your project’s script with Global.YourVarName. So in this case, Global.HitPoints. Or you can make a variable on the player object as well that takes this value and makes it the current total. So var playercurrentHP = Global.HitPoints. That doesn’t change the global value but allows you to reference it anytime. So a healing spell could be a function like playercurrentHP = hitPoints.

Just be careful with singletons - they can actually make code more coupled and therefore harder to work on. But by referencing that value in the player script, you always have access to it and can store its temporary value as a separate variable as above.

SnapCracklins | 2022-03-23 01:13

Thank you very much

Fyrol | 2022-03-23 02:20

:bust_in_silhouette: Reply From: w411-3

You can do what ramazan suggested above.

You could also change the priority of things and connect the area node body_entered signal to your player script instead. Then you can just call the player function natively when a body (such as enemy) enters the area. You may have to specify things like the if body.name == "Enemy" to get correct behavior. I also like to put class_name Enemy at the top of my enemy script and then you can do if body is Enemy instead. Whatever makes sense.

Oh this is also good because it is “low coupling”; the player is responsible for player things, other objects don’t directly affect it in code.

What I don’t entirely get is how to grab a variable from the enemy.

So assuming that I have the variables and damage functions in the player script, then I use a similar function of:

func _on_HurtBox_area_entered(area):
      //dealDamage(att)
               if area.name == "Player":
                       area.function_name_in_player() // 

but instead I have this on the player script, using class_name Enemy to give my enemies the class and then I would change it to

func _on_HurtBox_area_entered(area):
      //dealDamage(att)
               if body == Enemy:
                       area.function_name_in_player() // 

Though there are some script issues I don’t quite understand if this is the right way of doing it:
Why is the dealDamage() not in the if statement?
area.function_name_in_player() doesn’t seem to do anything, even when I switch function_name_in_player to dealDamage()
Why are there the // in the example code by ramazam?

Sorry I don’t have a ton of coding experience, so some of this stuff doesn’t entirely make sense to me

Fyrol | 2022-03-23 03:03

First, it’s if body is Enemy in that case, is is a special word for doing that.

I think he just put the // things to indicate comments even though gdscript does #. Comments are lines of code that don’t actually run, they are like notes to annotate things

If you want to do it the second way I described, you need to connect a slightly different signal that has a name like “body_entered” and connect it to the player script instead of enemy. If you do that, your new function should supply a body parameter, like

something_body_entered(body)

and then you can check if the thing is an enemy:

func something_body_entered(body)
    if body is Enemy:
        dealDamage()

The body is just a reference to the thing you want, so if it is the type of object you want, it will have the function you want to call and you can do body.some_function() if you’re not already in that file

w411-3 | 2022-03-23 04:31

Sorry for the late reply, life got in the way.

Thank you very much for the help, though there is one more thing with this, is there a way that I can grab the enemies attack power from the script in a generalized way. So something like body.attackPowerinstead of dealing a flat amount of HP.

Fyrol | 2022-04-09 01:37

If I understand you correctly, yea definitely. If you were to extend a general Enemy class in another script for a more specific enemy with extends Enemy, and then set that attackPower to what it should be for that enemy, you can call body.attackPower no problem if you know it’s an Enemy in general. This would be using ‘inheritance’ if you aren’t familiar

w411-3 | 2022-04-11 01:31

Thank you so much

Fyrol | 2022-04-12 17:10