Tips for making a building system

I want to make a simple building system, sort of like how it is done in bloxburg, adopt me, strucid, etc, where you simply just preview something and click to build. This system will be adaptable to many different game types so that it can be re-used.

All of the builds used in the build system will be provided when the module is used, and we dont have to worry about.

I already know how to code this system, I’m just asking for any tips you guys have that I should implement or watch out for in my modular building system!

1 Like
  • Are you using a grid?
  • Are you using a graph?
  • Have you considered auto-tiling?
  • Have you considered using occupancy and connectivity?
  • Make sure you set up the way you want to implement new assets early. If you have to change it later, you may need to go through all of your work and update it.
  • Have you considered how you’ll design the client-server architecture?
  • You’ll probably have different categories of buildable objects, with different rules. It’d be useful to write those down.
1 Like

I have considered most of the things you have said except I’m not exactly sure what you mean when you say

  • Have you considered auto-tiling?
  • Have you considered using occupancy and connectivity?
1 Like

Auto-tiling is a way to automatically change tiles depending on the tiles around them. You’d make a tile which is split up into segments, and depending on where a tile is place, you’d change the segment of the tile you get to see.

You could use it do to things like having cabinets fill half a tile, without having to place awkward half-tile objects.

As for occupancy and connectivity, that’s a way to handle where objects are, and where they can go. If every cell has 6 places for an object (each direction), then occupancy could be 6 bits. If there’s an object in a specific direction of the cell, set the corresponding bit to 1. If there is none, set it to 0.

Then, if you’re trying to place something from a neighbouring cell, which needs to snap to whatever you just put down in the other cell (Think of a door and a doorframe), you can use some bitwise operations to figure out if there’s an object in the neighbouring cell’s face which touches the one you’re trying to place it in.

1 Like

I was thinking of just doing this adaptable and straightforward approach:

  • Check if the object is within bounds
  • Check if the object being placed will clip into any other objects.

I would prevent exploitation because these checks would be run on the server. I would validate the position they give me by ensuring the Y axis is correct and checking its bounds. Once I have verified the position, I can check if that position is already occupied by a 3D object.

When I want to save, I just loop through all the objects in the build folder and record their positions and object types.

When I want to load, I just loop through the record and place the object, moving it toward the recorded position.

So you’re not going to have object snapping?

If you mean object snapping in terms of a grid, I would just use a system that places the object based on the grid size.

Lets say the grid size is 5

local gridSize = 5

Now lets say that the object they are moving is a couch

local object = workspace.Couch

I would make it so the couches position snaps to grid using this math:

local mousePos = game.Players.LocalPlayer.Mouse.Hit
local mouseX = math.floor(mousePos.X / gridSize) * gridSize
local mouseZ = math.floor(mousePos.Z / gridSize) * gridSize
object.Position = vector3.new(mouseX, 0, mouseZ)

This program would snap the objects position to the grid size of 5 and snap the Y position to 0 without relying on previous objects or a complex system. It would be implemented in a while loop so the position is constantly updated, and broken when they exit the build mode.

That seems reasonable. Although Mouse.Hit is deprecated

I just looked at the documentation, and I don’t believe it is deprecated

Well, Mouse itself in it’s entirety is deprecated, I think. There’s a better solution now.

I dont think mouse is deprecated either:

Also thank you for your detailed advice earlier. I’m certainly going to implement them in my modular, adaptable building system.

Are there any other suggestions you have to make my modular building system more adaptable?

“Mouse has been superseded by …”. It’s available, but using it is discouraged. That’s what deprecated means!

Here’s my raycastModule, it works with an input from UserInputService. So you can just do this on the client:

UserInputService.InputBegan:Connect(function(input)
  if input.UserInputType == Enum.MouseMovement then
    local ray = RaycastModule.rayCastToInput(workspace.CurrentCamera.CFrame.Position,input)
    if ray then if ray.Instance then print ray.Instance.Name end end
  end
end)

local RaycastModule = {}

-- Constants
local RAYCAST_DISTANCE = 1000

local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {workspace.CurrentCamera}

local function rayCast(origin, direction,passed_Params)
	local raycastParam = passed_Params or Params
	local worldRay = Ray.new(origin, direction.Unit * RAYCAST_DISTANCE)
	local raycastResult = workspace:Raycast(worldRay.Origin, worldRay.Direction * RAYCAST_DISTANCE, raycastParam)

	-- Optional beam visualization
	if raycastResult then
		local size = Vector3.new(0.2, 0.2, (worldRay.Origin - raycastResult.Position).Magnitude-2)
		local cframe = CFrame.lookAt(worldRay.Origin, raycastResult.Position) * CFrame.new(0, 0, -(size.Z/2)-2)
	else
		print("no hit")
	end

	return raycastResult
end

function RaycastModule.rayCastInDirection(origin, direction,passed_Params)
	return rayCast(origin, direction,passed_Params)
end

function RaycastModule.rayCastToPoint(origin, goalPos,passed_Params)
	local direction = goalPos - origin
	return rayCast(origin, direction,passed_Params)
end

function RaycastModule.rayCastToInput(origin, inputObject,passed_Params)
	local Camera = workspace.CurrentCamera
	local worldRay = Camera:ScreenPointToRay(inputObject.Position.X, inputObject.Position.Y)
	return rayCast(origin, worldRay.Direction,passed_Params)
end

return RaycastModule

what are some benefits of this module, particularly for the build system I’m making. How is mouse.hit not enough?

Well, if you’re using mouse you can ignore it. But if you’re using UIS, this plugs in and works.
But using a raycast instead of mouse.Hit will always give you more capabilities to filter objects in and out of it.