What is the best approach to 3D UI and Visual Indicators

Godot Version

Godot 4.5.stable

Question

I’d like some help with how to implement 3D visual effects and indicators in my game.

I’m currently generating meshes using SurfaceTool and adding various MeshInstance3Ds to my scene. Screencaptures of my current visuals are attached.

The problems with this are that it feels extremely clunky to get anything working this way, I’m adding a good few dozen extra Nodes to my scene that are often being removed and readded. The whole affair isn’t very performant, either.

Are there better tools I should be using? Are similar effects to this accomplishable using shaders or particle systems, in ways that might be easier to work with? Or at least more performant?

I’m very new to making 3D games, and my old techniques from working in 2D do not seem to translate directly, so I’m very likely going about this all wrong. I can post examples of code or specific things I’m doing if need be, but I’m mostly looking for advice as to what techniques or tools I should be using for this kind of thing.

Examples

sounds like you need to structure your code better?

you are not specifying what you need help with. code would be helpful, or a picture of your scene tree.

a few hints:
1 - avoid using a million different meshes. any static meshes should be combined in your 3D software (like blender). this includes characters and terrain, unless they need to be interacted with.
2 - avoid using a million materials. a character should be ideally ONE material. every mesh should be one material.
sometimes we can’t avoid it and a mesh needs 2 or 3 materials, but that should be the limit.
3 - for destructible characters, use one for the whole mech, and have variations with each missing arm, missing head/torso, etc. then swap them in game when they get destroyed.
or you can make each part a different mesh, but make them share the same material.
4 - for changing color, make a shader.
you can use a texture as mask, then use an instance uniform to change color.
here’s an old shader of mine from a similar game:

shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_toon,specular_toon;
uniform sampler2D texture_albedo : source_color,filter_linear_mipmap_anisotropic,repeat_disable;
uniform sampler2D texture_mask : hint_roughness_g, filter_linear_mipmap, repeat_disable;
instance uniform vec3 custom_color : source_color = vec3(0.1, 0.1, 0.0);

void fragment() {
	vec2 base_uv = UV;
	vec4 albedo_tex = texture(texture_albedo,base_uv);
	ALBEDO = mix(albedo_tex.rgb, custom_color, texture(texture_mask, base_uv).r);
}

this uses the red color of the mask and replaces it with custom_color.
instance uniform means you don’t edit the material to change color, instead, a new tab will appear in MeshInstace3D allowing you to change the color for each Node.
and since all meshes share the same material, the scene will have better performance than if there where 2 or 3 or more materials for each color.

5 - the effect below the unit can be achieved with a decal. when moving the decal, use math to lock it into position.
6 - the movement tiles look cool, but will use a lot of resources because of transparency. an alternative is to use decals. you won’t get the animation but they will stick to uneven surfaces.
the same for the red barrier, you can use decals to form an outer layer, be it with lines or hexes.
all of these should ideally be children of a Node3D, so you can delete them easily.
7 - another alternative for the movement tiles would be to use a MultimeshInstance3D. with this, you can duplicate the same mesh multiple times and change the Transform3D of each one inside a for loop, and do it every frame.
I would use a tween that is generated and run when the animation needs to happen, rather than in process. then use a tween.tween_method for moving the meshes of MultimeshInstance3D.
8 -

adding a good few dozen extra Nodes to my scene that are often being removed and readded. The whole affair isn’t very performant, either.

adding and removing nodes is normal, godot is optimized for it too. In fact removing unneeded nodes is good for performance.
but if the same number of nodes is going to be on screen at any given time, you can just hide them. if you make them children of a Node3D, you can hide the Node3D.

9 - the squares revolving around the selected unit can be a GPUParticles3D, or a single mesh that just rotates. That effect doesn’t really need much movement since it can create an optical illusion.
also, keep one it in global space and move it to the global_position of the unit when it is selected, rather than having it as child of each unit.


your game is looking really good!