So listen, I have an enemy "CharacterBody2D" node in one scene,
And I have a player "CharacterBody2D" node in another scene,
They are both children of the root node in my main scene,
My player has a child Node2D named "PlayerTop", placed above the player

I just want to know how to use gdscript to move my enemy's position to the "PlayerTop".

To sound less confusing, I just want to know how to move one node to the position of another node. Y'know, point A to point B. I feel like it should be the easiest thing in the world, yet nobody on the internet can explain it to me.

In my enemy's script, I defined the node path like:
@onready var player = get_node("/root/Main/node_wanted/child_node_wanted).global_position

And later on in that same enemy's script, when a certain condition is met, I said:
self.position = player

And then It resets my player at the position it started in when the game started running, with my enemy underneath it.

Anyway, BOTTOM LINE:
I just want to move a node to the position of a different node (or it's child). Am I going about this wrong??

So you probably want the enemy to have a boolean state is_carried and have it(the enemy script) get the node and copy the global position of the players 'PlayerTop' node, then set that position value as it's own global position value as long as the is_carried: state is true. In the _process() or _physics_process() loop. So updating it's position either every frame or every physics frame then. Which ever you prefer.

To analyze what is actually going wrong I think you'll have to share your actual enemy script here.

    I actually should have better ordered my previous post, one of your issues is that you are getting the global position @onready so just as the players global position is at the time the enemy node fires it's _ready() after entering the scene tree.

    You should be getting the global position of the 'PlayerTop' node within the if statement checking the is_carried boolean state. Which, remember, would be in either process or physics process, so updated every frame or every physics tick. Again whichever you prefer. (I'd prefer physics probably)

      Megalomaniak`

      extends CharacterBody2D
      
      enum States {FLOOR, AIR, GRABBED}
      var state = States.AIR
      
      @export var speed = 35
      @export var gravity = 17
      @export var direction = -1
      
      @onready var player_top = get_node("/root/Main/Mario/PlayerTop").get_global_position()
      
      
      
      func _physics_process(delta):
      	
      	match state:
      		States.AIR:
      			if is_on_floor():
      				state = States.FLOOR
      			velocity.y += gravity
      			
      	match state:
      		States.FLOOR:
      			if not is_on_floor():
      				state = States.AIR
      			velocity.x = speed * direction
      
      	$AnimatedSprite2D.play("walk")
      
      	set_velocity(velocity)
      	set_up_direction(Vector2.UP)
      	move_and_slide()
      
      func _on_top_checker_body_entered(body):
      	$AnimatedSprite2D.flip_v = true
      	self.position = player_top

      Kinda embarrassed to show my code, but this is the script for the enemy.

      "Mario" is the player (CharacterBody2D)

      I'm working on picking up and throwing enemies in Super Mario Bros 2, but right now I just want to teleport the enemy to the Node2D ("PlayerTop") above the player's head.

      This code instead teleports the enemy to the initial spawn position of the player (not the "PlayerTop" node above the player's head)

      So I guess I'm just kind of mentally struggling with it. I just want to teleport the enemy to the position above the player's head, wherever the player might be.

        You need to assign PlayerTop's current global position to node's global position:

        self.global_position = get_node("/root/Main/Mario/PlayerTop").global_position

        What were you doing instead was assigning PlayerTop's initial global position to node's local position. Which of course resulted in unwanted behavior.

        NJL64 try

        extends CharacterBody2D
        
        enum States {FLOOR, AIR, GRABBED}
        var state = States.AIR
        
        @export var speed = 35
        @export var gravity = 17
        @export var direction = -1
        
        @onready var player_top = get_node("/root/Main/Mario/PlayerTop")
        
        
        func _physics_process(delta):
        	match state:
        		States.AIR:
        			if is_on_floor():
        				state = States.FLOOR
        			velocity.y += gravity
        			
        	match state:
        		States.FLOOR:
        			if not is_on_floor():
        				state = States.AIR
        			velocity.x = speed * direction
        
        	$AnimatedSprite2D.play("walk")
        
        	set_velocity(velocity)
        	set_up_direction(Vector2.UP)
        	move_and_slide()
        
        func _on_top_checker_body_entered(body):
        	$AnimatedSprite2D.flip_v = true
        	self.position = player_top.get_global_position()

        Though I think you are still going to have an issue with this. The position updating should happen constantly while the carry state is true, thus either it should be happening in process or physics_process rather than on body entered signals firing.

          Megalomaniak Hmm I'm trying to experiment around with all the advice you guys are giving. Sorry, if some of this stuff is still a little over my head because I haven't mastered it.

          I edited my code to this, which results in both the player and enemy shooting vertically towards whatever side your standing on it incredibly fast.

          extends CharacterBody2D
          
          enum States {FLOOR, AIR, GRABBED}
          var state = States.AIR
          
          @export var speed = 35
          @export var gravity = 17
          @export var direction = -1
          
          @onready var player_top = get_node("/root/Main/Mario/PlayerTop")
          
          func _physics_process(delta):
          	
          	match state:
          		States.AIR:
          			if is_on_floor():
          				state = States.FLOOR
          			velocity.y += gravity
          			
          	match state:
          		States.FLOOR:
          			if not is_on_floor():
          				state = States.AIR
          			velocity.x = speed * direction
          			
          	match state:
          		States.GRABBED:
          			self.position = player_top.get_global_position()
          			
          
          	$AnimatedSprite2D.play("walk")
          
          	set_velocity(velocity)
          	set_up_direction(Vector2.UP)
          	move_and_slide()
          
          func _on_top_checker_body_entered(body):
          		state = States.GRABBED

          Maybe I'm having a little trouble understanding what you guys are trying to communicate. So are you saying the code that teleports the enemy to the PlayerTop needs to be in the physics process function in a match state, and if so, what do I put in the top_checker_body_entered function? I need that to tell if my player is on top of the enemy.

          I really appreciate all the advice by the way from everybody. I'm still rereading and going through everyone's comments to try to understand and apply it better.

          Wait it a minute! So this is weird!

          It worked! When I jumped real quick instead of continuing to stand. Something in my code is still screwing it up and I'm not sure if it's related to the enemy's movement code.

          Gotta figure out what's making it shoot them both vertically when I step on the enemy's top checker...

          • xyz replied to this.

            NJL64 The way you use match is breaking your code logic. Only one match statement is needed. Especially if code inside is changing the matched variable. Otherwise you can lose mutually excluded execution of blocks, which is the main purpose of using match in the first place.
            https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#match
            Fix that first and then see if any unwanted behavior persists.

              NJL64

              match state:
              	States.AIR:
              		if is_on_floor():
              			state = States.FLOOR
              		velocity.y += gravity
              
              	States.FLOOR:
              		if not is_on_floor():
              			state = States.AIR
              		velocity.x = speed * direction
              
              	States.GRABBED:
              		self.position = player_top.get_global_position()

                xyz Oh! Okay!

                I did do that in the meantime, but wasn't sure if it was what you meant.

                It didn't end up fixing the issue, but thank you for clarifying and helping me to learn to format my code more properly.

                • xyz replied to this.

                  NJL64 Second thing, and I mentioned this earlier; if you want to put one node in the position of another node that's in a different level in the scene hierarchy, you have to do it in common coordinate space. Otherwise the positions will mismatch. Most convenient way to do it is in global space:

                  self.global_position = player_top.global_position

                  Or same thing using getter/setter functions:

                  self.set_global_position(player_top.get_global_position())

                  You likely need to also disable collision for the enemy while it is carried, or change it's collision layers so it doesn't collide with the player while being carried.

                    NJL64 It didn't end up fixing the issue, but thank you for clarifying and helping me to learn to format my code more properly.

                    It's not a matter of formatting. Your code logic was broken, potentially leading to more bugs down the line as you add more code to your match blocks. If your first match block changed the value of state to FLOOR, the block after it would also execute, resulting in execution of both blocks (for state AIR and state FLOOR) in the same frame.

                      Megalomaniak Thanks for the tip. I did that in the meantime as well.

                      States.GRABBED:
                      			$AnimatedSprite2D.flip_v = true
                      			$CollisionShape2D.disabled = true
                      			$TopChecker/CollisionShape2D.disabled = true
                      			$FloorChecker.enabled = false
                      			self.global_position = get_node("/root/Main/Mario/PlayerTop").global_position

                      I just can't figure out why my character and enemy are blasting vertically once the player makes contact with the enemy's topchecker collision.

                      I'll experiment more with what you said about the States (in your comment that came after this one)

                      Thank you again!

                      xyz Would using "continue" help? I remember when I ported this code over to Godot 4 I was having issues using continue.

                      The issue of my character being blasted vertically upward may have to do with that character's script (go figure) because I tried deleting his states.

                      I'll leave the code here if anyone wants to volunteer to help and in the meantime, I'm gonna try to look over it and brainstorm myself when I can.

                      This is Mario's script (the body that enters the enemy's top checker)

                      extends CharacterBody2D
                      
                      enum States {AIR, FLOOR} # counting starts with zero, unless you assign 1 to the first state
                      var state = States.AIR # default
                      
                      @export var speed = 120
                      @export var gravity = 17
                      @export var jump_force = -450 # negative is up in Godot
                      
                      
                      func _physics_process(delta):
                      		
                      	match state:
                      		States.AIR:
                      			if is_on_floor():
                      				state = States.FLOOR
                      			$AnimatedSprite2D.play("jump")
                      			velocity.y += gravity
                      			
                      		States.FLOOR:
                      			if not is_on_floor():
                      				state = States.AIR
                      
                      			if velocity != Vector2.ZERO:
                      				$AnimatedSprite2D.play("walk")
                      			else:
                      				$AnimatedSprite2D.play("idle")
                      			
                      			if Input.is_action_just_pressed("jump"):
                      				velocity.y = jump_force
                      			
                      			
                      	get_input()
                      	
                      	set_velocity(velocity)
                      	set_up_direction(Vector2.UP)
                      	move_and_slide()
                      	
                      	
                      func get_input():
                      	velocity.x = 0 # don't forget to define velocity.x's default state
                      	if Input.is_action_pressed("right"):
                      		velocity.x += speed
                      		$AnimatedSprite2D.flip_h = false
                      	elif Input.is_action_pressed("left"):
                      		velocity.x -= speed
                      		$AnimatedSprite2D.flip_h = true
                      		
                      	if is_on_floor():	
                      		if Input.is_action_pressed("pick_up"):
                      			$AnimatedSprite2D.play("pick_up")

                      Thank you again to all the helpful people trying to help me sort this out.