Cframe/math.random help? position not accurate

So, im making this random gift spawning script that should spawn in the area of a baseplate, but sometimes the gifts spawn off the baseplate, and how can I simplify my script a lot more?? I have about 5 of these similar scripts but for different areas/different Y values. (Some of this is created with AI, so bare with me because I know its long and boring!)

Server Script:

local rs = game:GetService("ReplicatedStorage")
local pugr2 = rs.pickedUpGiftRemote2

local players = game:GetService("Players")


local smallGift = workspace.SpawnGifts:WaitForChild("SmallGift")
local mediumGift = workspace.SpawnGifts:WaitForChild("MediumGift")
local largeGift = workspace.SpawnGifts:WaitForChild("LargeGift")

local giftsFolder = workspace.SpawnGifts

local giftsInFolder = giftsFolder:GetChildren()


local baseplate = game.Workspace.GiftSpawnArea

local GiftsSpawned = 0


-- Calculate the spawn area based on the baseplate model
local function calculateSpawnArea(baseplateModel)
	local size = baseplateModel:GetExtentsSize()  -- Get overall size
	local cframe = baseplateModel:GetBoundingBox() -- Get center position and orientation

	local halfX = size.X / 2
	local halfZ = size.Z / 2

	-- Calculate boundaries for X and Z based on the model’s center and size
	local minX = cframe.Position.X - halfX
	local maxX = cframe.Position.X + halfX
	local minZ = cframe.Position.Z - halfZ
	local maxZ = cframe.Position.Z + halfZ

	-- Calculate the top Y position
	local topY = cframe.Position.Y + (size.Y / 2)

	return minX, maxX, minZ, maxZ, topY
end

-- Update your spawn functions to use the calculated area
local function spawnSmallGifts()
	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function

	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)


	wait()

	-- Clone and position the gift
	local clonedSmallGift = smallGift:Clone()
	clonedSmallGift:PivotTo(CFrame.new(randomX, 82.875, randomZ)) -- Use topY for Y coordinate
	clonedSmallGift.Parent = game.Workspace.SpawnGifts
	
	
end

-- Repeat the same update for `spawnMediumGifts` and `spawnLargeGifts`

local function spawnMediumGifts()

	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function
	

	-- Generate random positions within the baseplate boundaries
	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)
	
	
	wait()


	local clonedmediumGift = mediumGift:Clone()

	clonedmediumGift:PivotTo(CFrame.new(randomX, 83.75, randomZ)) 
	clonedmediumGift.Parent = game.Workspace.SpawnGifts
	--------------------------------------------------------------------------------------
end

local function spawnLargeGifts()

	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function


	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)


	wait()

	-- Clone and position the gift
	local clonedLargeGift = largeGift:Clone()

	clonedLargeGift:PivotTo(CFrame.new(randomX, 85.5, randomZ)) 
	clonedLargeGift.Parent = game.Workspace.SpawnGifts
	
	
	
end
local smallGiftChance = 0.5  -- 50% chance to spawn a small gift
local mediumGiftChance = 0.4 -- 40% chance to spawn a medium gift
local largeGiftChance = 0.1  -- 10% chance to spawn a large gift

local GiftsSpawned = 0
local maxGifts = 3
local status = true


-- Function to reset gift spawning and re-enable status
local function resetGiftSpawning()
	wait(1)
	GiftsSpawned = 0
	status = true
	startGiftSpawningLoop()  -- Restart the spawning loop
end

-- Event listener function for when the player picks up a gift
local function onGiftCollected()
	print(GiftsSpawned)
	GiftsSpawned -= 1  -- Reduce count when a gift is picked up
	if GiftsSpawned <= 0 then
		status = true
		wait(3)
		resetGiftSpawning()  -- Reset when all gifts are collected
	end
	
	print("Gift collected. Gifts left:", GiftsSpawned)
end

-- Function to handle the spawning loop
function startGiftSpawningLoop()

	while status do
		wait()
		if GiftsSpawned >= maxGifts then
			status = false
		else
		-- Generate a random number for each type of gift
		if math.random() < smallGiftChance then
			spawnSmallGifts()
			GiftsSpawned += 1
		end
		if math.random() < mediumGiftChance then
			spawnMediumGifts()
			GiftsSpawned += 1
		end
		if math.random() < largeGiftChance then
			spawnLargeGifts()
			GiftsSpawned += 1
		end

		
			wait(1)
			print("Gifts Spawned:", GiftsSpawned)
			
		end

	end

	print("Status:", status)  -- This will print once per loop iteration
end


players.PlayerAdded:Connect(function(plr)
	wait(3)
	startGiftSpawningLoop()
end)


pugr2.Event:Connect(onGiftCollected)




(Example of what happens sometimes)
image

(Example of a baseplate area)

1 Like

I would try rounding up with math.ceil() it will give you a more precise result, you can also use math.clamp() to clamp the minimum and maximum, that my also help.

1 Like

Which area is the Baseplate in your picture? If you have the white area then the bounding box of that complex section is going to be it’s outermost ends and the concave sections will be included.

@JAcoboiskaka1121 math.round() is for rounding up or down depending on the decimal.
math.ceil() rounds up to the next integer.
math.floor() rounds down to the lower integer.

1 Like

Also ensure your Model’s Pivot in studio is (re)set properly.

Yes the white parts is the “Baseplate part”, and that the concave sections will be included, is that why it spawns outside of it? I’ll test the math.round() & math.ceil() soon! THANKS FOR EVERYONE WHO RESPONDED TOO

