VoxBreaker | An OOP Voxel Destruction module

Get Module Here | Test Place | Github Repo

All Documentation is included in the module.

This module was made by @Bartokens with the help of several developers from this thread. It is free to use and you don’t have to credit me. It is largely performant, but not perfect, so expect some issues with performance, especially with much larger scale destruction.

VoxBreaker is an open source voxel destruction module that gives developers several tools to work with voxel destruction. With such options as:

  • Optimized Voxelization

  • Custom Voxel Sizes

  • OOP structured hitboxes

  • Voxels resetting after a duration.

Code Rundown

So firstly, the module has several functions available to use, but generally they all do the same thing, which is divide a part into more parts. Only parts with a specified attribute name (“Destroyable” by default) can be divided.

  1. The module will first check if the part is rectangular. This is to avoid rectangular voxels.

  2. If the part is rectangular, the module cuts it in half, resulting in squares.

  3. Then, let’s say your using the :CreateHitbox() function, the module will check for all parts touching the hitbox, and then repeatedly divide them until they reach a specified minimum voxel size. It divides them using quadtree partitioning, and results in something looking like this:

Notes:

  • Voxels will not always be perfect cubes. This is to accommodate every possible part size, so if you’re dividing a part that’s incredibly thin on one axis, then the resulting voxels will be thin on one axis as well. But will still be square-shaped on the other two axes.

  • I heavily recommend changing the minimum voxel size to be bigger if your hitbox is large. This is because larger hitboxes will result in a ridiculous amount of parts if your voxel size is small, killing your performance.

  • When tagging your destroyable parts, make sure they don’t have any children. The module clones your parts, so anything parented to them will be cloned as well.

  • You can specify a minimum voxel size, but in many cases the voxels will not quite be accurate. The module will try to get as close to your specified size as possible.

Functions
:VoxelizePart()

* :VoxelizePart(Part : Part, MinimumVoxelSize : number, TimeToReset : number)

Used for when you want to divide a part down to a specified minimum size. Returns table containing voxels.

:CreateHitbox()

* :CreateHitbox(Size : Vector3, Cframe : CFrame , Shape : Enum.PartType|MeshPart , MinimumVoxelSize : number , TimeToReset : number, Params : OverlapParams)

Used to create a hitbox that checks once for parts, and subdivides any parts touching it. Returns parts touching hitbox.

.CreateMoveableHitbox()

* .CreateMoveableHitbox(MinimumVoxelSize : number, TimeToReset : number, Size : Vector3, Cframe : CFrame , Shape : Enum.PartType|MeshPart, Params : OverlapParams)

OOP BASED

Use this to create a hitbox that can be moved manually or attached to a part, and divides any part it touches continuously until :Stop() is called.

Functions:

  • :Start() - Starts moveable hitbox. Can be used to resume after :Stop() is called.

  • :Stop() - Completely stops moveable hitbox

  • :Destroy() - Destroys moveable hitbox.

  • GetTouchingParts() - Returns a table of parts touching the hitbox

  • WeldTo(part) - Welds the hitbox to a specified part.

  • UnWeld() - Unwelds the hitbox

:CutInHalf()

* :CutInHalf(Part : Part)

Use this to divide a part in half. Returns table containing halves. This is mainly here to show how the module cuts parts in halves if they're rectangular. I don't actually know if there's a use case for this, so I suggest just using this to understand how the cutting works. It changes relative to how big the part is on different axes using pretty simple math.

Examples








Known Bugs:

  • In some rare cases, voxels larger than they should be will be returned. I’ve only found this to happen when PartCache is enabled, and will only occur on the client for some reason.

  • Parts may not always be resetting as intended

Latest Update ( 7/15/24 ):

156 Likes

Wow this is amazing! So glad all of us helped you

9 Likes

UPDATE

  • Voxels that are thin on the Y axis have been fixed. Before they would divide much thinner than necessary, resulting in a ton of thin parts stacking on top of one another, looking like this:

Now, they no longer do that, with much less stacked parts, so expect a drastic increase in performance.
-The test place has also been updated to fit the new version

Specifics

Before, all parts were being divided using octrees. This would result in a cube divided into eight parts, 4 on top and 4 on the bottom. When you apply this to thin parts, you get even thinner parts, which isn’t the intended result. To fix this, I instead opted to go for quadtrees only for thin parts on the y axis, resulting in 4 parts evenly sized parts that aren’t thin. Meaning less stacking and a major increase in performance.

* Fixed parts not resetting back to normal after duration
edit: they still aren’t resetting properly :sob:
2nd edit: voxels reset properly now!

14 Likes

