Rock Module V.1.0 - Craters, Explosions, and more! - Effects

Modular Rock System

Hello! I have developed a Rock Module that can be used for VFX/Effects in your games, specifically fighting games. This is my first published community resource so please tell me if you think it is good or not! I developed this in ~3 hours, I’m still pretty new to module scripts. This module is client-sided, this is normal and please use it on the client.

This module allows you to:

  • Create craters, explosions, and craters of different rows.
  • Customize collisions between rocks and players.
  • Change the radius, position, minimum rocks, maximum rocks, collisions, total rocks, minimum size, maximum size and more of all rocks. Which makes it very customizable.

Grab the module here, and be sure to read below on how to set it up and use it:
Rock Module V.1.0 - Effects

You can view what the module looks like in action here:


Setup:

  1. Place the module in ReplicatedStorage, and the ServerScript in ServerScriptService.
  2. Require the module and call it’s functions.

Functions:

Crater(Center, Radius, MinRocks, MaxRocks, PlayerCollision)

This function will create a crater of rocks, you can view this in action here:
https://www.youtube.com/watch?v=deRQYs3Tnx4&ab_channel=LuckyEcho

Arguments:

  • Center is the center of the crater, this should be a CFrame.
  • Radius is the radius of the rock crater, should be a number.
  • MinRocks is the minimum number of rocks that should spawn, should be a number.
  • MaxRocks is the maximum number of rocks that should spawn, should be a number.
  • PlayerCollision is if the rocks should be able to collide with the player, should be a boolean.

Example:

Module.Crater(character:FindFirstChild("HumanoidRootPart").CFrame, 7, 7, 10)
CraterRows(Center, Radius, Rows, NextRowRadius, MinRocks, MaxRocks, PlayerCollision)

This function will create a row of crater of rocks, you can view this in action here:

**Arguments:** - Center is the center of the crater, this should be a CFrame. - Radius is the radius of the rock crater, should be a number. - Rows is the amount of craters there should be, each one bigger than the other, should be a number. - NextRowRadius is how much bigger the next crater should be, this should be a number. - MinRocks is the minimum number of rocks that should spawn, should be a number. - MaxRocks is the maximum number of rocks that should spawn, should be a number. - PlayerCollision is if the rocks should be able to collide with the player, should be a boolean.

Example:

Module.CraterRows(character:FindFirstChild("HumanoidRootPart").CFrame, 7, 2, 4, 7, 10)
Explosion(Center, TotalRocks, MinSize, MaxSize, PlayerCollision)

This function will create a explosion of rocks, you can view this in action here:
https://www.youtube.com/watch?v=h4_GVSxImqQ&ab_channel=LuckyEcho

Arguments:

  • Center is the center of the crater, this should be a CFrame.
  • TotalRocks is how many rocks spit out, should be a number.
  • MinSize is the minimum size of rocks that should be allowed, should be a number.
  • MaxSize is the maximum size of rocks that should be allowed, should be a number.
  • PlayerCollision is if the rocks should be able to collide with the player, should be a boolean.

Example:

Module.Explosion(character:FindFirstChild("HumanoidRootPart").CFrame, 7, 0.5, 2.5)
ClearDebris(Fade)

This function will create a explosion of rocks, you can view this in action here:
https://www.youtube.com/watch?v=0TS-oke81DE&ab_channel=LuckyEcho

Arguments:

  • Fade is if all the debris should fade out with transparency and size, should be a boolean.

Example:

Module.ClearDebris(true)

IMPORTANT

  • This is made for client side, this makes the server not lag. If you want it to show up for everyone just use a RemoteEvent
  • This does not work on Terrain, it has not been tested.
  • Combine the Explosion and Crater for cool effects of exploding craters, which is seen in the video.
  • Any BaseParts/Meshes with the tag “RockModuleIgnore” will be ignored in the raycast to make the rocks.
Example Script (StarterCharacterScripts)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Module = require(ReplicatedStorage.RockModule)
local Player = game.Players.LocalPlayer
local Character = Player.Character
local RootPart = Character:WaitForChild("HumanoidRootPart")
local Mouse = Player:GetMouse()


