How do I increase customizability in my crater module

hey, so recently I’ve been working on a crater module, and I feel like my code is a bit awkward and messy
some problems in my module are lack of customizability, and adding more parameters will clutter and decrease readability in the function

my module is still in beta, so sorry if my code looks rlly messy

Impact crater module
local craterService = {}

local checkDistance = 8

local function typecheck(t, ...) : boolean
	for _, v in {...} do
		if typeof(v) ~= t then
			return false
		end
	end
	return true
end

local function smoothen(part:BasePart)
	part.BackSurface = Enum.SurfaceType.Smooth
	part.BottomSurface = Enum.SurfaceType.Smooth
	part.FrontSurface = Enum.SurfaceType.Smooth
	part.LeftSurface = Enum.SurfaceType.Smooth
	part.RightSurface = Enum.SurfaceType.Smooth
	part.TopSurface = Enum.SurfaceType.Smooth
end

--Service functions
function craterService:BuildCrater(
	originPoint:Vector3,
	rockAmount:number,
	originOffset:number,
	lookAt:number
)
	--Building the crater
	local lookatPos = originPoint - Vector3.new(0, lookAt, 0)
	
	for i = 0, rockAmount - 1, 1 do
		local angle = 360 / rockAmount

		local currentCFrame = CFrame.new(originPoint) * CFrame.Angles(0, math.rad(angle * i), 0)

		local offset = currentCFrame.LookVector * originOffset
		local craterSizeX = 2 * originOffset / 4
		local craterSizeZ = 2 * originOffset / 6
		--local craterSizeY = 2 * originOffset / 4
		
		--Positioning
		local newPart = Instance.new("Part")
		newPart.Position = originPoint + offset
		newPart.Size = Vector3.new(craterSizeX, 2, craterSizeZ)
		newPart.CFrame = CFrame.lookAt(newPart.Position, lookatPos)
		
		newPart.Anchored = true
		
		--Material checking
		local position = newPart.Position + Vector3.new(0, 3, 0)
		local direction = Vector3.new(0, -checkDistance, 0) --TEMPORARY!!
		local raycastResult = workspace:Raycast(position, direction)
		
		if raycastResult then
			--Debris configuration
			smoothen(newPart)
			newPart.Material = raycastResult.Material
			newPart.Color = raycastResult.Instance.Color
			newPart.Position = raycastResult.Position
			newPart.Anchored = true
			newPart.Parent = workspace
		else
			print("Nothing was hit")
		end
	end
end

function craterService:Debris(
	part:BasePart,
	position:Vector3,
	amount:number,
	size:number
)
	local Lmin = 12
	local Lmax = 25
	local Amin = 5
	local Amax = 10
	
	for i = 1, amount do
		local rockDebris = Instance.new("Part")
		
		--Replicating properties
		--  !! REMEMBER TO USE MATERIAL MAP
		rockDebris.Material = part.Material
		rockDebris.Color = part.Color
		
		rockDebris.Size = Vector3.new(size, size, size)
		rockDebris.Anchored = false
		rockDebris.Position = position + Vector3.new(0, 2, 0)
		rockDebris.CanCollide = true
		rockDebris.Parent = workspace
		
		smoothen(rockDebris)
		
		--Force decision
		local mass = math.round(rockDebris:GetMass())
		local linearForce = Vector3.new(math.random(Lmin, Lmax), math.random(Lmin, Lmax), math.random(Lmin, Lmax))
		local angularForce = Vector3.new(math.random(Amin, Amax), math.random(Amin, Amax), math.random(Amin, Amax))
		
		local linearImpulse = linearForce * mass
		local angularImpulse = angularForce * mass
		
		print(`Mass: {mass}`)
		print(`Force: {linearForce}`)
		
		if math.random(1, 2) == 1 then
			linearForce *= Vector3.new(-1, 1, -1)
			angularForce *= Vector3.new(-1, 1, -1)
			linearImpulse = linearForce * mass
			angularImpulse = angularForce * mass
		end
		
		print(`Linear Impulse: {linearImpulse}`)
		print(`Angular Impulse: {angularImpulse}`)
		rockDebris:ApplyImpulse(linearImpulse)
		rockDebris:ApplyAngularImpulse(angularImpulse)
	end
