'Node2D.visible' not working

Godot Version

Godot 3.7.2.stable

Question

Hello, thanks for reading, here’s my problem:

I have this Node2D that’s being linked to the script called dialoguebox

@onready var dialoguebox: Node2D = $"../Camera2D/dialoguebox"

Then I’d like to know if it is visible currently so that i can disallow the player’s movement and set the confirm action to remove the dialogue:

	if dialoguebox.visible:
		await get_tree().create_timer(0.1).timeout
		if Input.is_action_just_pressed("confirm"):
			dialoguebox.visible = false
			can_interact = true

However when I run the code it gives this error:

E 0:00:00:716   @implicit_new: Invalid access to property or key 'visible' on a base object of type 'Nil'.
  <GDScript Source>player.gd:57 @ @implicit_new()
  <Stack Trace> player.gd:57 @ @implicit_new()

Apparently this ‘Nil’ object mentioned is the variable created after the Node2D called ‘dialoguebox’

It’s such a random error because it worked just fine before, and i didn’t do anything with the variable after coding it

It started appearing after i tried to fix the same issue with a CharacterBody2D node’s .position so that the Camera2D would follow it. I proceeded to only remove that part of the code by placing a ‘#’ in front

Now that i think about it, the dialoguebox node is a child node of the Chamera2D, that may be the reason for the issue, however the code involving the Camera2D didn’t exist before the code with the dialoguebox worked, so removing it should’ve removed all issues.

I’m really stumped on this, if you could help it would mean a ton to me, thanks! : )

Generally parent-paths i.e. starting with "../" are bad, using @export may help you out and make this dialogue manager thing more re-usable too without requiring it to be a sibling of Camera2D and specifically named Camera2D.

Try changing your dialoguebox to an export variable, and make sure to assign it in the editor

@export var dialoguebox: Node2D

thank you so much for the quick response, after testing it seems to have worked, have a great day!

Alright, so… correction:

After changing all the node representing variables to the @export kind, it seems that every time I change the code AT ALL all the ‘dialoguebox.position’ and equivalents break instantly.

I’ve manage to work around this by, every time I run the code, just removing and re-adding the variables, but as you can imagine that was a huge pain in the butt.

However, this has stopped working out of nowhere even though i have not used the variables again,

This time it even flagged simple multiplication between two integers as “invalid”

This is causing me great pain, if anyone has a single clue of what is happening, please help me. Get this hellish weight off of my shoulders please i beg you.

(all the content in the first post is still valid, except the variables are of the @export kind now)

What error messages are you receiving?

this one

Are you running this code before the node is ready?

What is calling this code?

this is called by a _process() func in line 95

the @export variable is declared at line 83, it is not changed or called anywhere else.

the @implicit_new sounds odd, did you add your player script to the list of autoloads?

sorry, what does this mean exactly?

In the project settings, did you add this player script to the “autoload” tab?

no, I did not. should I?

it didn’t solve it

No you should not mark the player script as a global. Exports generally should not un-assign unless you change the variable names. Maybe it would help for your post your entire script?

okay, just keep in mind this is my first script.

This is the whole player script

extends CharacterBody2D

#walking vars
@export var speed = 80

func walk():
	velocity.x = Input.get_axis("left", "right") * speed
	velocity.y = Input.get_axis("up", "down") * speed

#turning vars
@onready var interactranger: Area2D = $interactranger
@onready var sprite_2d: Sprite2D = $Sprite2D
@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D

@onready var right_idol_texture = preload("res://assets/krisplacehold/krisright0.png")
@onready var left_idol_texture  = preload("res://assets/krisplacehold/krisleft0.png")
@onready var up_idol_texture    = preload("res://assets/krisplacehold/krisup0.png")
@onready var down_idol_texture  = preload("res://assets/krisplacehold/krisdown0.png")

@onready var right_mov_texture = "res://assets/krisplacehold/krisright1.png"
@onready var left_mov_texture  = "res://assets/krisplacehold/krisleft1.png"
@onready var up_mov1_texture   = "res://assets/krisplacehold/krisup1.png"
@onready var up_mov2_texture   = "res://assets/krisplacehold/krisup2.png"
@onready var down_mov1_texture = "res://assets/krisplacehold/krisdown1.png"
@onready var down_mov2_texture = "res://assets/krisplacehold/krisdown2.png"

