Battlegrounds crater module

I made a rock module similar to the The Strongest Battlegrounds. Right now it’s only a crater function but I might add more to this. This module uses a raycast to spawn the crater and also uses the PartCache module for better performance.

module link: https://create.roblox.com/store/asset/113713371540830/Crater?tab=description

showcase:

here are the default settings I use:

utility.Crater.GroundCrater(
	Part.Position, Vector3.new(0,-10,0), 10, 9, Vector3.new(2,3,2), true
)
-- // Services // --
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")

-- // Modules // --
local partCache = require(script.PartCache).new(Instance.new("Part"), 2000, workspace.Debris)

local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Include
Params.FilterDescendantsInstances = {workspace.Map}

-- // Function // --

local module = {}

function module.GroundCrater(origin: Vector3, direction: Vector3, radius: number, amount: number, size: Vector3, flyingrocks: boolean)
	local random = Random.new()

	-- Starting angle for placement and angle increment per rock
	local currentAngle = 30
	local angleIncrement = 360 / amount

	local groundRay = workspace:Raycast(origin, direction, Params)

	if groundRay then
		for i = 1, amount do
			coroutine.wrap(function()
				local baseCFrame = CFrame.new(groundRay.Position)
				local offsetCFrame = baseCFrame * CFrame.fromEulerAnglesXYZ(0, math.rad(currentAngle), 0) * CFrame.new(radius / 2 + radius / 15, 10, 0)
				
				local placementRay = workspace:Raycast(offsetCFrame.Position, Vector3.new(0, -15, 0), Params)
				currentAngle += angleIncrement

				if placementRay then
					local finalCFrame = CFrame.new(
						placementRay.Position - Vector3.new(0, size.Y * random:NextNumber(0.325, 0.45), 0),
						groundRay.Position
					) * CFrame.fromEulerAnglesXYZ(
						random:NextNumber(-1, -0.4),
						random:NextNumber(-0.1, 0.1),
						random:NextNumber(-0.1, 0.2)
					)
					local hidingPosition = finalCFrame.Position + placementRay.Normal * -8

					local part = partCache:GetPart()
					part.Size = Vector3.new(
						size.X * random:NextNumber(1.15, 1.5),
						size.Y * random:NextNumber(0.725, 0.9),
						size.Z * random:NextNumber(1.15, 1.5)
					) * random:NextNumber(1, 1.5)
					part.CFrame = finalCFrame
					part.Position = hidingPosition
					part.Material = placementRay.Instance.Material
					part.Transparency = placementRay.Instance.Transparency
					part.Color = placementRay.Instance.Color
					part.CanCollide = true
					part.CanTouch = false
					part.CastShadow = false
					part.Parent = workspace.FX -- replace with your own effects folder

					-- Tween the part into its final position
					TweenService:Create(part, TweenInfo.new(0.1), {Position = finalCFrame.Position}):Play()

					-- Fade out and return the part to the cache after a delay
					task.delay(5, function()
						TweenService:Create(
							part,
							TweenInfo.new(2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut),
							{Position = hidingPosition}
						):Play()
						task.delay(2, function()
							partCache:ReturnPart(part)
						end)
					end)
				end
			end)()
		end
		
		if flyingrocks then
			local sizeSwitch = 1
			
			for i = 1, math.round(amount / 1.5) do

				local rock = partCache:GetPart()
				rock.Position =  groundRay.Position + Vector3.new(0,2,0)
				rock.CollisionGroup = "Debris"
				rock.CanCollide = true
				rock.Anchored = false
				rock.Color = groundRay.Instance.Color
				rock.Material = groundRay.Instance.Material
				rock.CFrame = rock.CFrame * CFrame.new(math.random(-3, 3),1,math.random(-3, 3)) * CFrame.Angles(math.random(-360, 360), math.random(-360, 360), math.random(-360, 360))
				
				if sizeSwitch % 2 == 0  then
					rock.Size = Vector3.new(random:NextNumber(.3, .7),random:NextNumber(.3, .7),random:NextNumber(.3, .7))
				else
					rock.Size = Vector3.new(random:NextNumber(.75, 2.5),random:NextNumber(.75, 2.5),random:NextNumber(.75, 2.5))
				end
				
				rock.Parent = game.Workspace.FX
				
				rock.Velocity = CFrame.new(groundRay.Position + Vector3.new(0,-3, 0) , rock.Position).lookVector * (29 * random:NextNumber(.8, 1.2)) + Vector3.new(0, math.random(35,55), 0)
				
				task.delay(math.random(3,5), function()
					local tweeninfo = TweenInfo.new(5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
					local properties = {
						Size = Vector3.new(0,0,0)
					}

					local tween = TweenService:Create(rock, tweeninfo, properties)
					tween:Play()

					task.delay(5, function()
						partCache:ReturnPart(rock)
					end)
				end)
				
				sizeSwitch += 1
			end
		end
	end
end

return module
7 Likes