In my just released game âProtolife: Other Sideâ I have the destructible landscape. Creatures that we control can make new ways through the walls. Also, some enemies are able to modify the landscape as well.
That was made in Godot 3.5, but I was interested in how to do the same in Godot 4 (spoiler: no big differences).
The solution is pretty simple. I use subdivided plane mesh and HeightMapShape3D as a collider. In runtime, I modify both of them.
How to modify mesh in runtime
There are multiple tools that could be used in Godot to generate or modify meshes (they are described in docs:Â https://docs.godotengine.org/en/stable/tutorials/3d/procedural_geometry/index.html). I use two tools here:
MeshDataTool to modify vertex position
SurfaceTool to recalculate normals and tangents
BTW, the latter is the slowest part of the algorithm. I hope there is a simple way to recalculate normals manually just for a few modifier vertices.
func modify_height(position: Vector3, radius: float, set_to: float, min = -10.0, max = 10.0):
mesh_data_tool.clear()
mesh_data_tool.create_from_surface(mesh_data, 0)
var vertice_idxs = _get_vertice_indexes(position, radius)
# Modify affected vertices
for vi in vertice_idxs:
var pos = mesh_data_tool.get_vertex(vi)
pos.y = set_to
pos.y = clampf(pos.y, min, max)
mesh_data_tool.set_vertex(vi, pos)
mesh_data.clear_surfaces()
mesh_data_tool.commit_to_surface(mesh_data)
# Generate normals and tangents
var st = SurfaceTool.new()
st.create_from(mesh_data, 0)
st.generate_normals()
st.generate_tangents()
mesh_data.clear_surfaces()
st.commit(mesh_data)
func _get_vertice_indexes(position: Vector3, radius: float)->Array[int]:
var array: Array[int] = []
var radius2 = radius*radius
for i in mesh_data_tool.get_vertex_count():
var pos = mesh_data_tool.get_vertex(i)
if pos.distance_squared_to(position) <= radius2:
array.append(i)
return array
How to modify collision shape in runtime
This is much easier than modifying of mesh. Just need to calculate a valid offset in the height map data array, and set a new value to it.
# Modify affected vertices
for vi in vertice_idxs:
var pos = mesh_data_tool.get_vertex(vi)
pos.y = set_to
pos.y = clampf(pos.y, min, max)
mesh_data_tool.set_vertex(vi, pos)
# Calculate index in height map data array
# Array is linear, and has size width*height
# Plane is centered, so left-top corner is (-width/2, -height/2)
var hmy = int((pos.z + height/2.0) * 0.99)
var hmx = int((pos.x + width/2.0) * 0.99)
height_map_shape.map_data[hmy*height_map_shape.map_width + hmx] = pos.y
Editor
I could not resist and made an in-editor landscape map (via @tool
 script, not familiar with editor plugins yet).
Demo
This is how it may look like in the game itself.
Iâve put all this on github. Maybe someday I will make an addon for the asset library.
I hope that was useful.
P.S. Check my âProtolife: Other Sideâ game. But please note: this is a simple casual arcade, not a strategy like the original âProtolifeâ. Iâve made a mistake with game naming :(