game:GetService("UserInputService").InputBegan:Connect(function(input, typing)
	if typing then return end
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		Module.Crater(RootPart.CFrame, 7, 7, 10, true)
		Module.Explosion(RootPart.CFrame, 7, 0.5, 2.5, false)
	elseif input.UserInputType == Enum.UserInputType.MouseButton3  then
		Module.CraterRows(RootPart.CFrame, 7, 3, 4, 7, 10, true)
		Module.Explosion(RootPart.CFrame, 40, 0.5, 2.5, false)
	elseif input.KeyCode == Enum.KeyCode.F then
		Module.Explosion(RootPart.CFrame, 7, 0.5, 2.5, false)
	elseif input.KeyCode == Enum.KeyCode.G then
		Module.Explosion(RootPart.CFrame, 40, 0.5, 2.5, false)
	elseif input.KeyCode == Enum.KeyCode.E then
		Module.ClearDebris(true)
	elseif input.KeyCode == Enum.KeyCode.H then
		Module.ClearDebris(false)
	end
end)

Source Code

local RockModule = {}
local TweenSerivce = game:GetService("TweenService")
local Players= game:GetService("Players")
local DebrisFolder


local function CheckDebrisFolder()
	if not workspace:FindFirstChild("Debris") then
		DebrisFolder = Instance.new("Folder")
		DebrisFolder.Name = "Debris"
		DebrisFolder.Parent = workspace
	else
		DebrisFolder = workspace:FindFirstChild("Debris")
	end
end



