Assigning the value of a variable to equal a different instance of the same node

Godot Version

4.3

Question

I am making a game where the character body 2d player node has a var current_vine variable, the variable (current_vine) = another node (a static body 2d called vines). In the vine script im assigning Global.Player.current_vine = self. This works in my level scene for only one Instance of the vine scene if I place two different ones down current_vine will only ever equal to one of them.

in the physics process for the vine im checking for conditions to assign the Player.current_vine to self or to null depending on whether the conditions are met

I apologize but your question is unclear.

if I place to different ones down current_vine will one ever equal to one of them

If you are asking:
“Can I re-assign a variable’s value from one node reference to another?” then yes, you can.

If you show the relevant bits of code, perhaps we can help you to identify where this might be tripping up for you.

As I am now only guessing, if you are assigning ‘self’ in your vine script in the _ready process to the global variable, one will overwrite the other, the variable can only hold one value.

If you are overwriting the variable based on conditions somewhere, one vine may be setting it to self, and the others re-setting it to null. Instead, if the conditions are met, emit a signal like:

change_players_vine.emit(self)

To set it to null if that is required, the player should check to see if the vine is still valid, and set it null if it is not. But again I am only guessing here at your code.

Hope it helps somehow.

this is my vine scene code:

extends StaticBody2D

@onready var vinesprite = $Line2D

func vineray():
	return $vineray

#cheks line of sight from vine to player
func LOScheck():
	$vineray.target_position = (to_local(Global.player.position))
	if $vineray.is_colliding() and $vineray.get_collider().get("TYPE") == "monkey":
		return true
	else:
		return false

#Checks for LOS but only when inside area
func _physics_process(delta: float) -> void:
	if (abs(Global.player.position.x - position.x) < 32) and (Global.player.position.y - position.y < 120) and LOScheck():
		Global.player.current_vine = self
		Global.player.can_vine = true
	else:
		Global.player.current_vine = null
		Global.player.can_vine = false
		
	vinesprite.set_point_position(1, to_local(Global.player.position))
	
	if Global.player.state == Global.player.States.vine:
		vinesprite.visible=true
	else:
		vinesprite.visible=false
	

in the case of my lvl scene this is what it looks like there are two vine scenes but i only get Global.Player.current_vine to ever equal only one of the 2 vines

thank you for your response :smiley:

I’m unsure how to connect the vine scene to the player scene using signals yet, but I have already found an alternative solution to my problem, thank you for listening and responding either way.

I have tried using signals previously by only reassigning the variable using an area 2d- body_entered_signal and reassigning it to null with a- body_exited_signal so I wouldn’t be constantly assigning and reassigning on the physics process, but I still ran into the same issue. I could have done something wrong and would have rather made a signal of my own if I knew how.

For now, I have found an alternative solution to my problem I would still appreciate it if you could find a way to implement my previous attempt at changing the player’s var current_vine value.

For now, I moved a large part of my “vine swinging” logic to the player script and haven’t ran into the same issue

The problem is the else statement. Lets say you have two vines, vine1 and vine2 in that order in your scene tree. The _physics_process will run vine1 first, then vine2. If you could vine with vine1, it will set your player variables. Then vine2 will unset them. If you could vine with vine2, vine1 will unset them but vine2 will set them. If you had five vines, only the last one will ever work.

Remove your else statement first.

#Checks for LOS but only when inside area
func _physics_process(delta: float) -> void:

	if (abs(Global.player.position.x - position.x) < 32) and (Global.player.position.y - position.y < 120) and LOScheck():
		Global.player.current_vine = self
		Global.player.can_vine = true
	  # Remove the else statement below
          # else:
		# Global.player.current_vine = null
		# Global.player.can_vine = false
		
	vinesprite.set_point_position(1, to_local(Global.player.position))
	
	if Global.player.state == Global.player.States.vine:
		vinesprite.visible=true
	else:
		vinesprite.visible=false
	

Now you can detect when you can vine. It will be the player that now checks when you cannot vine. So in your player script, add this somewhere (like in your _process or _physics process):

# Check first we can vine and not currently vining:
if can_vine and not states.vine:
	# check can still vine and re-use your LOScheck function from the vine
	if not (
		(abs(current_vine.position.x - position.x) < 32) and 
		(current_vine.position.y - position.y < 120) and 
		current_vine.LOScheck()
	):
		current_vine = false
		can_vine = false

That should work.

The only thing I would raise here is that doing it the way you are doing it means you can never have vines too close together. I don’t know if you want vines close together, or how the vines are activated (does the player hit a button to swing or climb?). The way I might do this is to have a vine detector area on my player. When a vine is in reach, ie vine entered area, update the active vine in your current_vine. When area is exited, set it to null. If you make your check area directly up like a tall thin capsule shape, you can have vines very near each other too. (You may not need your LOScheck at all then depending on your other game mechanics).

Anyway, I hope that helps in some way.

PS current_vine should be set to null, not false.

var current_vine: StaticBody2D = null

In fact all you really need to do is just keep current_vine, and the flag is superfluous.

if is_instance_valid(current_vine):

would do the same thing as:

if can_vine:
1 Like

Thank you so much for your further help and response I will be taking it into account

For now, I got rid of my old code and can no longer test how I could have used your solution for my previous attempt as I’m working with my friends on this game and have already pushed my newest version of the vines/player, and I don’t want to break the game for them trying to edit from an older version.

But here is my current solution (now I am only missing an LOScheck() like I previously had but I’ll try to solve that again later):

this is the code for my vine state in the player script the physics are a bit janky for now:

		States.vine:
			#vine ray now exists in player scene; we only get our target from the vine scene
			vineray.target_position = to_local(vine_target)#where to shoot vine to
			var vine_angle = atan2(vineray.target_position.y,vineray.target_position.y)-(PI/2)
			#print("vined")#debugging and such
			velocity.x += ($AnimatedSprite2D.scale.x)*-VINEFORCE*cos(vine_angle)*delta #vineforce vector.x component
			if Input.is_action_pressed("ui_down"):
				velocity.y += GRAVITY * delta #default gravity
			elif Input.is_action_pressed("ui_accept"):
				velocity.y += -VINEFORCE*sin(vine_angle)*delta #vineforce vector.y component
			else:
				velocity.y = 0
			

this is the code to manage state changes in the physics process of my player script:

if is_on_floor() and !tree_behind:
		change_states(delta, States.floor)
	elif tree_behind:
		change_states(delta, States.climb)
	elif can_vine and Input.is_action_pressed("vine"):
		change_states(delta, States.vine)
	else:
		change_states(delta, States.air)

and this is the code from my vine scene:

extends StaticBody2D

const TYPE = "vine"



func _on_area_2d_body_entered(body: Node2D) -> void:
	if body == Global.player:
		body.can_vine = true
		body.vine_target = position


func _on_area_2d_body_exited(body: Node2D) -> void:
	if body == Global.player:
		body.can_vine = false
		body.vine_target = Vector2(0,0)