Arrange models neatly, side by side, left to right

Say theres a script that follows this same process over and over

  1. taking a random model with parts of various heights, widths and lengths
  2. cloning it
  3. and neatly placing it next to the previous model, no gaps or floating parts (Like how a bookshelf is arranged)

(example)
image

How can I achieve the third step? I already have something setup for the first two steps, but my attempt at the third one failed miserably, seen below.

image

All the books are at a different height, theres z-fighting everywhere, it’s all a jumbled mess.
I’m sure theres some complicated thing involving math to achieve this. I can reveal some of what I tried to do, but I would still like to keep my code to myself.

4 Likes

Bump :sob:

If it helps to know, what I’m doing right now is multiplying the previous book’s CFrame with it’s thickness and then using that to set the new book’s CFrame with :SetPrimaryPartCFrame().

For example, heres just a snippet of my code

local catalog = {
	--    OBJECT,            THICKNESS 
	[1] = {Folder.BookType1, 0.3}, 
    [2] = {Folder.BookType2, 0.2}
{

local function createBook(b, newpos)
	local clone = b:Clone()
	clone.PrimaryPart = clone:FindFirstChild("Main")
	clone:SetPrimaryPartCFrame(newpos)
	clone.Parent = bfolder
	return clone.PrimaryPart.CFrame
end

local randomBookstats = catalog[math.random(1, #catalog)]
local randomBook = randomBookstats[1]
local bookThickness = randomBookstats[2]

newpos = createBook(randomBook, newpos) * CFrame.new(bookThickness, 0, 0)
-- And then this is looped of course

You will need an accumulator for the total thickness of the books so far.

The first book will start at an x of 0.
Place the book at an x of accumulator + (thickness / 2) and then add thickness to accumulator. The /2 is needed because parts are positioned by their centers.
Repeat until you can’t fit another book (until accumulator + thickness < width_of_shelf).

The y of each book can just be half its height (again, because parts are positioned by their centers).

FWIW, your original code would’ve worked fine if it accounted for the thickness of both the current book and the previous book.
For example, there are some books of identical thickness in your second image which have no problems because it just worked out right.

edit: The start position (or rather, start CFrame) should be on one of the bottom back inner corners of the shelf, facing forward out of the bookshelf.
Then you just createBook(randomBook, startcf * CFrame.new(x, y, z))

1 Like

Sorry, I’m a little lost (and desperate) so I’m gonna go ahead and give you the place file.

Then could you explain what I need again? If that isn’t too much to ask?

All in all, I did already try something similar to the accumulator thing and then tried it your way which didn’t seem to solve anything.

Unfortunately, I can’t use the place file because I can’t run Roblox or Studio on this computer, so the best I can do is edit the script in the original post.
As implied, I have not tested this code. It should work after you fill in Folder, bfolder and the arguments to populateShelf() at the bottom.

local Folder
local bfolder

local catalog = Folder:GetChildren()

local function populateShelf(shelfCFrame, shelfWidth)
	local x = 0
	
	while true do
		-- create and setup book
		local book = catalog[math.random(1, #catalog)]:Clone()
		book.PrimaryPart = clone.Main -- no need for FindFirstChild here methinks. If there is no Main, then that's good, you *should* get an error
		local size = book.PrimaryPart.Size
		
		-- if the book won't fit, then stop
		if x + size.X > shelfWidth then return end
		
		book:SetPrimaryPartCFrame(shelfCFrame * CFrame.new(
			x + size.X / 2,
			size.Y / 2,
			size.Z / 2
		))
		
		x = x + size.X
		
		book.Parent = bfolder
	end
end

populateShelf(CFrame.new(), 10)

As you can see, there is an x variable that starts at 0 and the thickness of each book is added to it.

1 Like

Thanks, this function helped a lot in fixing it. Everything fits and is level. One last thing though, what’s causing this to happen?

local function populateShelf(shelfCFrame, shelfWidth)
	local x = 0

	while true do
		local book = catalog[math.random(1, #catalog)]:Clone()
		book.PrimaryPart = book.Main 
		local size = book.PrimaryPart.Size

		if x + size.X > shelfWidth then return end

		book:SetPrimaryPartCFrame(shelfCFrame * CFrame.new(
			x + size.X / 2,
			size.Y / 2,
			size.Z / 2
		))

		x = (round(x + size.X, 2))
		print(x)
		book.Parent = bfolder
	end
end

-------------------------------------------------

local x = -(start.Size.X/2) + 0.9

for i = 1, columns do
	local y = (start.Size.Y/2)

	for i = 1, rows do
		local shelfcf = start.CFrame * CFrame.new(x, y, -.5)
		populateShelf(shelfcf, sps)
		y = y + 2.2
	end
	
	x = x + 15
end

The last time I checked, 2.2 studs was just enough to ascend one shelf. I’m probably stupid and forgetting to do something you already mentioned.

Not at all.
I think you just have to increase that 2.2 to the distance between two shelves.
So if one shelf’s Y is at 2 and another’s is at 4.5, then that would be 2.5, for example. I don’t have the measurements you do, though.

I would also suggest running this function for each of these spaces between shelves, as right now the books are clipping into these pillar things.

1 Like

Yeah, 2.2 is the distance between two shelves. Even if I go 0.1 up it’ll leave gaps

2.2 seems to be precise for the first two shelves before it starts falling short

Then I can’t imagine what could be causing this. Maybe the shelves aren’t all 2.2 studs apart?

1 Like

My bad. All the shelves werent the same distance apart so I fixed that.
Thanks again