function RockModule.Crater(Center : CFrame, Radius : number, MinRocks : number, MaxRocks : number, PlayerCollision : boolean)
	Radius = Radius or 7
	MinRocks = MinRocks or 7
	MaxRocks = MaxRocks or 10
	if PlayerCollision == nil then
		PlayerCollision = true
	end
	
	
	CheckDebrisFolder()
	
	local raycastParams = RaycastParams.new()
	
	local players = {DebrisFolder}
	for i, v in pairs(Players:GetPlayers()) do
		local Character = v.Character
		if Character then
			table.insert(players, Character)
		end
	end
	for i, v in pairs(game.Workspace:GetDescendants()) do
		if game:GetService("CollectionService"):HasTag(v, "RockModuleIgnore") then
			table.insert(players, v)
		end
	end
	raycastParams.FilterDescendantsInstances = players
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.IgnoreWater = true
	local ray = workspace:Raycast(Center.Position, Center.UpVector * -1000, raycastParams)
	
	if ray then
	local numRocks = math.round(math.random(MinRocks, MaxRocks))
		local distance = Radius
		local newCframe = CFrame.new(ray.Position)
		local angle = 0
		
		for i = 1, numRocks do
			local Rock = Instance.new("Part")
			Rock.Parent = DebrisFolder
			Rock.Name = "GroundSlam"
			Rock.Size = Vector3.new(math.random(3,5), math.random(2, 3), math.random(2,4))
			Rock.CFrame = newCframe * CFrame.Angles(0, math.rad(angle), 0) * CFrame.new(0,0, -distance) + Vector3.new(0, -5, 0)
			Rock.CFrame = CFrame.lookAt(Rock.Position, ray.Position)
			local ray2 = workspace:Raycast(Rock.CFrame.Position + Vector3.new(0,7,0), Vector3.new(0,1,0) * -1000, raycastParams)
			if ray2 then
				Rock.Color = ray2.Instance.Color
				Rock.Material = ray2.Instance.Material
				Rock.Transparency = ray2.Instance.Transparency
				
			else
				Rock.Color = ray.Instance.Color
				Rock.Material = ray.Instance.Material
				Rock.Transparency = ray.Instance.Transparency
			end
			if PlayerCollision == false then
				Rock.CollisionGroup = "RockDebris"
			end
			
			Rock.Anchored = true
			
			Rock.CanCollide = true
			game:GetService("Debris"):AddItem(Rock, 30)
			local Tween = TweenSerivce:Create(Rock, TweenInfo.new(0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {Position = Rock.Position + Vector3.new(0, 4.5, 0)})
			Tween:Play()
			task.spawn(function()
				task.wait(math.random(650, 950) / 100)
				local Tween = TweenSerivce:Create(Rock, TweenInfo.new(4, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {Position = Rock.Position + Vector3.new(0, -5, 0), Size = Vector3.new(1,1,1)})
				Tween:Play()
			end)
			angle += 360/numRocks
		end
	end
	
end

function RockModule.Explosion(Center : CFrame, TotalRocks : number, MinSize : number, MaxSize : number, PlayerCollision : boolean)
	TotalRocks = TotalRocks or 7
	MinSize = MinSize or 0.5
	MaxSize = MaxSize or 2.5
	PlayerCollision = PlayerCollision or false
	
	local numOfRocks = TotalRocks
	CheckDebrisFolder()
	
	local raycastParams = RaycastParams.new()
	local players = {DebrisFolder}
	for i, v in pairs(Players:GetPlayers()) do
		local Character = v.Character
		if Character then
			table.insert(players, Character)
		end
	end
	for i, v in pairs(game.Workspace:GetDescendants()) do
		if game:GetService("CollectionService"):HasTag(v, "RockModuleIgnore") then
			table.insert(players, v)
		end
	end
	raycastParams.FilterDescendantsInstances = players
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.IgnoreWater = true

	local ray = workspace:Raycast(Center.Position, Center.UpVector * -100, raycastParams)
	
	if ray then
		for i = 1, numOfRocks do
			local Rock = Instance.new("Part")
			Rock.Name = "ExplosionPart"
			Rock.Parent = DebrisFolder
			
			local RandomSize = (math.random(MinSize * 100, MaxSize * 100)) / 100.0
			if math.random(1, 5) == 3 then
				Rock.Size = Vector3.new(RandomSize * 2, 0.15, RandomSize * 2)
			else
				Rock.Size = Vector3.new(RandomSize, RandomSize, RandomSize)
			end
			
			Rock.CFrame = Center
			Rock.Color = ray.Instance.Color
			Rock.Material = ray.Instance.Material
			Rock.Anchored = false
			Rock.Orientation = Vector3.new(math.random(-359, 359),math.random(-359, 359),math.random(-359, 359))
			Rock.Transparency = ray.Instance.Transparency
			Rock.CanCollide = true
			if PlayerCollision == false then
				Rock.CollisionGroup = "RockDebris"
			end
			
			
			game:GetService("Debris"):AddItem(Rock, 4.5)
			
			local velocitySpread = 32 
			local upwardForce = 22
			local velocity = Vector3.new(math.random(-velocitySpread, velocitySpread),upwardForce,math.random(-velocitySpread, velocitySpread))
			local bodyVelocity = Instance.new("BodyVelocity")
		
			bodyVelocity.Velocity = velocity
			bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
			bodyVelocity.P = 5000
			bodyVelocity.Parent = Rock
			
			game:GetService("Debris"):AddItem(bodyVelocity, 0.25)
			task.spawn(function()
				task.wait(4)
				local Tween = TweenSerivce:Create(Rock, TweenInfo.new(0.75, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut), {Size = Vector3.new(0.1,0.1,0.1), Transparency = 1}):Play()	
			end)
		end
	end
end

function RockModule.CraterRows(Center : CFrame, Radius : number, Rows: number, NextRowRadius : number, MinRocks : number, MaxRocks : number, PlayerCollision : boolean)
	if PlayerCollision == nil then
		PlayerCollision = true
	end
	Radius = Radius or 7
	MinRocks = MinRocks or 7
	MaxRocks = MaxRocks or 10
	Rows = Rows or 3
	NextRowRadius = NextRowRadius or 4
	local currentRadius = Radius
	for i = 1, Rows do
		RockModule.Crater(Center, currentRadius, MinRocks * i, MaxRocks * i, PlayerCollision)
		currentRadius += NextRowRadius
	end
end

function RockModule.ClearDebris(Fade : boolean)
	CheckDebrisFolder()
	
	if Fade then
		for i, v in pairs(DebrisFolder:GetChildren()) do
			if v:IsA("BasePart") then
				local Tween = TweenSerivce:Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut), {Size = Vector3.new(0.1,0.1,0.1), Transparency = 1}):Play()	
				game:GetService("Debris"):AddItem(v, 1)
				
			end
		end
	else
		for i, v in pairs(DebrisFolder:GetChildren()) do
			v:Destroy()
		end
	end
end



return RockModule

Thank you for making to the end, if you have any problems just reply, or feedback!

19 Likes

Looks great but not really realistic as the crater just pops out from it’s position. I suggest makig it spread from the explosion point which will be more realistic.

1 Like

It does Tween upwards, and also you got to think if the center is going down, the edges get brought up which creates the crater ring.

this is cool!
ive been expanding your module and its been really fun lol, I wish there was a v2 or something!!!

Ooo, nice! I would love to see what you added to it :grin:. As for a V2, I don’t know yet what else to add, any suggestions would be great!

