Tackling a Placement system

Hey DevForum! Recently I have decided upon creating a Placement system. I started working and with my basic knowledge, created a grid system on studs of three. I have a few problems that I have little knowledge on how to combat.

How do I create a bounding box?
I don’t want users to be able to place outside the limits of the grid. I have my eyes currently on Region3 but this might be a problem.

How do I set the grid relative to the base and not the world?
My first solution was to use modulus to grab the remainder, subtract it from the position and use that to grab the offset from the world grid.
(9.2,0,9.2) → 0.2 offset.

How would I make collisions?
I have absolutely no idea. I thought of using :GetTouchingParts() but it would confuse my system because it would assume its overlapping even though it may just be colliding with its surface.

Final question, to align an item to a grid when positioning it, I use modulus to create a 3x3 alignment but when using mod on something that isn’t a multiple of 6, it goes out 1.5 studs from all directions (Position of a brick 3x3x3 would be 1.5,1.5,1.5). I currently tackle this by offsetting 1.5 if it isn’t a multiple of 6 but I feel like this offset makes the placement look glitched to the player because it isn’t right under the mouse.

Help is appreeciated!
Sorry if I wrote too much.

A lot of these issues are fixed by simply having a grid – which you already do, so what comes next is easier than it would usually be otherwise.


The easiest way I can think of for making a bounding box is to simply set some maximum and minimum position values. You could do this visually, using a part, or could do it somewhere in your code (e.g. you could give each area a ‘settings’ module that stores a Vector3 of the ‘size’ of the bounding box). Region3 works but doesn’t have any (usable) support for rotation – you’d be better off just using CFrames (more on that in your next question).

To actually check where something is within your bounding box, just compare the position values of the model you are placing to the maximum and minimum positions. An easy example of this is:

local WithinX = Position.X >= Minimum.X and Position.X <= Maximum.X
local WithinY = Position.Y >= Minimum.Y and Position.Y <= Maximum.Y
local WithinZ = Position.Z >= Minimum.Z and Position.Z <= Maximum.Z

if WithinX and WithinY and WithinZ then
    -- place object
end

Next up is local space. Using local space will make your job much easier for everything else you are doing (notably the above), so this is a good thing to think about. The built-in, proper way to handle local and world spaces is to use CFrames.

CFrame1:ToObjectSpace(CFrame2) will put CFrame2 into CFrame1’s space (if it was equal to CFrame2, then it would be at the local position ‘0, 0, 0’). CFrame2:ToWorldSpace(CFrame1) will reverse this and put CFrame1 into the world space (that is, if CFrame2 is the local space it was in, otherwise the returned CFrame won’t be accurate). You can see these documented further in the DevHub: https://www.robloxdev.com/api-reference/datatype/CFrame

This will help you with collisions, your bounding box, and aligning the object to the grid.

Here’s a brief example:

local BoundingPart = workspace.BoundingPart.CFrane
local BoundingPartCFrame = BoundingPart.CFrame

-- Convert into local space
local PlacementModelCFrame = MouseCFrame:ToObjectSpace(BoundingPartCFrame)

-- Round
-- Check bounding box
-- Check collisions

-- Return to world space
PlacementModelCFrame = PlacementModelCFrame:ToWorldSpace(BoundingPartCFrame)

-- Place item

As for collisions, you can handle that in a variety of ways. If you wanted, you could do it all purely in data and just store the positions and sizes (in grid areas, not in studs) of all the objects you have and check all of those when checking collisions. This is the best option compared to the others, IMO.

Alternatively, you could try comparing some sort of root ‘main’ part in each of the models. This is what would work best with collisions, however like you said they may confuse the system and Roblox doesn’t really have any built- in ways for us to handle specifically colliding (not touching) parts.

This is an example of the former option:

local PlacedModels = {
    1 = {
        1 = {
            1 = ModelA; -- Stored at X1, Y1, Z1
            2 = ModelB;
        }
    }
}

local function CheckNoCollisions(X, Y, Z)
    -- Beware this will error if the locations you go through don't exist in the table already
    -- so make sure to handle cases where X, Y or Z doesn't exist
    return PlacedModels[X][Y][Z] == nil
end

This example uses a three-dimensional array, however if you wanted you could turn it into a dictionary instead and do something like PlaceModels[Vector3.new(1, 1, 1)].


Finally, I’ve found the best method to align something to a grid is as simple as rounding the position values. If you’re in local space, rounding should be simple and you won’t need to handle things such as weird offsets and rotations.

Here’s an example:

local function RoundNumber(Number, To)
    return math.floor((Number / To) + 0.5) * To
end

local function RoundPosition(Position)
    local X, Y, Z = Position.X, Position.Y, Position.Z
    X = Round(X, 3)
    Y = Round(Y, 3)
    Z = Round(Z, 3)
    return Vector3.new(X, Y, Z)
end

Hopefully this is helpful :slight_smile:

15 Likes

Adding to this, you can also use

RoundPosition(Position,Increment,Offset)

and

local offsetx = (Offset and Offset.X or 0)
X = Round(Position.X+offsetx,Increment)-offsetx
3 Likes

Hmm. Is that for the random offset? I changed my code to go into local world and it works but it still seems that anything that isn’t a multiple of three but is a multiple of 6 is divided in half. (Position of 3x3x3 brick is 1.5,1.5,1.5, But 6,6,6 is 3,3,3 which is alligned on the grid.) I might be doing something wrong but if I decide to offset this by 1.5, this could mess up how the grid handles collisions.

Nevermind, I fixed it.