end

return craterService

this is what it looks like now

my goal with my crater is:

  1. a lot of customizability (e.g. minvelocity, maxvelocity, cratersize, craterangle, etc.)
  2. be able to set properties in the script beforehand, like this:
crater.MinVelo = 10
crater.MaxVelo = 50
crater.DebrisMaterial = Enum.Material.Slate

crater:BuildCrater()
  1. check which surface the player/object has touched (for example: if the object touches the ground, the crater will spawn on the ground, if it touches the wall, the crater will spawn on the wall)

if anybody has any suggestions, please help me out! thanks :happy4:

Hi, this looks pretty cool, i wouldnt say your script is cluttered, i think it looks nice. When you say minimum and maximum velocity i dont really know what you are talking about, but for the other things you mentioned they are pretty easy actually.

Crater size

I would just multiply the offset and the size itself by a size multiplier so the bigger the crater part is, further away it will be.

Crater angle

What i think would work for this, is multiplying the offsets CFrame, with a CFrame.Angles(). Be sure to use math.rad().

local offset = (currentCFrame.LookVector * originOffset) * CFrame.Angles(0, math.rad(ANGLE), 0)
Crater material

You basically already got this down, just pass an extra variable through the function, and instead of setting the Material property to the raycast parts material, set it to the passed variable (if it isnt nil, otherwise set it to the raycast parts material)

When you are done with these customizations be sure to send a video of the result, im excited to see how it will turn out!

hey sorry for replying late, I hadn’t had time these past few days
thanks for complimenting me on my code, I rlly appreciate it :happy4:

anyway, what I meant by “minvelocity” or “maxvelocity” was asking if there’s a way to add more customization to my impact crater system. I tied this with my goal #2, which is setting their properties beforehand

I also attached a 3rd goal, which was detecting which surface the crater is spawned on, could u help me with that?

I have modified your module to contain a few more options and to add more comments and to allow the user to edit some hidden properties, and did some other modifications with comments.

--[[
	CHANGES:
	DebugMode option
	downCheckDistance property, checks the ground for [x] distance.
	checkRaycastParams property alongside downCheckDistance.
	
	BuildCrater now returns the list of crater parts in an array.
	Debris now returns the list of debris rock parts in an array.
	
	-- REVAMPED TYPE DECLARATION --
]]

local craterService = {}

craterService.DebugMode = false
craterService.downCheckDistance = 8
craterService.checkRaycastParams = RaycastParams.new()

-- Change print to only work if DebugMode is on.

local print = print
do
	local log = print
	
	function print(...)
		if craterService.DebugMode then
			log(...)
		end
	end
end

local function smoothen(part:BasePart)
	for _,v in Enum.NormalId:GetEnumItems() do		
		part[string.format("%sSurface", v.Name)] = Enum.SurfaceType.Smooth
	end
end

-- / Service functions