var last_rot = "right"
var walking:bool = false
var is_sprite_changed_from_mov:bool = false
var mov_last_sprite = ""
const half_rotation = PI / 2

func turn_shortcut_mov(direction):
	sprite_2d.visible = false
	animated_sprite_2d.visible = true
	animated_sprite_2d.play(direction)
	last_rot = direction

func turn():
	var input_vec = Input.get_vector("left", "right", "up", "down")
	if velocity == Vector2(0, 0):
		#idol
		animated_sprite_2d.visible = false
		animated_sprite_2d.stop()
		sprite_2d.visible = true
		walking = false
		is_sprite_changed_from_mov = false
		mov_last_sprite = ""
		
		if last_rot == "right":
			sprite_2d.texture = right_idol_texture
		elif last_rot == "left":
			sprite_2d.texture = left_idol_texture
		elif last_rot == "up":
			sprite_2d.texture = up_idol_texture
		elif last_rot == "down":
			sprite_2d.texture = down_idol_texture
	
	#this part is order dependant on the priority, change
	elif  input_vec == Vector2(1, 0):
		#right
		turn_shortcut_mov("right")
		interactranger.rotate(- interactranger.rotation)
	
	elif  input_vec == Vector2(-1, 0):
		#left
		turn_shortcut_mov("left")
		interactranger.rotate(half_rotation * 2 - interactranger.rotation)
	
	elif input_vec == Vector2(0, -1):
		#up
		turn_shortcut_mov("up")
		interactranger.rotate(half_rotation * 3 - interactranger.rotation)
	
	elif input_vec == Vector2(0, 1):
		#down
		turn_shortcut_mov("down")
		interactranger.rotate(half_rotation - interactranger.rotation)

#interaction vars
var current_interactions := []
var can_interact:= true
@onready var interactrange: Area2D = $interactranger
@export var dialoguebox: Node2D

var finish_interacting = false

func _input(_event: InputEvent) -> void:
	if Input.is_action_just_pressed("confirm") and can_interact:
		if current_interactions:
			can_interact = false
			await current_interactions[0].interact.call()

#interactions process
func _process(_delta: float) -> void:
	if current_interactions and can_interact:
		current_interactions.sort_custom(_sort_nearest)
	
	if dialoguebox.visible:
		await get_tree().create_timer(0.1).timeout
		if Input.is_action_just_pressed("confirm"):
			dialoguebox.visible = false
			can_interact = true

func _sort_nearest(area1, area2):
	var area1_dist = interactrange.global_position.distance_to(area1.global_position)
	var area2_dist = interactrange.global_position.distance_to(area2.global_position)
	return area1_dist < area2_dist

func _on_interactranger_area_entered(area: Area2D) -> void:
	current_interactions.push_back(area)
func _on_interactranger_area_exited(area: Area2D) -> void:
	current_interactions.erase(area)

@export var character_body_2d: CharacterBody2D
@export var camera_2d: Camera2D

func camera_movement():
	camera_2d.position = character_body_2d.position
	pass

#this is the main process
func _physics_process(_delta):
	if can_interact:
		walk()
		move_and_slide()
		camera_movement()
		if Input.is_action_just_pressed("cancel") or Input.is_action_just_pressed("confirm") or Input.is_action_just_pressed("right") or Input.is_action_just_pressed("left") or Input.is_action_just_pressed("up") or Input.is_action_just_pressed("down") or Input.is_action_just_released("right") or Input.is_action_just_released("left") or Input.is_action_just_released("up") or Input.is_action_just_released("down"):
			turn()
			pass
	

Get rid of the awaits.

well, that’s a band-aid solution to a problem i’ve faced a long time ago about the dialogue box, either not appearing or not closing because it uses the same button.

but i’ll try that

… and nope, didn’t work.

just noticed that i didn’t address the first await because i forgot. But it’s the only thing that’s linking this script and the script of an ‘Area2D’ inside of the ‘Area2D’ of the player named ‘interactranger’, if there’s another way that solves this issue feel free to tell me.

What “didn’t work”? Doing await in _process() is almost guaranteed to cause bugs. By getting rid of it I didn’t mean just to delete it. Re-create the intended functionality using regular signal handlers instead.

okay, sorry for the misunderstanding, can you tell me an equivalent of the await create timer?

The awaits would happen after the error anyways, chances are just removing them works just as well especially if you aren’t certain they are useful as is.

Would there be any reason the dialoguebox deletes itself? does it have a script, or your camera? Do you change scenes?