Why not add a minimum size? Wouldn’t that be easier to implement and less edge cases? Minimum size could be like a ratio of the base part’s size to some value

5 Likes

Minimum Size is already a variable you can use in the module. As I’ve explained, the module will try to match the voxels to the minimum size on two axes. Since it has to accommodate every possible part size, one of those axes has to be left free, which results in parts like in the image I sent in my update post. But, I’ve already fixed it, so it’s fine.

7 Likes

This is very cool! super fun to use!

But I’m wondering 3 things:

  • What if I don’t want parts to reset?
  • Thoughts on Server replication? It’d be cool to make like some eating system
  • Is it possible to detect the size of the destruction? for example to give points
4 Likes
  1. You can make the parts not reset by making the reset time less than zero.
  2. Could you give me a bit more detail?
  3. What do you mean by size?
6 Likes

Server Replication:

For performance reasons I assume this is clientsided, yet it reminded me of a game where you eat stuff to get bigger, and it is slightly competitive as the map is limited so players fight for it, so I was wondering what were your thoughts on replicating the effects to the server.

Size

For example, the bigger the hitbox is, the more it will destroy, so the more points it would give, that’s just an example so I was wondering if there was any way to see that.

-

Sorry if I did a poor job at explaining myself I’m tired.

3 Likes

If you want to use it on the server, you can. I have only tested it on the client, so I’m not sure about the performance, but its pretty likely that it wouldn’t do well on the server.

And for the size question, yeah, you can always get whatever voxels are touching the hitboxes with built-in functions. So if you have a big ball-shaped hitbox, you can get all points (I’m assuming you mean voxels/parts), that are inside of the hitbox. All the hitboxes do is divide the voxels down, what you do with them is entirely up to you. You could unanchor them, delete them, send them flying, store them somewhere else, etc.

5 Likes

Oh I see, thats awesome!

Also I just tested it on the server and it wasn’t working because I was using the MoveableHitbox and appearently RenderStepped can’t be used on the server.

Yet… I must be doing something wrong, I’m running into 2 issues here

  • It takes me multiple “hitboxes” for the destruction to show, as in I need to create a hitbox many times before the part is actually affected
  • Having a reset time less than zero makes the part just instantly disappear as soon as it is touched

Did I mess up the setup or something?

2 Likes

I’ve just updated the module again. There were a few bugs with resetting the parts, but they should all be fixed now. Reinstall the module and you should be good

(and also I forgot runservice doesn’t work on the server, you could change renderstepped to a while loop and it should work)

3 Likes

Well I tried the new version and things have changed, but I still ran into some problems.

Using any hitbox:

Using any hitbox with Reset time below 0:

Prior to this version, using a MoveableHitbox would work perfectly.

4 Likes

So I should mention, the module does not automatically destroy everything. I don’t know if this is the mistake you made, as I can’t see your script, but when you create a normal hitbox (not moveable) you can create it as a variable, and it will return all voxels in a table. If you want to destroy them, then you would loop through the table and then destroy them.
Example Included in the module:

local VoxBreaker = require(game.ReplicatedStorage.VoxBreaker)						
local Size = Vector3.new(10,10,10)
local Cframe = CFrame.new(5,5,5)
local Shape = Enum.PartType.Ball
local MinimumSize = 8
local Seconds = 20

local Voxels = VoxBreaker:CreateHitbox(Size,Cframe,Shape,MinimumSize,Seconds)
for i, v in pairs(Voxels) do
v:Destroy()
end

And as for the rest time issue, I’ll look more into it. I haven’t tested that much so I’ll see if that’s a bug.

3 Likes

Also, I’ve updated the orb tool in the test place to have flying debris.

I might make more tools to show off what the module is capable of.

5 Likes

I apologize, I wasn’t aware of that, especially since using the older version would automatically destroy the voxels for some reason.

It works flawlessly now. (except for the reset time below 0)

One last question! How would I access the table of voxels when using a MoveableHitbox?

1 Like

There is a .touched() event for MoveableHitbox objects.

Hitbox.Touched:Connect(function(Touched)
print(Touched)
end)

You can also use the :GetTouchingParts() function.

print(Hitbox:GetTouchingParts())
1 Like

Oh wait so what is the “TouchEvent” for?
image

1 Like

the TouchEvent is just the bindable event used to fire the .Touched() event. You can just ignore it.

And for most questions concerning functions and the like, all of the documentation is listed in the module with examples, so you can look through that if you’re still confused. Or ask me if you’d like. There’s likely still bugs I haven’t seen yet.

2 Likes

Yea, some of my doubts could’ve been cleared by reading the documentation, I just didn’t know it was inside the module, my bad.

3 Likes

No worries, and I don’t mind the questions either.

2 Likes