You’d definitely want to use matrices to achieve this. For example:
Let’s say I wanted a Diamond formation around the player
--[[
DIAMOND FORMATION
0 0 0 1 0 0 0
0 0 1 0 1 0 0
0 1 0 2 0 1 0
0 0 1 0 1 0 0
0 0 0 1 0 0 0
0 - EMPTY
1 - GUARD
2 - PLAYER
--]]
Great, now what? Well, let’s make it into a table.
local diamondPattern = {
{0, 0, 0, 1, 0, 0, 0},
{0, 0, 1, 0, 1, 0, 0},
{0, 1, 0, 2, 0, 1, 0},
{0, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0},
}
Now the rest is easy. We just loop through each value of the table, and start from an origin position of however many studs away from the player is.
Let’s define those values:
local Players = game:GetService("Players")
local playerToGuard = Players:FindFirstChild("thePlayerName")
local characterToGuard = playerToGuard.Character or playerToGuard.CharacterAdded:Wait()
local formationHeight = #diamondPattern
local formationWidth = diamondPattern[1] -- We are using 1 because any other relative value would produce the same width
-- Getting the distance away from the character
local distanceHeight, distanceWidth = formationHeight / 2, formationWidth / 2
local cloneSize -- You do this part, since I am not sure what clone model you are using
local startCFrame = characterToGuard.CFrame * CFrame.new(-(distanceHeight * cloneSize), 0, -(distanceWidth * cloneSize))
Now for our for loops
for i, row in pairs(diamondPattern) do
for j, designation in pairs(row) do
if designation == 1 then
-- Spawn at startCFrame * CFrame.new(-((i - 1) * cloneSize), 0, (j - 1) * cloneSize)
elseif designation == 2 then
-- That is the player!
end
end
end
Let me know if this helped.