Attempting to retrieve custom tile data on TileMap; keep getting the error "Attempt to call function 'get_custom_data' in base 'null instance' on a null instance."

Godot Version

4.2.1

Question

Hi all,

Fairly new to Godot (and coding) here, and currently stuck with a project I’m working on.

I’m using a 2D TileMap, and I’m working on a “selection box”/indicator that shows the player which tile they’re currently hovering over. I managed to get the selection indicator working fine, however I want to add a feature to it where it switches to another sprite animation if the tile you’re hovering on can’t be accessed by the player.

I’m attempting to do this using a custom data layer that I’ve named “solid” (which I use in layers 1 & 2 of my TileSet). Essentially, what I would like to happen is that if the cursor hovers over a tile with the data layer “solid”, that it would switch sprites, then switch back to the regular sprite when the cursor moves off of the tile.

This part of the code is responsible for translating my selection sprite onto the map, which is working fine on its own:

func _process(delta):
	#Places tile selection sprite on tile where mouse is
	var cursor_position = tile_map.local_to_map(get_global_mouse_position())
	var selected_tile = tile_map.map_to_local(cursor_position)
	tile_select.global_position = selected_tile

However here, in the same function, is where it’s giving me issues:

	var solid_objects = tile_map.get_cell_tile_data(1, selected_tile)
	var solid_walls = tile_map.get_cell_tile_data(2, selected_tile)
	
	if solid_objects.get_custom_data("solid") == true or solid_walls.get_custom_data("solid") == true:
		tile_select.play("cannot_select")
	else:
		tile_select.play("can_select")

For context, “tile_map” here is my TileMap node, and “tile_select” is an AnimatedSprite2D with an animated indicator sprite.

Whenever I run the game, I get the error “Attempt to call function ‘get_custom_data’ in base ‘null instance’ on a null instance.”

If I’m understanding this correctly, both the variables “solid_objects” and “solid_walls” are returning null, but I’m confused as to why they’re doing that.

Any help here would be appreciated!

is this on _process ?

if so, can you print(tilemap) to see if it’s actually referenced well?

Yes, this comes directly after the first code block I posted within _process. I’ve tried doing print(tile_map) and I’ve attached a photo of what it returns.

Worth noting that I’m referencing the data layer “solid” in another part of my code and that seems to be working just fine oddly enough, though the main difference is that it’s done inside a for loop. Pasted below:

func _ready():
	print(tile_map)
	
	set_camera_limits()
	
	#Initialize AStarGrid2D
	astar_grid = AStarGrid2D.new()
	astar_grid.region = tile_map.get_used_rect()
	astar_grid.cell_size = Vector2(16, 16)
	astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
	astar_grid.update()
	
	#Detects and sets solid tiles on layer 1
	for cell in tile_map.get_used_cells(1):
		var tile_data = tile_map.get_cell_tile_data(1, cell)
			
		if tile_data.get_custom_data("solid") == true:
			astar_grid.set_point_solid(cell)
	
	#Detects and sets solid tiles on layer 2
	for cell in tile_map.get_used_cells(2):
		var tile_data = tile_map.get_cell_tile_data(2, cell)
			
		if tile_data.get_custom_data("solid") == true:
			astar_grid.set_point_solid(cell)

Not necessarily.
I could be that only 1 is returning null.
I may be wrong but GDScript does not short circuit the if statement.
That is, both conditions in your if statement must resolve to a valid value.

EDIT:
I looked around and I think that GDScript actually does short circuit the if statements.
So that makes my answer above incorrect.
I would test both those conditions separately anyway to at least see which one is firing the error.

1 Like

Thanks for the reply @sancho2! Your reply got me thinking and I’m glad to say I’ve finally figured it out :smile:

There were two issues with the code:

First: Attempting to get the cell tile data by using the var selected_tile was wrong, as the method get_cell_tile_data requires the Vector2i of the tile within the grid as opposed to the global position of said tile. This was fixed by replacing selected_tile with cursor_position.

Second: It would still return an error when one of the variables returned null, and so I had to split up the if statements separately and check for null before moving on. Not sure if this is the most elegant solution, but this was what I ended up with:

		var solid_objects = tile_map.get_cell_tile_data(1, cursor_position)
		var solid_walls = tile_map.get_cell_tile_data(2, cursor_position)
		
		#If player can't move to the tile, sprite switches to red
		if solid_objects != null and solid_walls == null:
			if solid_objects.get_custom_data("solid") == true:
				tile_select.play("cannot_select")
				
		elif solid_objects == null and solid_walls != null:
			if solid_walls.get_custom_data("solid") == true:
				tile_select.play("cannot_select")
				
		else:
			tile_select.play("can_select")

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.