VoxBreaker | An OOP Voxel Destruction module

nvm i got it figured out thankfully

2 Likes

Hello there, its me again. I kinda re-made this module with a different and more performance-friendly method, thats also probably easier to edit for beginners. Here’s how it works :

First, we need a target part and a subtractor and the subtractor is gonna act like the hitbox of your version.

Then we voxelize the whole target using pretty accurate voxels (almost perfect cubes), the last step consist in removing all the voxels touched by our subtractor. This gives this pretty clean results.

gianfragolo's Place_ 05012024_1 - Roblox Studio 2024-05-01 09-42-58

Here is the code if anyone is wondering how i made this.

--[[

USAGE :

local mod = require(game.ReplicatedStorage.ModuleScript)
mod.voxelizeHitbox(workspace.Target,workspace.Subtractor)

]]

local destruct = {}

function destruct.voxelizeAll(target : Part, divider : number, anchor : boolean, velocity : number)
	if target == nil then return end
	
	divider = divider or 2
	anchor = anchor or false
	velocity = velocity or 100
	
	local newContainer = Instance.new("Model", workspace)
	newContainer.Name = "container"

	local Size = target.Size
	local SizeX,SizeY,SizeZ = Size.X/divider,Size.Y/divider,Size.Z/divider

	local selectedCorner = target.CFrame*CFrame.new(Size.X/2,Size.Y/2,-Size.Z/2)

	for x = 0, divider-1 do
		for y = 0, divider-1 do
			for z = 0, divider-1 do
				local generatedPosition = CFrame.new(-SizeX/2,-SizeY/2,SizeZ/2).Position+selectedCorner.Position+Vector3.new(-x*SizeX,-y*SizeY,z*SizeZ)			
				local newCube = Instance.new("Part", newContainer)
				
				newCube:AddTag("debris")
				newCube.Anchored = true
				newCube.CanCollide = true
				newCube.Size = Vector3.new(SizeX,SizeY,SizeZ)
				newCube.BrickColor = BrickColor.random()
				newCube.Position = generatedPosition
				newCube.Massless = true
				newCube:ApplyImpulse(newCube.CFrame.LookVector * velocity)
			end
		end
	end
end

function destruct.voxelizeHitbox(target : Part, subtract : Part, params : OverlapParams, divider : number)
	if target == nil then return end
	if subtract == nil then return end
	
	params = params or OverlapParams.new()
	divider = divider or 6
	destruct.voxelizeAll(target, divider, true, 0)
	target:Destroy()
	
	game:GetService("RunService").Heartbeat:Connect(function(dty)
		local touching = workspace:GetPartsInPart(subtract)

		if #touching ~= 0 then
			for _,v in pairs(touching) do
				if v:HasTag("debris") then
					v.Anchored = false 
					v:Destroy()
				--	v:ApplyImpulse(v.CFrame.LookVector * 100)
				end
			end
		end
	end)
end

return destruct

Edit : i might turn this into an actual resource later on.

4 Likes

ok my pethod has proved to be really unreliable somehow n trying to fix this has drained me beyond belief so can u tell me if im supposed to:

  1. make the voxels on the server, fire the voxel table to client to do whatever, and then destroy the server voxels
    or
  2. make the voxels on the server and fire each individual one to the client
1 Like

oh and by the way the voxel table i fire to the client happens to not contain the recently created voxels, but only the parts that were already voxelized n just happened to be in the hitbox

1 Like

There isn’t any particular way you have to do it, you can do it however you’d like. But I’ll just share how I went about it.

Firstly, you create the voxels on the server.

Then, you would parent the voxels to replicated storage. The reason for this being is that for some reason the voxels are not visible on the client, and if you were to try and send that table through a remote, it would appear empty. So, you parent them to replicatedStorage so that the client can see them.

Then, fire a remote event to the client containing the table of voxels.

Then destroy all of them on the Server, AFTER firing the remote.

