Getting Material of object in a 3d scene via raycast

Godot Version

4.3

Question

Hi, I’m trying to revamp the sound system in my game, and part of that is switching the sounds played you step to be based on the surface you are on rather than the previous system (a super scuffed gamejam implementation that determined the sound based-on the level id). I know I’d need some sort of dictionary or long match statement for each material in the game.

So I attempted to do this in the code snippet here

func find_step_sound():
	var floor = material_checker.get_collider()
	var floor_material_node = floor.get_node_or_null("MeshInstance3D")
	var floor_material = null
	if floor_material_node != null:
		floor_material = floor_material_node.get_mesh().get_material()
	print(floor_material)
	match floor_material:
		"res://Assets/3d/materials/world_map.tres":
			step_sound = snow_footsteps.pick_random()
		"res://Assets/3d/materials/stairs.tres":
			step_sound = stair_footsteps.pick_random()

The issue is, no matter what surface I’m on floor_material returns null. I’ve tried reading the documentation for the RayCast3D node and the MeshInstance3D, and I’m unsure what I’m doing wrong. I’ve found some Godot 3-era Reddit posts discussing a similar system, but trying to transfer it over to Godot 4-era naming conventions hasn’t helped either.

Any help would be much appreciated!

have you checked if “floor_material_node” is null?

Several things that could be going wrong here:

  • floor could be null, if your raycast doesn’t find anything (I assume material_checker is your raycast?). If this happens when it shouldn’t, perhaps there’s a problem with your collision layers.
  • floor_material_node could be null, if floor does not have a child named precisely MeshInstance3D.
  • Assuming floor_material_node is not null, then the problem could be with how you get the material. Is the material directly on the mesh itself, or is it an override material on the meshinstance? Also, get_material(), afaik, will only work on Godot’s primitive meshes - if you ever use it with a custom mesh you’ve imported, you’ll need surface_get_material(idx) instead, with idx being the index of the surface, since models can have more than one material on them.
    Now, MeshInstance3D has a method called get_active_material you might try - according to the docs, it will give you whatever material will actually be drawn. It needs an index as well, but if you’re pretty sure your floors will always have just one material, you can just use floor_material_node.get_active_material(0).
2 Likes