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.
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?
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.
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)
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.
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.
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!