Uniquely Identifying Grandchildren / Great-Grandchildren with the Same Name

Godot Version

Godot Engine v4.2.2.stable.official [15073afe3]


I am currently in the process of implementing a system for a StaticBody3D called “Voxel” that allows the MeshInstance3D attached to it to change color whenever it is being looked at by the player.

func _physics_process(delta: float):
	# other stuff...
	if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
		# ray cast stuff cut for brevity
		var _result = _spaceState.intersect_ray(_query)
		if _result != null && _result.size() > 0:
			for key in _result.keys():
				print("{k}: {r}".format({"k": key, "r": _result[key]}))
			emit_signal("isLookingAt", _result["collider"])

I then have a Node3D that acts as a “container” for all of my StaticBody3Ds. I have attached a signal handler to this container for the “isLookingAt” signal.

func _on_is_looking_at(collider: Node):
	print("joe is looking at {node}".format({"node": collider.name}))
	for child in collider.get_children():
		print("child: {child}".format({"child": child}))
		if child.name == "VoxelMesh":
			child.get_active_material(0).albedo_color = Color(0, 0, 0)

The following information gets printed to the console whenever I look at one Voxel:

position: (-0.550182, 0, -4.911057)
normal: (0, 1, 0)
face_index: -1
collider_id: 31004296529
collider: Voxel1:<StaticBody3D#31004296529>
shape: 0
rid: RID(4681514352646)

joe is looking at Voxel1
child: VoxelCollision:<CollisionShape3D#31021073746>
child: VoxelMesh:<MeshInstance3D#31037850963>

However, whenever I look at the results in-game, all of the voxels will get colored, despite the emitter triggering for one object’s collision.

I suspect that this has to do with the fact that the children of the Voxel (aptly named “VoxelMesh” and “VoxelCollision”) are all named the same regardless of which Voxel it is. I am simply checking the name of the child node to see if it is the MeshInstance3D that I want to change.

My issue is wanting to reference a particular child node’s children without changing the properties of other similarly-named children in the group. I’m unsure as to why this is happening, because as far as I’m aware accessing the child Node only should be changing only that Node’s MeshInstance3D.

If it’s possible to get the child node by a particular unique ID, that might work, but I’m not sure how to accomplish this. Any help is greatly appreciated.


Just to be sure, when you’re doing the meshs you just duplicated them or you use the same shader material for all of them? Because if you didn’t make the material be unique for each of the meshes, when you modify the material in one mesh, the others will apply the same changes

1 Like

My idea with these is that they are all child scene instantiations of a scene called “Voxel”. And I’ve placed them in my main “World” scene.

The ones I’ve shown in the screenshot are static, as I’ve manually added them in using “Instantiate Child Scene” in the editor. They are named differently (“Voxel1”, “Voxel2”, etc). One thing that I’m wondering is that if it matters if I copied and pasted the Voxels. I created one instantiation of the child scene, and then copied / pasted it into the container.

The ones in the final game aren’t going to be static and instead generated programmatically. However, that does not matter with the way my emitter is set up, as it checks the instance of the object and doesn’t require a name to get the node.

Regardless of this, the child scene instantiations need to stay this way. I do not have a saved material resource for these Voxels, and have simply opted for setting the material albedo color of the Voxel.

With your post, I’m a bit confused on how this will be implemented. Are you saying that I should have a unique shader material instance for all of these Voxels?

Because these are going to be programmatically generated child scenes, I do not believe that I have a neat and concise way to do what you described. If you could provide some more details on that, I’d appreciate it.

Thank you for your timely response.

Before we continue, let’s do a test for confirm if the problem is what i’m saying, apply this modification in your code.

if child.name == "VoxelMesh":
	# This line is to create an unique material for this node
	var new_material = child.get_active_material(0).duplicate()
	child.set_active_material(0, new_material)
	# if the code not work, try comment the line above and uncomment the line bellow
	#child.material_override = new_material
	child.get_active_material(0).albedo_color = Color(0, 0, 0)

If this solve your problem, will be what i’m saying

1 Like

I attempted both of your solutions and applying the material override worked.

The first one did not work because set_active_material is not a method of MeshInstance3D. I believe you were talking about set_surface_material. I tried this, but that also doesn’t seem to be working either.

Your solution that employs material override works fine, but I’ve been told that using the material override isn’t good practice in terms of setting the material you want it to be. It’s almost always better to set the actual material of the MeshInstance instead.

The error in particular is “Nonexistent function ‘set_surface_material’ in base ‘MeshInstance3D’”. I’ve tried it with ‘set_active_material’ also, and that doesn’t work. Including on the collider object itself as opposed to the mesh.

In practice, I just want this to act as a highlight to the block and not a complete change of the material. They are going to have the texture applied, and then the highlight could act as a “Next Pass” with the albedo color as the highlight over the texture.

Not sure if that makes this any easier to understand. I’d greatly appreciate your feedback, and thank you for getting back to me so quickly.

Oh my and i wrote the correct way in code but when i write here i did that, bleh. Dumb mistakes aside, with that test we confirm the problem is the material don’t being unique between the meshes

Yes, i’m don’t have much knowledge in the 3d part because i work more with the 2d part but i find a good way for solve your problem:

# You'll need a function to interact with every mesh you have
# i don't know how is the nodes name you use but i think you'll understand
# the logic anyways, you need to make every mesh be unique
func make_meshes_unique():
	for collider in colliders_poll.get_children():
		var mesh_instance = collider.get_node("VoxelMesh")
		# Here is the magic touch, with that every mesh will be
		# unique and have an unique material
		mesh_instance.mesh = mesh_instance.mesh.duplicate(true)

You also can make the meshes unique in the moment you instance them

var your_mesh_load = preload("your_mesh_scene_path")
var mesh_instance = your_mesh_load.instantiate()

mesh_instance.mesh = mesh_instance.mesh.duplicate(true)

Maybe you need to adapt the code for match your code but the logic is the same, duplicate the mesh to make an unique copy for every node, don’t forget to use duplicate(true) because the true argument will made a unique mesh resource with everything inside him unique too (inclusive the material)

1 Like

Hello again. I know it has been a while, but I have just been busy over the past few days and haven’t worked on the game more until yesterday.

I’ve implemented changes and I have to say, that it’s a success. You have helped me greatly.

I ended up making some slight tweaks to your implementation in order to fit it to my needs, but the general principle still holds:

if collider.get_children().any(
		func(n: Node): return n.name == "VoxelMesh"
		var myMesh: MeshInstance3D = \
		collider.get_node("VoxelMesh") as MeshInstance3D
		myMesh.mesh = myMesh.mesh.duplicate(true)
		var color: Color = myMesh.get_active_material(0).albedo_color
		_lastLookedAtOriginalColor = color
		color.r += 0.2
		color.g += 0.2
		color.b += 0.2
		myMesh.get_active_material(0).albedo_color = color

Here, I’m basically checking if the VoxelMesh exists as a child of the object I am currently looking at through my ray cast signal, and brightening up the albedo by 20% to give it a highlight effect.

Duplicating the mesh instance was the secret I was missing all this time. Thanks again. You’ve been invaluable to the progress on implementing more systems in the game.

I wish I could show it to you in action, but unfortunately, I am unable to upload attachments.

All the best.

1 Like

I also find an interesting info, if you’re instancing your MeshInstance3D from a scene, you can just mark the mesh and the material to be local to scene, that will automaticly make the resources unique when you instance the MeshInstance3D