I mostly added a few extra arguments such as fading, rotation, realtime size tween, etc and sound effects
as for the rock functions themselves I added a couple more
the first being a simple line, then I added a multi line function where it can do multiple of the first line with a desired amount and spacing as arguments, the third one is a sort of line crater if that makes sense ? basically 3-6 lines (depending on the argument) that intersect forming a circle, the fourth is an alternate version of the explosion where the cubes stay a certain amount of time in the air before dropping (time depends on argument) and the last one is just the explosion one without the actual exploding part, just spawns a few cubes at the desired location

that’s pretty much it lol
I hope it gives more ideas for the v2!
good luck :smiley:

1 Like

how to make it server sided with events? im trying do that, im know nothing in modules

here script:

local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")


PhysicsService:RegisterCollisionGroup("Players")
PhysicsService:RegisterCollisionGroup("RockDebris")
PhysicsService:CollisionGroupSetCollidable("Players", "RockDebris", false)

Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		for i, v in pairs(character:GetChildren()) do
			if v:IsA("BasePart") or v:IsA("MeshPart") then
				v.CollisionGroup = "Players"
			end
		end
	end)
end)

local RockModule = {}
local TweenSerivce = game:GetService("TweenService")
local Players= game:GetService("Players")
local DebrisFolder


local function CheckDebrisFolder()
	if not workspace:FindFirstChild("Debris") then
		DebrisFolder = Instance.new("Folder")
		DebrisFolder.Name = "Debris"
		DebrisFolder.Parent = workspace
	else
		DebrisFolder = workspace:FindFirstChild("Debris")
	end
end

local fold = game.ReplicatedStorage.events.rock_events

local exp = fold.explosion
local crater_rows = fold.crater_rows
local crater = fold.crater
local debris_clear = fold.clear_debris

exp.OnServerEvent:Connect(function(Center : CFrame, TotalRocks : number, MinSize : number, MaxSize : number, PlayerCollision : boolean)
	TotalRocks = TotalRocks or 7
	MinSize = MinSize or 0.5
	MaxSize = MaxSize or 2.5
	PlayerCollision = PlayerCollision or false

	local numOfRocks = TotalRocks
	CheckDebrisFolder()

	local raycastParams = RaycastParams.new()
	local players = {DebrisFolder}
	for i, v in pairs(Players:GetPlayers()) do
		local Character = v.Character
		if Character then
			table.insert(players, Character)
		end
	end
	for i, v in pairs(game.Workspace:GetDescendants()) do
		if game:GetService("CollectionService"):HasTag(v, "RockModuleIgnore") then
			table.insert(players, v)
		end
	end
	raycastParams.FilterDescendantsInstances = players
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.IgnoreWater = true

	local ray = workspace:Raycast(Center.Position, Center.UpVector * -100, raycastParams)

	if ray then
		for i = 1, numOfRocks do
			local Rock = Instance.new("Part")
			Rock.Name = "ExplosionPart"
			Rock.Parent = DebrisFolder

			local RandomSize = (math.random(MinSize * 100, MaxSize * 100)) / 100.0
			if math.random(1, 5) == 3 then
				Rock.Size = Vector3.new(RandomSize * 2, 0.15, RandomSize * 2)
			else
				Rock.Size = Vector3.new(RandomSize, RandomSize, RandomSize)
			end

			Rock.CFrame = Center
			Rock.Color = ray.Instance.Color
			Rock.Material = ray.Instance.Material
			Rock.Anchored = false
			Rock.Orientation = Vector3.new(math.random(-359, 359),math.random(-359, 359),math.random(-359, 359))
			Rock.Transparency = ray.Instance.Transparency
			Rock.CanCollide = true
			if PlayerCollision == false then
				Rock.CollisionGroup = "RockDebris"
			end


			game:GetService("Debris"):AddItem(Rock, 4.5)

			local velocitySpread = 32 
			local upwardForce = 22
			local velocity = Vector3.new(math.random(-velocitySpread, velocitySpread),upwardForce,math.random(-velocitySpread, velocitySpread))
			local bodyVelocity = Instance.new("BodyVelocity")

			bodyVelocity.Velocity = velocity
			bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
			bodyVelocity.P = 5000
			bodyVelocity.Parent = Rock

			game:GetService("Debris"):AddItem(bodyVelocity, 0.25)
			task.spawn(function()
				task.wait(4)
				local Tween = TweenSerivce:Create(Rock, TweenInfo.new(0.75, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut), {Size = Vector3.new(0.1,0.1,0.1), Transparency = 1}):Play()	
			end)
		end
	end
end)

