Can't Add 'gun.tscn' As A Child

Godot Version

4.3

Question

I get this error when I try to add ‘gun.tscn’ as a child to ‘player.tscn’: Invalid access to property or key 'position' on a base object of type 'Nil'
player_movement.gd

extends CharacterBody2D

var _rotation_speed : float = TAU * 2
var _theta : float
var _direction : Vector2
@export var gun_scene : PackedScene
var gun : CharacterBody2D

func _ready():
	var gun = gun_scene.instantiate()
	if gun:
		add_child(gun) 
		gun.position = Vector2(50, 0) 
	else:
		print("Error: Gun instantiation failed!")

func move(direction : Vector2):
	_direction = direction

func _physics_process(delta : float):
	var input_vector = Vector2.ZERO
	input_vector.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
	input_vector.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
	input_vector = input_vector.normalized()

	if _direction:
		_theta = wrapf(atan2(_direction.y, _direction.x) - rotation, -PI, PI)
		rotation += clamp(_rotation_speed * delta, 0, abs(_theta)) * sign(_theta)

	if input_vector:
		velocity = input_vector * 300
		rotation = lerp_angle(rotation, atan2(-velocity.x, -velocity.y), delta * 75.0)
	else:
		velocity = input_vector

	move_and_slide()

enemy.gd

extends CharacterBody2D

@export var player_scene : PackedScene
var player : CharacterBody2D

func _ready():
	player = player_scene.instantiate()
	get_tree().current_scene.add_child(player)
	player.position = Vector2(100, 100)

func changeRotation():
	if player:
		print("Player position: ", player.position)
		look_at(player.position)
	else:
		print("Error: Player node not found.")

func _on_timer_timeout():
	changeRotation()

gun.gd

extends CharacterBody2D


@export var player : Node2D
@export var distance_from_player : float = 100
@export var bullet_scene : PackedScene
@export var delay: float

var can_shoot : bool = true

func _ready():
	position = player.position + Vector2(distance_from_player, 0)

func _process(_delta):
	var direction = (get_global_mouse_position() - player.position).normalized()
	position = player.position + direction * distance_from_player
	look_at(get_global_mouse_position())

	if Input.is_action_pressed("mouse_left") and can_shoot:
		shoot_bullet(direction)

func shoot_bullet(direction: Vector2):
	if bullet_scene:
		var bullet = bullet_scene.instantiate()
		bullet.position = position
		bullet.direction = direction 
		get_parent().add_child(bullet)
		can_shoot = false
		await get_tree().create_timer(delay).timeout
		can_shoot = true
	else:
		print("Error: Bullet scene is not assigned!")

Seems like your gun.gd is the only script that doesn’t check for null before accessing position, are you sure you assigned the player node in the inspector?

Make sure to point out which line you are getting the error

gun.gd

extends CharacterBody2D


@export var player : Node2D
@export var distance_from_player : float = 100
@export var bullet_scene : PackedScene
@export var delay: float

var can_shoot : bool = true

func _ready():
	position = player.position + Vector2(distance_from_player, 0) # Error is here

func _process(_delta):
	var direction = (get_global_mouse_position() - player.position).normalized()
	position = player.position + direction * distance_from_player
	look_at(get_global_mouse_position())

	if Input.is_action_pressed("mouse_left") and can_shoot:
		shoot_bullet(direction)

func shoot_bullet(direction: Vector2):
	if bullet_scene:
		var bullet = bullet_scene.instantiate()
		bullet.position = position
		bullet.direction = direction 
		get_parent().add_child(bullet)
		can_shoot = false
		await get_tree().create_timer(delay).timeout
		can_shoot = true
	else:
		print("Error: Bullet scene is not assigned!")

the player export must not be assigned.

How can I do that? I still need a way to define player for the rest of the script.

When you @export a variable it shows up in the Inspector, you must assign it for the variable to have a value.

What do I assign it with? I thought it already has a value.

You would have to assign it to the node you want,I would assume the player node. I can’t help without knowing the scene tree this has been added to. It doesn’t have a value by default, if you see “Assign…” in the Inspector then it is null.

Thanks that kind of worked. Now I have this bug where the gun goes to my mouse and not to my player instance in enemy.gd.
enemy.gd

extends CharacterBody2D

@export var player_scene : PackedScene
var player : CharacterBody2D

func _ready():
	player = player_scene.instantiate()
	get_tree().current_scene.add_child(player)
	player.position = Vector2(100, 100)

func changeRotation():
	if player:
		look_at(player.position)
	else:
		print("Error: Player node not found.")

func _on_timer_timeout():
	changeRotation()

gun.gd

extends CharacterBody2D


@export var player : Node2D
@export var distance_from_player : float = 100
@export var bullet_scene : PackedScene
@export var delay: float

var can_shoot : bool = true

func _ready():
	position = player.position + Vector2(distance_from_player, 0)

func _process(_delta):
	var direction = (get_global_mouse_position() - player.position).normalized()
	position = player.position + direction * distance_from_player
	look_at(get_global_mouse_position())

	if Input.is_action_pressed("mouse_left") and can_shoot:
		shoot_bullet(direction)

func shoot_bullet(direction: Vector2):
	if bullet_scene:
		var bullet = bullet_scene.instantiate()
		bullet.position = position
		bullet.direction = direction 
		get_parent().add_child(bullet)
		can_shoot = false
		await get_tree().create_timer(delay).timeout
		can_shoot = true
	else:
		print("Error: Bullet scene is not assigned!")

You might have to explain this problem more, can you post a screenshot and explain what you expect it to look like?

Here is a screenshot


I want the gun (the rectangle) to stay around the player at a set distance and just point towards the mouse.

maybe your gun or player scene are offset from position 0,0. If you gun is a child of the player then it shouldn’t be using the player’s position in this calculation. If you show the Player’s scene that could help clarify.

player.tscn

Right I forgot that the gun is added from part of the script. So you will have to change that equation to not add the player’s position again to the gun

position = direction * distance_from_player

That’s getting somewhere, but now it is always around the bottom right corner of the player. How can I center it on the player’s position?

Sounds like the player’s sprite and/or collision shape are not centered, if you show the 2D view of your player you may see the sprite offset from 0,0. Your Player root node has a transform change, if it’s position you should reset it.

What do you mean exactly? How would I reset it? The offsets on both sprites are (0, 0).

Very strange, I could only guess the Sprite2D isn’t set to centered then? Or the gun scene is offset? Main point is where ever the gizmo for Player is, that’s where the gun’s gizmo will be offset, make sure both are centered at 0,0