I know what the functions do, When I made my own building system, I used math.ceil. If it rounds up and down, the offset will be wrong depending on the float. I just like using it.

@JAcoboiskaka1121 @Scottifly Thanks, I think Math.clamp() was the best option, here is what was received:

(gifts on baseplate/ not off it)

I would like it if this script was more simplified, if you can help. (Shorter/Better)

local rs = game:GetService("ReplicatedStorage")
local pugr2 = rs.pickedUpGiftRemote2

local players = game:GetService("Players")


local smallGift = workspace.SpawnGifts:WaitForChild("SmallGift")
local mediumGift = workspace.SpawnGifts:WaitForChild("MediumGift")
local largeGift = workspace.SpawnGifts:WaitForChild("LargeGift")

local giftsFolder = workspace.SpawnGifts

local giftsInFolder = giftsFolder:GetChildren()


local baseplate = workspace.SpawnGifts.GiftSpawnArea2

local GiftsSpawned = 0

local smallY = 86.875
local mediumY = 87.75
local largeY = 89.5


-- Calculate the spawn area based on the baseplate model
local function calculateSpawnArea(baseplateModel)
	local size = baseplateModel:GetExtentsSize()  -- Get overall size
	local cframe = baseplateModel:GetBoundingBox() -- Get center position and orientation

	local halfX = size.X / 2
	local halfZ = size.Z / 2

	-- Calculate boundaries for X and Z based on the model’s center and size
	local minX = cframe.Position.X - halfX
	local maxX = cframe.Position.X + halfX
	local minZ = cframe.Position.Z - halfZ
	local maxZ = cframe.Position.Z + halfZ

	-- Calculate the top Y position
	local topY = cframe.Position.Y + (size.Y / 2)

	return minX, maxX, minZ, maxZ, topY
end

-- Update your spawn functions to use the calculated area
local function spawnSmallGifts()
	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function
	
	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)
	
	
	local clampX = math.clamp(randomX, minX, maxX)
	local clampZ = math.clamp(randomZ, minZ, maxZ)

	wait()

	local pos = CFrame.new(clampX, smallY, clampZ)
	-- Clone and position the gift
	local clonedSmallGift = smallGift:Clone()
	clonedSmallGift:PivotTo(pos) -- Use topY for Y coordinate
	clonedSmallGift.Parent = game.Workspace.SpawnGifts
	
	print(clampX, clampZ)
	
end

-- Repeat the same update for `spawnMediumGifts` and `spawnLargeGifts`

local function spawnMediumGifts()

	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function
	local valueX = -150
	local valueY = 85.25

	-- Generate random positions within the baseplate boundaries
	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)
	
	local clampX = math.clamp(randomX, minX, maxX)
	local clampZ = math.clamp(randomZ, minZ, maxZ)

	wait()


	local clonedmediumGift = mediumGift:Clone()

	clonedmediumGift:PivotTo(CFrame.new(clampX, mediumY, clampZ)) 
	clonedmediumGift.Parent = game.Workspace.SpawnGifts


	print(clampX, clampZ)
	--------------------------------------------------------------------------------------
end

local function spawnLargeGifts()

	local minX, maxX, minZ, maxZ, topY = calculateSpawnArea(baseplate) -- Call the function

	local randomX = math.random(minX, maxX)
	local randomZ = math.random(minZ, maxZ)

	local clampX = math.clamp(randomX, minX, maxX)
	local clampZ = math.clamp(randomZ, minZ, maxZ)

	wait()

	-- Clone and position the gift
	local clonedLargeGift = largeGift:Clone()

	clonedLargeGift:PivotTo(CFrame.new(clampX, largeY, clampZ)) 
	clonedLargeGift.Parent = game.Workspace.SpawnGifts

	
	print(clampX, clampZ)

end


local GiftsSpawned = 0
local maxGifts = 5
local status = true

-- Define chances for gift types
local smallGiftChance = 0.40 -- 45% chance
local mediumGiftChance = 0.30 --= 35% chance
local largeGiftChance = 0.3 -- 20% chance

-- Function to reset gift spawning and re-enable status
local function resetGiftSpawning()
	wait(1)
	GiftsSpawned = 0
	status = true
	startGiftSpawningLoop()  -- Restart the spawning loop
end

-- Event listener function for when the player picks up a gift
local function onGiftCollected()
	print(GiftsSpawned)
	GiftsSpawned -= 1  -- Reduce count when a gift is picked up
	if GiftsSpawned <= 0 then
		status = true
		wait(3)
		resetGiftSpawning()  -- Reset when all gifts are collected
	end

	print("Gift collected. Gifts left:", GiftsSpawned)
end

-- Function to handle the spawning loop
function startGiftSpawningLoop()

	while status do
		wait()
		if GiftsSpawned >= maxGifts then
			status = false
		else
			-- Generate a random number for each type of gift
			if math.random() < smallGiftChance then
				spawnSmallGifts()
				GiftsSpawned += 1
			end
			if math.random() < mediumGiftChance then
				spawnMediumGifts()
				GiftsSpawned += 1
			end
			if math.random() < largeGiftChance then
				spawnLargeGifts()
				GiftsSpawned += 1
			end



			wait(1)
			print("Gifts Spawned:", GiftsSpawned)

		end

	end

	print("Status:", status)  -- This will print once per loop iteration
end


players.PlayerAdded:Connect(function(plr)
	wait(3)
	startGiftSpawningLoop()
end)


pugr2.Event:Connect(onGiftCollected)