I managed to come up with a solution a while back, I don’t have time to post it rn remind me
I am creating a script that positions the child instances of the parent that is tagged in a list formation. You can think of the UI List Layout constraint where the UI elements are able to be formated from top to bottom (or left to right), but instead the container or the “constraints size” would be the parent and the elements are the children of the parent.
I am creating this for a menu gui and need the ui elements to be in-order because I plan on expanding the ui and would like to just add buttons guilt-free without spending time tweeking positions to fit the camera.
also, yes I aware that 3d ui frameworks exist but I have reasons I don’t want to use this.
The Problem:
I am struggling with apling the gap-size calculation to position the children of the parent to fit the proper constraint. Additionally, I would like to calculate the X or Z for horizontal lists but Im not sure how to implement that because of rotation and I don’t have the greatest grasp of CFrames besides basic calculations.
[/details]
The Script
type self = {
contraintObject: BasePart;
ConstraintFlex: string;
RespectContainerSize: boolean;
FillDirection: string;
SortOrder: string;
ContainerSize: Vector3;
Elements: {[number]: BasePart};
ContentSize: Vector3;
FreeSpace: Vector3;
NumberOfGaps: number;
GapSize: Vector3;
Initilize: () -> ();
Destroy: () -> ();
} -- Added for readablity not for strict mode
local ONLY_Y = Vector3.new(0,1,0)
local ListContraint = {}
function ListContraint.new(model): self
local self = setmetatable({
contraintObject = model
} :: self, {__index = ListContraint})
self.ConstraintFlex = self.contraintObject:GetAttribute("ConstraintFlex") or "SpaceEvenly" -- "None", "Fill", "SpaceEvenly", "SpaceAround", "SpaceBetween"
self.RespectContainerSize = true-- If elements should shrink to fit the container
self.FillDirection = self.contraintObject:GetAttribute("FillDirection") or "Vertical" -- "Horizontal", "Vertical"
self.SortOrder = self.contraintObject:GetAttribute("SortOrder") or "ChildOrder" -- "Name", "LayoutOrder", "ChildOrder"
self.ContainerSize = self.contraintObject.Size * ONLY_Y -- Get only the Obejcts Y, additionaly change this later so I can make horizontal lists
self.Elements = self.contraintObject:GetChildren()
self.ContentSize = Vector3.zero
for _, Element in self.Elements do --Get the size of all Elements
self.ContentSize *= (Element.Size * ONLY_Y)
end
self.FreeSpace = self.ContainerSize - self.ContentSize
-- Space Evenly
self.NumberOfGaps = #self.Elements + 1
self.GapSize = self.FreeSpace / self.NumberOfGaps
self:Initialize()
return self
end
function ListContraint:Initialize(): ()
local nextElement: BasePart = self.contraintObject
for index, Element in self.Elements do
if index == 1 then
Element.Position = (nextElement.Position + Vector3.new(0,nextElement.Size.Y /2 - Element.Size.Y /2,0)) -self.GapSize
else
Element.Position = (nextElement.Position + Vector3.new(0,-nextElement.Size.Y /2 - Element.Size.Y /2,0)) -self.GapSize
end
nextElement = Element
end
self:Destroy()
end
function ListContraint:Destroy(): ()
setmetatable(self, nil)
table.clear(self)
end
return ListContraint
also if you would like to debug in studio use this script to initialize the tags (works for server and client).
Script
local CollectionService = game:GetService(“CollectionService”)
function InitializeComponents(tag)
local component = script.Parent:FindFirstChild(tag)
if component then
for _, instance in ipairs(CollectionService:GetTagged(tag)) do
require(component).new(instance)
end
CollectionService:GetInstanceAddedSignal(tag):Connect(function(newInstance)
require(component).new(newInstance)
end)
end
end
for _, tag in ipairs(CollectionService:GetAllTags()) do
InitializeComponents(tag)
end
More things to note!
I wasn’t able to find the calculatuions used for Flex-Constraints in CSS, so I asked AI! Therfore, I urge you to correct any mistakes with the calculations of the gap size. Also I realized while writing this code that I should have used CFrame’s instead of Position or Vector3’s for the calculation. Let me know what you think, also I can’t come up with a better name for this post so I hope it will do.
[/quote]
I am creating a script that positions the child instances of the parent that is tagged in a list formation. You can think of the UI List Layout constraint where the UI elements are able to be formated from top to bottom (or left to right), but instead the container or the “constraints size” would be the parent and the elements are the children of the parent.
I am creating this for a menu gui and need the ui elements to be in-order because I plan on expanding the ui and would like to just add buttons guilt-free without spending time tweeking positions to fit the camera.
also, yes I aware that 3d ui frameworks exist but I have reasons I don’t want to use this.
The Problem:
I am struggling with apling the gap-size calculation to position the children of the parent to fit the proper constraint. Additionally, I would like to calculate the X or Z for horizontal lists but Im not sure how to implement that because of rotation and I don’t have the greatest grasp of CFrames besides basic calculations.
[/details]
The Script
type self = {
contraintObject: BasePart;
ConstraintFlex: string;
RespectContainerSize: boolean;
FillDirection: string;
SortOrder: string;
ContainerSize: Vector3;
Elements: {[number]: BasePart};
ContentSize: Vector3;
FreeSpace: Vector3;
NumberOfGaps: number;
GapSize: Vector3;
Initilize: () -> ();
Destroy: () -> ();
} -- Added for readablity not for strict mode
local ONLY_Y = Vector3.new(0,1,0)
local ListContraint = {}
function ListContraint.new(model): self
local self = setmetatable({
contraintObject = model
} :: self, {__index = ListContraint})
self.ConstraintFlex = self.contraintObject:GetAttribute("ConstraintFlex") or "SpaceEvenly" -- "None", "Fill", "SpaceEvenly", "SpaceAround", "SpaceBetween"
self.RespectContainerSize = true-- If elements should shrink to fit the container
self.FillDirection = self.contraintObject:GetAttribute("FillDirection") or "Vertical" -- "Horizontal", "Vertical"
self.SortOrder = self.contraintObject:GetAttribute("SortOrder") or "ChildOrder" -- "Name", "LayoutOrder", "ChildOrder"
self.ContainerSize = self.contraintObject.Size * ONLY_Y -- Get only the Obejcts Y, additionaly change this later so I can make horizontal lists
self.Elements = self.contraintObject:GetChildren()
self.ContentSize = Vector3.zero
for _, Element in self.Elements do --Get the size of all Elements
self.ContentSize *= (Element.Size * ONLY_Y)
end
self.FreeSpace = self.ContainerSize - self.ContentSize
-- Space Evenly
self.NumberOfGaps = #self.Elements + 1
self.GapSize = self.FreeSpace / self.NumberOfGaps
self:Initialize()
return self
end
function ListContraint:Initialize(): ()
local nextElement: BasePart = self.contraintObject
for index, Element in self.Elements do
if index == 1 then
Element.Position = (nextElement.Position + Vector3.new(0,nextElement.Size.Y /2 - Element.Size.Y /2,0)) -self.GapSize
else
Element.Position = (nextElement.Position + Vector3.new(0,-nextElement.Size.Y /2 - Element.Size.Y /2,0)) -self.GapSize
end
nextElement = Element
end
self:Destroy()
end
function ListContraint:Destroy(): ()
setmetatable(self, nil)
table.clear(self)
end
return ListContraint
also if you would like to debug in studio use this script to initialize the tags (works for server and client).
Script
local CollectionService = game:GetService(“CollectionService”)
function InitializeComponents(tag)
local component = script.Parent:FindFirstChild(tag)
if component then
for _, instance in ipairs(CollectionService:GetTagged(tag)) do
require(component).new(instance)
end
CollectionService:GetInstanceAddedSignal(tag):Connect(function(newInstance)
require(component).new(newInstance)
end)
end
end
for _, tag in ipairs(CollectionService:GetAllTags()) do
InitializeComponents(tag)
end
More things to note!
I wasn’t able to find the calculatuions used for Flex-Constraints in CSS, so I asked AI! Therfore, I urge you to correct any mistakes with the calculations of the gap size. Also I realized while writing this code that I should have used CFrame’s instead of Position or Vector3’s for the calculation. Let me know what you think, also I can’t come up with a better name for this post so I hope it will do.