crater.OnServerEvent:Connect(function(Center : CFrame, Radius : number, MinRocks : number, MaxRocks : number, PlayerCollision : boolean)
	Radius = Radius or 7
	MinRocks = MinRocks or 7
	MaxRocks = MaxRocks or 10
	if PlayerCollision == nil then
		PlayerCollision = true
	end


	CheckDebrisFolder()

	local raycastParams = RaycastParams.new()

	local players = {DebrisFolder}
	for i, v in pairs(Players:GetPlayers()) do
		local Character = v.Character
		if Character then
			table.insert(players, Character)
		end
	end
	for i, v in pairs(game.Workspace:GetDescendants()) do
		if game:GetService("CollectionService"):HasTag(v, "RockModuleIgnore") then
			table.insert(players, v)
		end
	end
	raycastParams.FilterDescendantsInstances = players
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.IgnoreWater = true
	local ray = workspace:Raycast(Center.Position, Center.UpVector * -1000, raycastParams)

	if ray then
		local numRocks = math.round(math.random(MinRocks, MaxRocks))
		local distance = Radius
		local newCframe = CFrame.new(ray.Position)
		local angle = 0

		for i = 1, numRocks do
			local Rock = Instance.new("Part")
			Rock.Parent = DebrisFolder
			Rock.Name = "GroundSlam"
			Rock.Size = Vector3.new(math.random(3,5), math.random(2, 3), math.random(2,4))
			Rock.CFrame = newCframe * CFrame.Angles(0, math.rad(angle), 0) * CFrame.new(0,0, -distance) + Vector3.new(0, -5, 0)
			Rock.CFrame = CFrame.lookAt(Rock.Position, ray.Position)
			local ray2 = workspace:Raycast(Rock.CFrame.Position + Vector3.new(0,7,0), Vector3.new(0,1,0) * -1000, raycastParams)
			if ray2 then
				Rock.Color = ray2.Instance.Color
				Rock.Material = ray2.Instance.Material
				Rock.Transparency = ray2.Instance.Transparency

			else
				Rock.Color = ray.Instance.Color
				Rock.Material = ray.Instance.Material
				Rock.Transparency = ray.Instance.Transparency
			end
			if PlayerCollision == false then
				Rock.CollisionGroup = "RockDebris"
			end

			Rock.Anchored = true

			Rock.CanCollide = true
			game:GetService("Debris"):AddItem(Rock, 30)
			local Tween = TweenSerivce:Create(Rock, TweenInfo.new(0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {Position = Rock.Position + Vector3.new(0, 4.5, 0)})
			Tween:Play()
			task.spawn(function()
				task.wait(math.random(650, 950) / 100)
				local Tween = TweenSerivce:Create(Rock, TweenInfo.new(4, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut), {Position = Rock.Position + Vector3.new(0, -5, 0), Size = Vector3.new(1,1,1)})
				Tween:Play()
			end)
			angle += 360/numRocks
		end
	end
end)

crater_rows.OnServerEvent:Connect(function(Center : CFrame, Radius : number, Rows: number, NextRowRadius : number, MinRocks : number, MaxRocks : number, PlayerCollision : boolean)
	if PlayerCollision == nil then
		PlayerCollision = true
	end
	Radius = Radius or 7
	MinRocks = MinRocks or 7
	MaxRocks = MaxRocks or 10
	Rows = Rows or 3
	NextRowRadius = NextRowRadius or 4
	local currentRadius = Radius
	for i = 1, Rows do
		RockModule.Crater(Center, currentRadius, MinRocks * i, MaxRocks * i, PlayerCollision)
		currentRadius += NextRowRadius
	end
end)

debris_clear.OnServerEvent:Connect(function(Fade : boolean)
	CheckDebrisFolder()

	if Fade then
		for i, v in pairs(DebrisFolder:GetChildren()) do
			if v:IsA("BasePart") then
				local Tween = TweenSerivce:Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut), {Size = Vector3.new(0.1,0.1,0.1), Transparency = 1}):Play()	
				game:GetService("Debris"):AddItem(v, 1)

			end
		end
	else
		for i, v in pairs(DebrisFolder:GetChildren()) do
			v:Destroy()
		end
	end
end)

I’m pretty sure you don’t want to make something like this server sided as it will just end up clogging the server with useless things
instead just use the :FireAllClients() method on remote events instead
then in the .OnServerEvent:Connect() function, you can do the rocks there

roblox link is private (character limit)