Then, on the client, you loop through the table of voxels and clone them, then parent them to workspace.

And finally, you have debris freshly made on the client.

Now if you were to unanchor the voxels, to avoid players appearing as they are floating due to some potential discrepancies between clients, I suggest changing the collisiongroups of the voxels so that they cannot collide with players.

2 Likes

ok im gonna try this rn and let u know how it goes

2 Likes

oh my god youre so smart thank you

2 Likes

Hello, heres how he does it. He creates a hole based on the hitbox of how much he wants a hole by making 4 parts, then calculating the whole part that got removed. Then slicing the part that got removed into 1 stud or 2 stud pieces.

Rather than turning the whole wall into a grid when making a hole, he makes it with only 4 blocks.

1 Like

Hello bartokens, to add to your module, you can use partcache module. This is something jujutsu shenanigans uses. It boasts performance way better

2 Likes

Got the module some days ago, made my own personal optimizations and works clean on my game, I use both in server (only the final result for server collisions, they are not replicated to the client) and in client, it takes FPS to decide how accurate the voxels should be, along with limiting how many voxels are created and adjusting the minimum size according to the hitbox size (so if it is a larger hitbox, the voxel minimum size increases), and instead of using the get parts in part i use GetPartBoundsInBox and other functions to work with spheres and cylinders with overlap parameters that tell which parts I can destroy, along with checking if the parts are inside of the hitbox so it doesn’t voxelize them, saving on part count, there’s only a small spike on big hitboxes but I’m pretty happy with the final result, hope my findings can help somehow. Might test with PartCache too in the future.
Here’s me using it in a real server, while showing stats + server FPS:
Cylinder
Sphere

3 Likes

Very cool stuff, and I think you might already know this, but if you want to fix the lag spike with larger hitboxes then you would have to raise the minimum voxel size.

(Also, I was going to use getPartsInBoundBox originally, but I thought it only worked for rectangular/cube shaped boxes? I swapped to getPartsInPart so that I could make hitboxes of any part shape)

A lot of suggestions have been made recently, so I will be updating the module soon. I’ll look into partCache, and I will also be adding support for overlapParams, as well as the ability to make the minimum voxel size relative to the size of the hitbox. (Also I will be making the github repo, I’ve been working on a personal project so I haven’t had the time to do so)

1 Like

Hey man, love the module, tried to make one of my own but found yours and it was much better. I have a few things, 1. there is a bug when a player dies the parts wont reset. 2. jujutsu shenanigans will split parts like it will take the hitbox size and make a section the size of the hitbox, resulting in 3 parts (top, hitbox size, bottom) then it will cut on the x axis (left, center, right) then will finally voxelize that hitbox sized cube/ rectangle. They use part cache like duckling said for 200% better perfomance. and the only other issue im running into is the voxel generation bug when the server has any kind of stress over 15kbs (fully server sided like jjk shenanigans) im gonna wait a week and work on other parts of my game until you update the module, but cant wait to see what it ends up like!

Edit: heres the destruction they use if i explained it poorly:

2 Likes


Anyone know how to fix this or why this is happening? It used to work perfectly fine now it’s wonky and works weirdly, I’m creating a hitbox on the server then sending the voxels to the client to clone then destroying it on the server.

Edit: I made a semi-working fix but if you have any ideas feel free to share.

hi you know how i can make this work with a model touching the part ? pls help and ty

I love this type of voxel, but is there any way to properly have the hitbox follow the player without delay?

Try welding an hitbox to the player humanoid root part and use the module on the client. If you want destruction to be visible for everyone (so on the server) then you could try looking into network ownership maybe.

2 Likes

Could you elaborate? The module checks for parts, so if there are parts inside your model then it should work fine

it does have parts inside the model

Have you given each individual part the “Destroyable” attribute with a value set to true?

yea, but I already solved the problem :smiley: was just a little tricky setting it up cuz I was not planning on using tools.

1 Like