function craterService:BuildCrater(originPoint,rockAmount,originOffset,lookAt)
	-- / Building the crater
	
	local CreatedCraterParts = {}
	
	for i = 0, rockAmount - 1, 1 do
		local angle = 360 / rockAmount

		local currentCFrame = CFrame.new(originPoint) * CFrame.Angles(0, math.rad(angle * i), 0)

		local offset = currentCFrame.LookVector * originOffset
		local craterSizeX = 2 * originOffset / 4
		local craterSizeZ = 2 * originOffset / 6
		--local craterSizeY = 2 * originOffset / 4

		--Positioning
		local newPart = Instance.new("Part")
		
		newPart.Position = originPoint + offset
		newPart.Size = Vector3.new(craterSizeX, 2, craterSizeZ)
		newPart.CFrame = CFrame.lookAlong(newPart.Position, Vector3.yAxis * lookAt)

		newPart.Anchored = true

		--Material checking
		local position = newPart.Position + (Vector3.yAxis * 3)
		-- Magic number again, probably to raycast from a height, would break with a roof.
		-- But craters don't appear under roofs so this should not be a problem.
		
		local raycastResult = workspace:Raycast(position, Vector3.yAxis * -craterService.downCheckDistance, craterService.checkRaycastParams)

		if raycastResult then
			--Debris configuration
			smoothen(newPart)
			
			newPart.Material = raycastResult.Material
			newPart.Color = raycastResult.Instance.Color
			newPart.Position = raycastResult.Position
			
			newPart.Anchored = true
			newPart.Parent = workspace
			
			table.insert(CreatedCraterParts, newPart)
		else
			print("Nothing was hit")
		end
	end
	
	return CreatedCraterParts
end

function craterService:Debris(part, position, amount, size)
	-- MAGIC NAMES!! --
	
	-- Linear Velocity Range
	local Lrange = NumberRange.new(
		12, 25
	)
	
	-- Angular Velocity Range --

	local Arange = NumberRange.new(
		5, 10
	)
	
	local CreatedDebrisParts = {}
	
	for i = 1, amount do
		local rockDebris = Instance.new("Part")

		--Replicating properties
		--  !! REMEMBER TO USE MATERIAL MAP
		rockDebris.Material = part.Material
		rockDebris.Color = part.Color

		rockDebris.Size = Vector3.new(size, size, size)
		rockDebris.Anchored = false
		rockDebris.Position = position + Vector3.new(0, 2, 0)
		rockDebris.CanCollide = true
		rockDebris.Parent = workspace

		smoothen(rockDebris)

		--Force decision
		local mass = math.round(rockDebris:GetMass())
		local linearForce = Vector3.new(math.random(Lrange.Min, Lrange.Max), math.random(Lrange.Min, Lrange.Max), math.random(Lrange.Min, Lrange.Max))
		local angularForce = Vector3.new(math.random(Arange.Min, Arange.Max), math.random(Arange.Min, Arange.Max), math.random(Arange.Min, Arange.Max))

		local linearImpulse = linearForce * mass
		local angularImpulse = angularForce * mass

		print(`Mass: {mass}`)
		print(`Force: {linearForce}`)

		if math.random(1, 2) == 1 then
			-- Inverts forces but does not invert Y axis.
			linearForce *= Vector3.new(-1, 1, -1)
			angularForce *= Vector3.new(-1, 1, -1)
			
			linearImpulse *= mass
			angularImpulse *= mass
		end

		print(`Linear Impulse: {linearImpulse}`)
		print(`Angular Impulse: {angularImpulse}`)
		
		-- Applying velocities --
		
		rockDebris:ApplyImpulse(linearImpulse)
		rockDebris:ApplyAngularImpulse(angularImpulse)
		
		table.insert(rockDebris, CreatedDebrisParts)
	end
	
	return CreatedDebrisParts
end

type craterService = {
	BuildCrater: (
		originPoint:Vector3,
		rockAmount:number,
		originOffset:number,
		lookAtAxis:number
	) -> {BasePart},
	
	Debris: (
		part:BasePart,
		position:Vector3,
		amount:number,
		size:number
	) -> {BasePart},
	
	DebugMode: boolean,
	downCheckDistance: number,
	checkRaycastParams: RaycastParams
}

return craterService :: craterService

Read the comments to know what I’ve changed, good luck with your module!

1 Like

thanks for helping me on call, I can customize stuff now :))

1 Like

I’ve opened this post up again, because I forgot about my 3rd goal, which is checking which surface the crater has touched, could anybody give me any solutions for this? I was thinking about casting a ray at the origin of the crater but figured that wouldn’t work…