Adding collision to a randomly generated MeshInstance3D

Godot Version

4.4.stable

Question

I’m relatively new to coding, and I’m having trouble figuring out how to add collision to this mesh. I got code for basic random terrain generation from a tutorial, and I’ve been trying to research on how I could add collision, but I can’t find anything. I feel like the solution is absurdly simple, but I don’t want to waste any more time attempting to blindly solve this by myself. I’ll copy and paste the current code I have in the mesh. (I just got back into game development so I have no idea if there’s a better way of sending code)

@tool
extends MeshInstance3D

const size := 256.0

func _ready() → void:
if not Engine.is_editor_hint():
var new_noise = noise.duplicate() if noise else FastNoiseLite.new()
new_noise.seed = randi()
self.noise = new_noise

@export_range(4, 256, 4) var resolution := 32:
set(new_resolution):
resolution = new_resolution
update_mesh()

@export var noise: FastNoiseLite:
set(new_noise):
noise = new_noise
update_mesh()
if noise:
noise.changed.connect(update_mesh)

@export_range(4.0, 128.0, 4.0) var height := 64.0:
set(new_height):
height = new_height
material_override.set_shader_parameter(“height”, height * 2.0)
update_mesh()

func get_height(x: float, y: float) → float:
return noise.get_noise_2d(x, y) * height

func get_normal(x: float, y: float) → Vector3:
var epsilon := size / resolution
var normal := Vector3(
(get_height(x + epsilon, y) - get_height(x - epsilon, y)) / (2.0 * epsilon),
1.0,
(get_height(x, y + epsilon) - get_height(x, y - epsilon)) / (2.0 * epsilon),
)
return normal.normalized()

func update_mesh() → void:
var plane := PlaneMesh.new()
plane.subdivide_depth = resolution
plane.subdivide_width = resolution
plane.size = Vector2(size, size)

var plane_arrays := plane.get_mesh_arrays()
var vertex_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_VERTEX]
var normal_array: PackedVector3Array = plane_arrays[ArrayMesh.ARRAY_NORMAL]
var tangent_array: PackedFloat32Array = plane_arrays[ArrayMesh.ARRAY_TANGENT]

for i:int in vertex_array.size():
	var vertex := vertex_array[i]
	var normal := Vector3.UP
	var tangent := Vector3.RIGHT
	if noise:
		vertex.y = get_height(vertex.x, vertex.z)
		normal = get_normal(vertex.x, vertex.z)
		tangent = normal.cross(Vector3.UP)
	vertex_array[i] = vertex
	normal_array[i] = normal
	tangent_array[4 * i] = tangent.x
	tangent_array[4 * i + 1] = tangent.y
	tangent_array[4 * i + 2] = tangent.z

var array_mesh := ArrayMesh.new()
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, plane_arrays)
mesh = array_mesh

Put your code between 3 ticks: `

The solution to your problem is to create a new instance of a CollisionShape3D and add it as a child of your MeshInstance3D. Then create a new Shape3D and assign it to the CollisionShape3D’s shape property.

I recommend you actually do all this manually and learn how they work before trying to do it in code. It’ll take you 15 minutes to learn and make the process a whole lot easier. You can find lots of tutorials online.

I added a collisionshape3d, but the shape area on it in the inspector doesn’t have an option for shape3d, theres only things like sphereshape3d and boxshape3d and other specific shapes like that. And the collision shape has a warning on it that says it “only serves to provide a collision shape to a CollisionObject3D derived node.” Also, how would I add collision without code if it generates a new seed whenever the scene starts? I know it would be easy if it was just one permanent shape I could add collision too, but it randomly generates every time. I also searched online for tutorials on how to add collision to a randomly generated mesh, but I only could find what to do for meshes with a permanent shape.

I forgot something. You need a StaticBody3D with the MeshInstance3D, and that should have the CollisionShape3D as a child. Shape3D is an abstract class. You have to pick an implementation. HeightMapShape3D is probably what you want.

Ok, so I did that, but I don’t see a visual collision thing that I can drag around. Am I supposed to add code to make the collision work? Which node am I supposed to add code to if I do have to add code? This is my first time ever touching the 3D part of Godot, so I’m really confused on how this all works

Based on what you said about being new to coding, you’ve jumped pretty quickly into the deep end and skipped some basic steps that are needed to understand the answers for which you’re asking. I think this tutorial will answer your questions a lot faster than a long back-and-forth on the forum. Collisions are based on layers and masks.

After that, you might get your answers by watching these tutorials, but again, having a basic understanding of how collision-detection works in Godot is going to help you understand these videos.

I already know how collision works and how layers and masks work, and the “How to Procedurally Generate Terrain - Using Godot Engine” video was the one I used to make the terrain generation, but I haven’t seen the tutorial from the chevifier guy you linked, so I watched the entire terrain generation series with all 5 videos for multiple hours (it took two days) and I found out that you literally just have to put create_trismesh_collisions() into the code. There is nothing complex at all, it’s really just one line of code and I wasted a lot of time, but at least I have a much better terrain generation system now

1 Like