VoxBreaker | An OOP Voxel Destruction module

I wouldn’t say its ‘better’ than VB since any kind of voxel destruction tools on roblox are a great step in the right direction.

It is very performant though if that is what you’re asking, at least if you dont go overboard with it. It has features like greedy meshing and part cache to help reduce part count and make it run smoothly on the server. I mean with any large amount of parts created quickly you will have lag, but theres features for making your voxel and debris sizes relative to compensate for that.

Any tool like this is going to have some lag, mine reduces it as much as possible using the same techniques in JJS but it won’t be enough to compensate for your games lag if you don’t use it mindfully.

1 Like

Finally got around to releasing it and making a page for it, but then the mods had to be vultures and close the thread before I had time to make all my changes. :man_facepalming:

Hope its fine if I post it here, I’d like to see it used successfully.

2 Likes

how should i make a hitbox that follows the player performant

because each time, i use moveable hitbox and just make it unanchor every voxel caught in the hitbox and that always ends up lagging a lot if i ever hit the ground unless i raise the voxel size above something like 4 or 5

1 Like

You’ll have to share your usage of this function, as well as what your code looks like on the client.

1 Like

whenever i try to voxelize a single part with partcache on it returns a error. (server)

	local VoxBreaker = require(game.ReplicatedStorage.Modules.VoxBreaker)		
    local vox = VoxBreaker:VoxelizePart(game.Workspace.PART, 5, 3)
ReplicatedStorage.Modules.VoxBreaker.PartCache:141: Attempted to return part "Breakable" (Workspace.Placee.Breakable) to the cache, but it's not in-use! Did you call this on the wrong part?

i also get errors like these from voxelizing parts in a model (server) – Not home rn so code below may be from a old script

local VoxBreaker = require(game.ReplicatedStorage.Modules.VoxBreaker)		

for i,v in pairs(game.Workspace.Train:GetChildren()) do
	if v:IsA("Part") then
		local voxelize2 = VoxBreaker:VoxelizePart(v, 5, 3)
	end
end
The Parent property of Part is locked, current parent: NULL, new parent VoxelHolder 
ReplicatedStorage.Modules.VoxBreaker:443: attempt to index nil with 'IsA'
1 Like

Some of the big parts or the outer parts are being counted somehow as a debris.
Is there any fix for this since its pretty annoying
image

2 Likes

Yeah this is a bug. Theres a few other issues that i will be fixing in the next update as well.

2 Likes

for some reason the table of the dissected parts return nil, any clue to why this is? The hitboxes do get created, that I am sure of, because the parts light up. However the part doesn’t get voxelated.

1 Like

Resolved, forgot to add attribute ‘Destroyable’

2 Likes

I’m having an issue with starting moveable hitboxes right now. Error : 12:12:41.498 ReplicatedStorage.Modules.VoxBreaker:1231: attempt to index nil with ‘Part’ - Client - VoxBreaker:1231 .
Here is the localscript that is running VoxBreaker :

local voxBreaker = require(game:GetService("ReplicatedStorage").Modules.VoxBreaker)
local event = game:GetService("ReplicatedStorage").Events.DestructionClient

local function shuffleTable(t)
	print("Shuffling table...")
	local n = #t
	for i = n, 2, -1 do
		local j = math.random(i)
		t[i], t[j] = t[j], t[i]
	end
end

