World
DestructionService
Make parts and models breakable. Health, real mesh fracturing, pre-fractured pieces, explosions, debris clean-up and structural collapse.
Getting it
local DestructionService = Engine:GetService("DestructionService")Overview
DestructionService turns ordinary parts and models into destructibles. A destructible has health; when it runs out, the object is cut into real mesh shards (using GeometryService-style mesh generation) that fly apart with physics, settle, and are cleaned up after a while.
local DestructionService = Engine:GetService("DestructionService")
local wall = DestructionService:MakeDestructible(workspace.Wall, {
health = 250,
pieces = 24,
effect = "WallCrumble", -- an EffectService effect, optional
debris = { removal = "Fade", delay = { min = 8, max = 14 } },
})
wall.Damaged:Connect(function(amount, health)
print("wall at", health)
end)
-- From a weapon
wall:Damage(40)
-- Or blow up everything nearby
DestructionService:Explode(position, { radius = 20, damage = 300 })Fracturing live or ahead of time
Cutting a mesh into pieces takes time. For objects that must break instantly, pre-fracture them while the game loads: the pieces are computed once and kept hidden, and breaking only swaps them in.
DestructionService:PrefractureManyAsync(workspace.Breakables:GetChildren(), { pieces = 20 })
:andThen(function()
print("all breakables ready")
end)Where to call it
Destructibles work on both the server and the client.
- Server-side destructibles break for everyone and are the safe choice for gameplay objects.
- Client-side destructibles break only for that player, which is cheap and good for decoration. Make the same object destructible on the server too; see Client breaks and Server Authority for what the server does with it.
- With
authoritative = true, a client's:Damage()and:BreakAsync()calls are sent to the server instead of applied locally.
Client breaks and Server Authority
When an object breaks on one client only, the client tells the server, and the server looks at who moves that player's character:
- The client moves its own character, as in classic games. The player walks through the gap on their own screen, and the object keeps colliding on the server, so server raycasts and NPCs still hit a wall that is broken on one screen only.
- The server moves the character, with Server Authority or in a game that keeps characters owned by the server. The server turns
CanCollide,CanQueryandCanTouchoff on its copy while a player has the object broken, so that player can walk through it, and turns them back on once no player has it broken any more (it was repaired, or the player left). This needs the object to be destructible on the server too; Engine warns you when it is not.
Making things destructible
MakeDestructible
DestructionService:MakeDestructible(target: Model | BasePart, config: MakeDestructibleConfig?): Destructible| Field | Description |
|---|---|
health: number? | Starting and maximum health. |
pieces: number? | How many shards to aim for. |
fracture: FractureOverrides? | { pattern?, pieces?, seed?, minPieceSize?, useCompute? }. |
prefracturedFolder: Folder? | Use these pieces instead of cutting the mesh. |
debris: DebrisConfig? | How shards behave and disappear, see Debris. |
structural: boolean? | false keeps this object out of support graphs. |
effect: string? | An EffectService effect to play where it breaks. |
authoritative: boolean? | Route client damage through the server. |
Fracture patterns:
| Pattern | Look |
|---|---|
"Voronoi" | Irregular chunks, like stone or concrete. Default. |
"Radial" | Shards spreading out from the impact point, like glass. |
"Slab" | Flat layers, like planks or stacked slabs. |
Shards smaller than minPieceSize (bounding-box diagonal in studs) are dropped. With useCompute and a piece count at or above the configured threshold, cutting runs in parallel through ComputeService. A seed makes the fracture repeatable.
GetDestructible
DestructionService:GetDestructible(target: Instance): Destructible?Destructible
| Member | Description |
|---|---|
:Damage(amount, breakConfig?): boolean | Removes health; breaks at 0 and returns true when this call broke it. |
:BreakAsync(breakConfig?): Promise<{ BasePart }> | Breaks right away and resolves with the pieces. Calling it again while breaking returns the same Promise. |
:Repair() | Removes the pieces and puts the original back at full health. |
:SetPrefracturedFolder(folder?) | Use (or stop using) pre-made pieces. |
:Destroy() | Stops tracking the object. |
Health, MaxHealth, Broken, Destroyed, PieceCount, Authoritative, Instance | State. |
Damaged: Signal<(amount, health)> | After each hit. |
Fractured: Signal<pieces> | When it broke. |
Repaired: Signal | After :Repair(). |
BreakConfig is { ImpulseOrigin: Vector3?, ImpulseMagnitude: number? }: where the blast comes from and how hard pieces are thrown.
Pieces are added to the world in small batches per frame (DefaultFinalizeBatchSize), so even a large break does not cause a hitch.
What the shards look like
Shards look like the part they came from:
- A MeshPart's
SurfaceAppearancecarries over: its color map with itsColortint andAlphaMode, and its normal, metalness, roughness and emissive maps through aSurfaceAppearancemade withAssetService:CreateSurfaceAppearanceAsync. Engine uses the appearance whoseSurfaceSlotis All, or the one for slot 0 on a mesh with a single material. - Decals keep their
Color3andTransparencyand stack byZIndex. A transparent texture or decal shows the part's color underneath. - A pixelated texture (
ResampleMode = Pixelatedon a Decal, Texture or SurfaceAppearance) stays pixelated, and a textured MeshPart keeps itsTransparency. - The cut faces show the texture of the part they were cut from.
All textures of one break share a single texture atlas of at most 1024 by 1024 pixels, and are scaled down when they do not fit. A texture the game may not load does not stop the break: those shards use the part's color, and one warning names the asset.
Explosions
Explode
DestructionService:Explode(position: Vector3, config: { radius: number, damage: number, impulse: number?, falloff: boolean?, losCheck: boolean? })Damages every destructible within radius. With falloff (default on) damage and impulse shrink with distance. With losCheck an object behind cover is not hit. Only destructibles are affected; damage players yourself.
Pre-fracturing
| Method | Description |
|---|---|
:PrefractureAsync(target, config?): Promise<Folder> | Computes the pieces and stores them in a hidden folder. Config: { pieces?, fracture?, parent? }. |
:PrefractureManyAsync(targets, config?): Promise<{ [Instance]: Folder }> | The same for a list. |
:IsPrefractured(target): boolean | |
:ClearPrefracture(target) | Deletes the stored pieces. |
PrefractureRendered: Signal<(target, folder)> | Fires when pieces are ready. |
Pre-fractured pieces are used automatically when the object is made destructible, before or after pre-fracturing.
Debris
| DebrisConfig field | Description |
|---|---|
removal | "Despawn", "Fade", "Sink", or false to keep pieces. |
delay | Seconds before removal starts, or { min, max } for a random delay. |
fadeSpeed, fadeEasingStyle, fadeEasingDirection | Fade tween. |
sinkSpeed, sinkDepth, sinkEasingStyle, sinkEasingDirection | Sink-into-the-ground tween. |
physics | Throw pieces outward when breaking. |
anchorAfterSettle | Anchor pieces once they stop moving, to save physics time. |
ignorePlayers | Characters walk through shards and kick them aside instead of tripping over them. |
There is a global cap on live debris pieces. When it is exceeded the oldest pieces are removed and DebrisBudgetExceeded fires with the number removed.
| Member | Description |
|---|---|
:SetDebrisBudget(maxParts?) | Changes the cap. |
DebrisCount: number | Live debris right now. |
DebrisBudget: number? | The current cap. |
DebrisBudgetExceeded: Signal<number> |
Structural collapse
BuildSupportGraph
DestructionService:BuildSupportGraph(model: Model, config: { supportTag: string?, autoCollapse: boolean? }?): SupportGraphWorks out which parts of a model touch each other. When a part is destroyed, any part that is no longer connected to the ground, directly or through other parts, falls: with autoCollapse (default on) it is unanchored and becomes debris, and SupportBroken fires.
A part counts as ground when it is Anchored or has the supportTag CollectionService tag. Parts that are neither are held up only through their connections, so leave the parts that should be able to fall unanchored (for example welded to the structure) and keep the foundation anchored or tagged.
| SupportGraph member | Description |
|---|---|
:Recompute() | Rebuild connections after you changed the model. |
:RemovePart(part) | Treat a part as gone and check what falls. Destructibles in the model do this when they break. |
:Destroy() | |
Model, PartCount, Destroyed | Read-only. |
| Method / event | Description |
|---|---|
:GetSupportGraph(model) | The model's graph, if built. |
SupportBroken: Signal<(model, fallenPieces)> | Parts lost their support. |
Configuration
DefaultConfiguration/Services/DestructionService40 settings| Setting | Default | What it does |
|---|---|---|
| DefaultHealth | 100 | Health :MakeDestructible() gives an object when config.health is omitted. |
| DefaultFracturePattern | "Voronoi" | "Voronoi" | "Radial" | "Slab". |
| DefaultPieces | 16 | Pieces a fracture produces when fracture.pieces is omitted. |
| DefaultMinPieceSize | 0.5 | Studs; a shard whose bounding-box diagonal is below this is discarded. |
| DefaultSeedJitter | 0.6 | 0..1, how far a Voronoi seed is pulled from its source vertex toward the mesh centroid. |
| DefaultUseCompute | false | Whether fracture clipping may run through ComputeService. |
| DefaultComputeThreshold | 9999 | Pieces at/above which a fracture goes parallel when UseCompute is on. |
| DefaultFinalizeBatchSize | 16 | Pieces parented/registered per Heartbeat before a break yields. |
| DefaultShardName | "Shard" | Name of every shard MeshPart. |
| DefaultShardCollisionFidelity | "Hull" | Enum.CollisionFidelity name of a shard; Hull is exact and cheapest for a convex Voronoi cell. |
| DefaultShardShrink | 0.97 | 0..1 scale toward its own centroid a shard gets before commit. |
| DefaultShardRenderFidelity | "Precise" | Enum.RenderFidelity name of a shard. |
| DefaultShardCanCollide | true | CanCollide of a shard. |
| DefaultShardCanTouch | true | CanTouch of a shard. |
| DefaultShardCanQuery | true | CanQuery of a shard. |
| DefaultDebrisBudget | 200 | Max live debris parts engine-wide before oldest-eviction kicks in. |
| DefaultDebrisPhysics | true | Whether freshly broken pieces get an outward impulse. |
| DefaultDebrisAnchorAfterSettle | true | Whether a piece is anchored once its velocity settles. |
| DefaultSettleVelocityThreshold | 0.5 | Studs/s below which a piece counts as at rest. |
| DefaultSettleSeconds | 1.5 | Seconds a piece must stay at rest before it is anchored. |
| DefaultShardIgnorePlayers | false | Whether characters pass through shards and kick loose ones aside instead of colliding with them. |
| DefaultShardPushRadius | 1.25 | Studs around a character's centre line that push an IgnorePlayers shard away. |
| DefaultShardPushStiffness | 8 | Studs/s of push per stud a shard sits inside that radius. |
| DefaultShardPushLift | 0.15 | Upward share of a push, so a kicked shard hops instead of sliding. |
| DefaultShardMaxPushSpeed | 40 | Studs/s cap on the velocity change a single push gives a shard. |
| DefaultRemoval | "Despawn" | "Despawn" | "Fade" | "Sink" | false. |
| DefaultRemovalDelayMin | 12 | Seconds before removal starts (lower bound of the random delay). |
| DefaultRemovalDelayMax | 12 | Seconds before removal starts (upper bound); equal Min/Max is a fixed delay. |
| DefaultFadeSpeed | 1.5 | Seconds the Fade tween takes. |
| DefaultFadeEasingStyle | "Linear" | Enum.EasingStyle name of the Fade tween. |
| DefaultFadeEasingDirection | "Out" | Enum.EasingDirection name of the Fade tween. |
| DefaultSinkSpeed | 1.5 | Seconds the Sink tween takes. |
| DefaultSinkEasingStyle | "Linear" | Enum.EasingStyle name of the Sink tween. |
| DefaultSinkEasingDirection | "Out" | Enum.EasingDirection name of the Sink tween. |
| DefaultExplodeFalloff | true | Whether :Explode() scales damage down with distance from the centre. |
| DefaultExplodeLosCheck | false | Whether :Explode() requires line of sight to damage a target. |
| DefaultExplodeImpulse | 40 | Studs/s velocity change applied to broken debris at the explosion centre. |
| DefaultAdjacencyMargin | 0.5 | Studs of padding when :BuildSupportGraph() confirms two parts touch. |
| DefaultAutoCollapse | true | Whether a part is unanchored the moment it loses support. |
| DefaultAuthoritative | false | Whether a Destructible routes client damage through the server when config.authoritative is omitted. |
Types
export type Destructible
export type SupportGraph
export type MakeDestructibleConfig
export type FractureOverrides
export type DebrisConfig
export type ExplodeConfig
export type SupportGraphConfig
export type PrefractureConfig