Position Difficulties

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

I am using an Area2D as the player. I want it to shoot an arrow at the player’s position and a certain rotation. This is all going well, but there’s one problem - it’s starting position changes based on the position of the player. When the player is at (0, 0), it gets spawned at the player’s position. When the player moves towards the center of the screen, or really anywhere other than (0, 0), the arrow gets spawned at a seemingly random location. Could someone please help me?

You’ll need to share some of your code in order to see what you might be doing wrong. My guess is that you need to use global_position rather than position (which is relative) when setting the arrow, but seeing the code would make it clear.

kidscancode | 2018-11-23 21:07

Sure thing. Keep in mind, there is a separate scene called “bow” which I instanced as a child in the player scene. I decided not to include its code, since all it does is rotate based on the cursor’s position.

Player code:

extends Area2D

var mouse_pos
var m_pos
var g_mouse_pos
var g_m_pos
var speed = 5
var dash = "false"
var new_anim
var anim
var direction = "right"
enum {IRIGHT, ILEFT, WRIGHT, WLEFT}
var state
var archer_arrow = preload("res://archer_arrow.tscn")

func change_state(new_state):
	#changing state
	state = new_state
	match state:
		IRIGHT:
			new_anim = "idle_right"
		ILEFT:
			new_anim = "idle_left"
		WRIGHT:
			new_anim = "walk_right"
		WLEFT:
			new_anim = "walk_left"

func shoot():
	#shoot
	var a = archer_arrow.instance()
	a.start(global_position, $bow.rotation)
	add_child(a)

func _process(delta):
	#variables
	mouse_pos = get_viewport().get_mouse_position()
	g_mouse_pos = get_global_mouse_position()
	m_pos = get_transform().affine_inverse() * mouse_pos
	g_m_pos = get_transform().affine_inverse() * g_mouse_pos
	if mouse_pos.x < position.x:
		direction = "left"
	if mouse_pos.x > position.x:
		direction = "right"
	#setting aim line
	$bow/Line2D.set_point_position(0, $bow/Position2D.position)
	$bow/Line2D.set_point_position(1, $bow/Position2D2.position)
	#movement
	if Input.is_action_pressed("right") and direction == "right":
		position.x += speed
		change_state(WRIGHT)
	if Input.is_action_pressed("right") and direction == "left":
		position.x += speed
		change_state(WLEFT)
	if Input.is_action_pressed("left") and direction == "right":
		position.x -= speed
		change_state(WRIGHT)
	if Input.is_action_pressed("left") and direction == "left":
		position.x -= speed
		change_state(WLEFT)
	if Input.is_action_pressed("down") and direction == "right":
		position.y += speed
		change_state(WRIGHT)
	if Input.is_action_pressed("down") and direction == "left":
		position.y += speed
		change_state(WLEFT)
	if Input.is_action_pressed("up") and direction == "right":
		position.y -= speed
		change_state(WRIGHT)
	if Input.is_action_pressed("up") and direction == "left":
		position.y -= speed
		change_state(WLEFT)
	if !Input.is_action_pressed("right") and !Input.is_action_pressed("left") and !Input.is_action_pressed("down") and !Input.is_action_pressed("up"):
		if direction == "right":
			change_state(IRIGHT)
		if direction == "left":
			change_state(ILEFT)
	#shoot
	if Input.is_action_just_pressed("Lclick"):
		shoot()
	
	if new_anim != anim:
		anim = new_anim
		$AnimationPlayer.play(anim)

Arrow/Bullet code:

extends KinematicBody2D

var speed = 10000
var velocity = Vector2()

func _ready():
	position = get_parent().position

func start(pos, dir):
	rotation = dir
	position = pos
	velocity = Vector2(speed, 0).rotated(rotation)

func _process(delta):
	move_and_slide(velocity * delta)

ThreeSpark | 2018-11-23 21:14

At first I thought it was because I was using position instead of velocity (I knew this wasn’t true, but it had been an hour, and I was willing to try anything) so I changed it to a kinematic body and used move_and_slide(vel * delta) which also didn’t work, so I changed it back to an Area2D, and changed the code accordingly

ThreeSpark | 2018-11-23 21:23

:bust_in_silhouette: Reply From: kidscancode

Here’s your problem:

func shoot():
    #shoot
    var a = archer_arrow.instance()
    a.start(global_position, $bow.rotation)
    add_child(a)

You’re calling start() and setting the properties correctly, but then when you call add_child() you cause the _ready() in the arrow to be executed, which is resetting the position. Lose the _ready() entirely - it’s unnecessary.

I just tried it, but it didn’t work. The farther the player gets away from (0, 0), the farther the arrow spawns. It’s the same as when there was no func _ready(). Did you see any other problems with the code?

ThreeSpark | 2018-11-23 21:30

Here’s what I mean: Position Problems - Godot Q and A on Vimeo

ThreeSpark | 2018-11-23 21:33

Ah, that problem is because you’re making the arrows children of the player. That means that when the player moves, they will too. Your arrows should not be children of the player.

There’s an example here:
Using CharacterBody2D/3D — Godot Engine (latest) documentation in English

Note this uses get_parent().add_child() on the arrows. This will work, but is not the ideal method. Using signals is more flexible in the long run.

kidscancode | 2018-11-23 21:38