local function onEvent(player, force, velocityTime, size, cframe, shape, minimumVoxelSize, destroyPercent, weldToPart)
	print("Received destruction event for player:", player.Name)
	if not weldToPart or weldToPart == nil then
		local character = player.Character
		local parts = voxBreaker:CreateHitbox(size, cframe, shape, minimumVoxelSize)
		print("Created hitbox parts:", #parts)

		-- Create a table of indices and shuffle it
		local indices = {}
		for i = 1, #parts do
			table.insert(indices, i)
		end
		shuffleTable(indices)

		-- Destroy 50% of the parts
		local destroyCount = math.floor(#parts / destroyPercent)
		print("Destroying", destroyCount, "parts out of", #parts)
		for i = 1, destroyCount do
			local partIndex = indices[i]
			parts[partIndex]:Destroy()
			print("Destroyed part", i, "with index", partIndex)
		end

		-- Add BodyVelocity to the remaining parts
		for i, v in pairs(parts) do
			if v.Parent then -- Check if the part was not destroyed
				v.Anchored = false
				local bv = Instance.new("BodyVelocity", v)
				bv.Velocity = character.HumanoidRootPart.CFrame.LookVector * force
				bv.MaxForce = Vector3.new(99999, 99999, 99999)
				bv.Name = "Velocity"
				game:GetService("Debris"):AddItem(bv, velocityTime)
				print("Added BodyVelocity to part:", v.Name)
			else
				print("Part", v.Name, "was destroyed before adding BodyVelocity")
				return
			end
		end
	end
	if weldToPart and weldToPart ~= nil then
		local character = player.Character
		local parts = voxBreaker.CreateMoveableHitbox(minimumVoxelSize, 50, size, weldToPart.CFrame, shape)
		parts.Start()
		print("started")
		parts.WeldTo(weldToPart)
		print("welded")
		print("Created movable hitbox parts:", #parts)
		
		if not weldToPart.Parent then
			parts.Stop()
			parts.Destroy()
			print("voxelbreaker destroyed early")
		end

		-- Create a table of indices and shuffle it
		local indices = {}
		for i = 1, #parts do
			table.insert(indices, i)
		end
		shuffleTable(indices)

		-- Destroy 50% of the parts
		local destroyCount = math.floor(#parts / destroyPercent)
		print("Destroying", destroyCount, "parts out of", #parts)
		for i = 1, destroyCount do
			local partIndex = indices[i]
			parts[partIndex]:Destroy()
			print("Destroyed part", i, "with index", partIndex)
		end

		-- Add BodyVelocity to the remaining parts
		for i, v in pairs(parts) do
			if v.Parent then -- Check if the part was not destroyed
				v.Anchored = false
				local bv = Instance.new("BodyVelocity", v)
				bv.Velocity = character.HumanoidRootPart.CFrame.LookVector * force
				bv.MaxForce = Vector3.new(99999, 99999, 99999)
				bv.Name = "Velocity"
				game:GetService("Debris"):AddItem(bv, velocityTime)
				print("Added BodyVelocity to part:", v.Name)
			else
				print("Part", v.Name, "was destroyed before adding BodyVelocity")
				return
			end
		end
	end	
end

event.OnClientEvent:Connect(onEvent)
print("Connected onEvent function to client event:", event.Name)

Does anyone have any ideas on how to fix?

EDIT: YOU CAN FIX IT BY DOING

parts:Start()
parts:Stop()
parts:Destroy()
--Instead of doing
parts.Start()
parts.Stop()
parts.Destroy()

@Bartokens is there a way to make the module destroy the parts of the main part clean it up but not reset the main part

You mean to not reset the part after a duration? You would do this by just setting reset time to -1 or any number below zero

no like don’t reset specific parts with stuff in them i have this building mechanic and if you destroy them they wouldn’t reset used with a diff voxel module but i like the parts flying out of this module and destroying themselves after a period of time.

easier terms if it has a value in the part that gets destroyed → it doesnt reset except for the little voxel parts

So in other words you want the voxels to be reset but not the original part? I mean an easy way to do this would be to just set the reset time to below zero, and then destroy the voxels manually

not really what i mean if the original part has a value it cant be reset if the created building which would be the wall . and the building was in front of walls of the map the wall of the map is destroyed and doesnt reset is what i dont want . I want the building to be the only one to not reset not the wall of the map

for some reason setting it to below 0 like -1 creates an error

Yeah I’m sorry, I have no idea what your asking.

should i show what i mean
like a video
from a game what i wanna replicate

Go ahead, would help me better visualize it