Invalid get index 'global_position' (on base: 'null instance')

Godot Version

V4.2.1

Question

I exported the checkpoints array in player.gd and set it up in main.tscn. Whenever the player passes through one of the checkpoints, the save() function is called within checkpoint(). When the player dies, load_data() is triggered. However, when the player dies, the debugger points to $player.global_position = file.get_var($player.global_position) in load_data(). Additionally, I’m getting the following error: save(): Node not found: "player" (relative to "/root/Game").

player.gd:

extends CharacterBody2D

@export_category("Exports")
@export_group("Checkpoints")
@export var checkpoints = []  #list of vector2

func die():
	hit = true
	velocity.y = -700
	collision_mask = 32
	collision_layer = 32

main.gd:

extends Node2D

var save_path = "user://newGame.save"

func _physics_process(_delta):
	Saw()
	SpikedBall()
	RockHead()
	for i in $Player.get_slide_collision_count():
		var collision = $Player.get_slide_collision(i)
		collision = collision.get_collider()
		#print("Collided with: %s" % collision.name)
		
		if collision.name == "Trampoline":
			Trampoline()
		elif collision.name.begins_with("Spikes"):
			Spikes()
			$Player/MetalHitSound.play()
		elif collision.name.begins_with("Saw"):
			$Player.die()
			load_data()
			$Player/MetalHitSound.play()
		elif collision.name.begins_with("Fire"):
			var Fire = collision
			Fire.FireHit = true
			if Fire.FireHit == true and Fire.FireOn == true:
				$Player.die()
				load_data()
		elif collision.name.begins_with("Box"):
			var Box = collision
			Box.BoxHit = true
			$Player.velocity.y = -700
		elif collision.name.begins_with("SpikedBall"):
			$Player.die()
			load_data()
			$Player/MetalHitSound.play()
	checkpoint() #save at checkpoints

func checkpoint():
	for check in $Player.checkpoints:
		if $Player.global_position.x >= check.x:
			save()

func save():
	if $player:
		var file = FileAccess.open(save_path, FileAccess.WRITE)
		file.store_var($player.global_position)
		file.store_var($player.collision_mask)
		file.store_var($player.collision_layer)

func load_data():
	if $Player:
		if FileAccess.file_exists(save_path):
			var file = FileAccess.open(save_path, FileAccess.READ)
			$player.global_position = file.get_var($player.global_position) #Debugger pointing at this
			$player.collision_mask = file.get_var($player.collision_mask)
			$player.collision_layer = file.get_var($player.collision_layer)
		else:
			print("no data saved")

Is there a reason to use $Player (uppercase P) and $player (lowercase p) in the same script? This is probably the source of your issues.

1 Like

That’s where I’d put my money on.

Like @FencerDevLog said, this is your error. You’re looking for $player, and only $Player exists. Here’s the fix for this both now and in the future.

  1. Click and drag your Player node into your script editor right above var save path...
  2. Press Ctrl on your keyboard when you’re hovering over that line your editor.
  3. Let go of the mouse button while holding Ctrl.
  4. The line @onready var player: CharacterBody3D = $Player will appear.
  5. Replace every reference to $player and $Player in your code with player.

Do the same with $Player/MetalHitSound. Then, if you decide to put $Player as a child of $Level or something, you only have to update one reference.

Thanks, I fixed that, but now the debugger is again pointing at player.global_position = file.get_var(player.global_position) and throwing this error: Invalid type in function 'get_var' in base 'FileAccess'. Cannot convert argument 1 from Vector2 to bool. I followed this from the tutorial where they passed an integer variable into get_var().

TBH, that error doesn’t make sense. I stuck it in my editor and got the same error. global_position is a Vector3.

I’ve got some code that should work, but it uses a slightly different method. the upside is that if you add or remove values, you won’t corrupt past save files. The method they’re using will. This method saves everything to a single dictionary. When you pull it out, you are guaranteed to get the information, since everything is stored in the dictionary.

func save():
	if player:
		var save_information = {
			"global_position" : player.global_position,
			"collision_mask" : player.collision_mask,
			"collision_layer" : player.collision_layer
		}
		var file = FileAccess.open(save_path, FileAccess.WRITE)
		file.store_var(save_information)

func load_data():
	if player:
		if FileAccess.file_exists(save_path):
			var save_information = FileAccess.open(save_path, FileAccess.READ)
			player.global_position = save_information["global_position"]
			player.collision_mask = save_information["collision_mask"]
			player.collision_layer = save_information["collision_layer"]
		else:
			print("no data saved")
1 Like

Now I’m getting the error: Invalid get index 'global_position' (on base: 'FileAccess'). Maybe I’m making another mistake in the code; I’ll look into it.