How Do I Replace Terrain In The Shape Of Spheres? (For Explosion Craters)

Im experimenting with creating a vehicular war game similar to battlefield, and im using roblox smooth voxel terrain for the maps since you can build on, and destroy them in very creative ways. Part of the destruction process involves the creation of craters whenever explosions occur against the terrain. To make it look nicer, my goal is to replace the grass inside the craters with dirt whenever they appear.

At first i used the ReplaceMaterial() method, however it requires Region3’s as parameters which are defined as square volumes. Therefore I cannot simply replace all the terrain within an explosions radius, as they are spherical & not square. (They are not compatible at all)

I searched around and could not find solutions specific to my situation. They do not involve terrain.

If all else fails I suppose I could place dirt under the grass manually so when its destroyed the dirt layer is revealed, however this would make map creation annoying and difficult. Plus it wouldn’t look as visually appealing, and be way more limited in what’s possible, so that’s definitely a last solution. I will attach a picture to help me explain.

So that’s basically my whole situation.

TLDR: The goal is effectively to replace all grass inside a spherical radius to dirt. Similar to a Region3. Since spherical Region3’s don’t exist however, does anyone know any way to replicate that effect? Even if its a long run-around one?

Any help is appreciated!

1 Like

You’ll probably have to do read/write voxels and push the appropriate materials yourself. You can calculate the distance from blast for each voxel you write too, to determine whether to keep the material or to replace it.

1 Like

I made one because I was bored.

I put this part in a module script called “Explosion” under ReplicatedStorage

local resolution = 4

return function(position, radius, dirtradius)
	local radiusVector = Vector3.one * dirtradius
	local resVector = Vector3.one * resolution

	local region = Region3.new(position - radiusVector - resVector, position + radiusVector + resVector)
	region:ExpandToGrid(resolution)

	local offset = workspace.Terrain:WorldToCell(region.CFrame.Position - region.Size/2)

	local materials, occupancies = workspace.Terrain:ReadVoxels(region, resolution)
	local tablesize = {#occupancies, #occupancies[1], #occupancies[1][1]}

	for x=1, tablesize[1] do
		for y=1, tablesize[2] do
			for z=1, tablesize[3] do
				local cellInd = offset + Vector3.new(x,y,z)
				local cellpos = workspace.Terrain:CellCenterToWorld(cellInd.X, cellInd.Y, cellInd.Z)

				local d = (position - cellpos).Magnitude
				if d < radius then
					occupancies[x][y][z] = 0  -- fully inside the explosion radius
				elseif d < radius + resolution then
					-- Partially affected, estimate occupancy
					local partialFactor = (radius + resolution - d) / resolution
					occupancies[x][y][z] = occupancies[x][y][z] * (1 - partialFactor)
				end

				if d < dirtradius then
					materials[x][y][z] = Enum.Material.Mud
				end
			end
		end
	end

	workspace.Terrain:WriteVoxels(region, resolution, materials, occupancies)
end

And for testing purposes, I put this in a local script in StarterPlayerScripts so wherever you click would explode

local mouse = game.Players.LocalPlayer:GetMouse()

local explosion = require(game:GetService("ReplicatedStorage"):WaitForChild("Explosion"))

mouse.Button1Down:Connect(function()
	local position = mouse.Hit.p
	explosion(position + Vector3.new(0, 5, 0), 20, 32)
end)
3 Likes

Thank you so much! This is a lifesaver!

I did have a little trouble at first with scaling to different sizes but i managed to figure it out. I will probably tweak the script with variables to make it more easily customizable.

I also found that ALL materials are replaced with dirt, not just grass. However this is definitely a major step in the right direction and ill take what I can get.
Im open to suggestions, but otherwise Ill probably just study the process and do some research and see if I can tweak that in as well.

Thank you again for the help!

1 Like

I’m glad it’ll help you.

As for the material overwriting problem, this conditional is the only place it’s overwriting materials.

if d < dirtradius then
	materials[x][y][z] = Enum.Material.Mud
end

Where reading materials[x][y][z] gets you the value it presently is and overwriting it is what runs it to mud. So you’ll just need an extra conditional

if d < dirtradius then
    if materials[x][y][z] == Enum.Material.Grass then
	    materials[x][y][z] = Enum.Material.Mud
    end
end

The other if statements are for cutting the hole which are safe to remove if you don’t need that.

1 Like

Oh wow I didn’t realize it was THAT easy! You’re definitely smarter then me lol.

It works perfectly! Everything on the checklist is now complete, and with very little additional code, I can even replace multiple different materials with their own individual replacement materials. (Grass to Dirt, Dirt to Mud, Sandstone to Sand, Glacier to Snow, etc etc)

Thanks again for all the help you’ve done! I really appreciate it!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.