How do you position multiple parts between 2 positions, while making them centered

  1. What do you want to achieve?
    Hi, im trying to make a cards game and i want to position the cards across a part like this: (kind of like the UIGridLayout when set to “Center.”)

But I also would like the cards to be positioned equal distances from each-other without from the center, like this:

  1. What is the issue?
    So far ive been trying to divide the X size of the main part, by the number of cards. Then positioning the card by doing (card number * result of previous equation.)
    Which results in the cards still not being in the center.
local cards = workspace.Cards:GetChildren()
local l1 = workspace.Layers.Layer1 --BasePart for cards to go against

local cardsXOrigin = l1.Position-Vector3.new(0,0,(l1.Size.X/2))
--Create part to see where the cardsXOrigin is
local p = Instance.new("Part",workspace); p.Position = cardsXOrigin; p.Anchored = true; p.Size = Vector3.new(.1,.1,.1); p.Name = "A"; p.Color = Color3.fromRGB(255, 128, 0)

local offs = l1.Size.X/#cards
---[[Loop through all the cards and position them acoordingly
for i,card in ipairs(cards) do
	card.CFrame = l1.CFrame*CFrame.new(Vector3.new(0,0,-l1.Size.Z/2 - card.Size.Z/2)) --Positioning them on top of the layer1 part
	card.Position = Vector3.new(card.Position.X,card.Position.Y,(cardsXOrigin.Z+i*offs)) --Moving it across the Z axis
end
---]]

This is the result im getting:

I’ve tried offsetting each card, by half the X size of the card.
But it still isn’t centered:


The code i used to offset it:

card.Position = Vector3.new(card.Position.X,card.Position.Y,(cardsXOrigin.Z+i*offs))+Vector3.new(0,0,-card.Size.X/2)

Any help would be apreciated.
Thanks!

Assuming the width of each card Wc is the same and fixed, and that the width of their container Wt is fixed, you need to calculate the spacing S between each card as well as the spacing between the outermost cards and the edges of the container, which should be the same.

For any number of cards N, you want N+1 spaces. Since you want the cards and the spaces together to exactly take up the width of the container, the width of the container must equal the sum of card widths and space widths:

Wt = N * Wc + (N+1) * S

Solving for S:

S = (Wt - N * Wc) / (N+1)

Expressed in code:

function calculateCardSpacing(containerWidth, cardWidth, numCards)
    local n = numCards
    return (containerWidth - n * cardWidth) / (n + 1)
end

Or a bit more readable:

function calculateCardSpacing(containerWidth, cardWidth, cards)
    local nCards = #cards
    local nSpacings = nCards + 1
    local cardsWidth = nCards  * cardWidth
    local spacingsWidth = containerWidth - cardsWidth
    local spacingWidth = spacingsWidth / nSpacings 
    return spacingWidth
end

You’ll need to compute the position of a card given its card number:

function calculateCardOffset(cardWidth, spacingWidth, cardNumber)
    local numCardsBefore = cardNumber - 1
    local numSpacingsBefore = cardNumber
    return numCardsBefore * cardWidth + numSpacingsBefore * spacingWidth
end

function calculateCardPositions(containerPoint1, containerPoint2, cardWidth, numCards)
    local containerDiff = containerPoint2 - containerPoint1
    local containerWidth = containerDiff.Magnitude
    local containerDir = containerDiff.Unit
    
    local spacingWidth = calculateCardSpacing(containerWidth, cardWidth, numCards)
    local positions = {}
    for i = 1, numCards do
        positions[i] = containerPoint1 + containerDir * calculateCardOffset(cardWidth, spacingWidth, i)
    end
    return positions
end

function updateCardPositions(containerPoint1, containerPoint2, cardWidth, cards)
    local numCards = #cards
    local positions = calculateCardPositions(containerPoint1, containerPoint2, cardWidth, numCards)
    for i, card in ipairs(cards) do
        card.Position = positions[i]
    end
end

Hope this helps a bit :sweat_smile:

1 Like

Wow!
First of all, thank you so much for spending the time figuring all this out!

I am currently testing the code you have given, and attempting to grasp everything written.

And as of now, I have ran your code without changing anything other than the cards and container of course. But unfortunately the cards still seem to be mostly drifting to one containerPoint.

Here is an image:

This is assuming containerPoint1 is the very left point of the container and containerPoint2 is the very right point of the container.

What could I be doing wrong?
Thanks again in advance!

[Edit]
Forgot to add the update code:

updateCardPositions(l1.Position+Vector3.new(0,0,(l1.Size.X/2)), l1.Position-Vector3.new(0,0,(l1.Size.X/2)),Cards[1].Size.X, Cards)

I don’t know which parts are l1 and l2, but if their center is at the left and right edges of the container, you don’t need to offset the container points by half their size.

“l1” Is my badly named container, sorry for the confusion.
In that code i was just setting the containerPoint1 and containerPoint2 to the edges of the container

Hey!
After tweaking it for a bit, I have managed to fix it!
The “calculateCardOffset” function returns the very left position of the card.

The simple solution to this, is to simply add half the card’s width to correctly offset the card’s position.

Thank you again for this, ill still mark your original reply as the solution!

Here is the fix:

function calculateCardOffset(cardWidth, spacingWidth, cardNumber)
	local numCardsBefore = cardNumber - 1
	local numSpacingsBefore = cardNumber
	return numCardsBefore * cardWidth + numSpacingsBefore * spacingWidth + cardWidth/2
end
1 Like