An Interior Building System Guide

My Story
I got into Roblox game development, specifically scripting about a month ago by the time this post is up. I’ve got a programming experience from my 10th-grade computer science class, so I didn’t know much to begin with. I’ve put in a few hours every day trying to understand more and more of lua and game development. If you’re wondering, the best resources for me were the DevForum, watching YouTube videos, and getting help in Roblox development discord servers. Since I found DevForum posts to be so useful, I’ve decided to turn what I learned into a mini-guide and for you to get an inside view of what was going through my head. I hope you find this thread helpful, thank you. :smile:

Overview

The goal of this placement system was to have an interior building system that supports different object categories. These categories are

  • Floor: Objects can only be placed on the ground (e.x. beds, lamps, tables, etc)
  • Wall: Objects can only be placed on a wall (e.x. windows, shelves, paintings, etc)
  • Ceiling: Objects can only be placed on the wall (e.x. ceiling lights, fans, etc)
  • Decorative: Objects can be placed on another object’s surface (e.x. a book on a table, a pillow on a bed, etc)

Secondary features:

  • Re- moving items (Moving the item after it was placed, to get it confused with removing)
  • Coloring items (& walls, ceilings, floors)
  • Copying items (& and their colors)
  • Deleting items (of course)

This was inspired by other great building systems such as Welcome to Bloxburg’s.

Warning:
This system is not the cleanest. I’m aware of problems it can cause with adding new features. However, its logic should be easy to understand and useful for new scripters. I’ll be redoing this system with an OOP vision after this post is up. Any pointers to what I can improve or change is appreciated.


Setting up items


Note: make sure all the parts are anchored.

  • ObjectType - A string value of what type it is.
  • isObject - This is useful for the secondary featuers to check if a player clicked on an object
  • Color3 - Storing the object’s color. This is useful for recoloring the object to red when the object’s can place is false
Alternative

Using a module script to make a table of all the properties would be a lot better especially if you want subcategories like stacking. (e.x. Book on a book, photo on a book, food on a plate, etc)

Organizing Objects
Picture2

Instead of having an ObjectType string value, we can simply organize the items into categories and their type be their parent’s name. However, this caused a problem with re- moving the object because it would no longer be in a folder.

Alternative

I could’ve created a folder for each player onPlayerJoin and create FLOOR, WALL, CEILING and DECORATIVE folders in them. (I went with the ObjectType route here)

Object Placement

  1. When a player selects an object, we need to create a “ghost clone” of that object to visualize where and if the player can place the object.

  2. If the player left-clicks and can place is true then fire a remote function to the server confirming its placement

  3. If the remote function returns true then delete the ghost object on the client

The Client
After setting up some items, we need to create a GUI and connect it to the objects in ReplicatedStorage.
image
All that’s needed is a button with an object value

local objectSelection = playerGui:WaitForChild("Objects"):WaitForChild("Frame"):GetChildren()

-- Main GUI Input
for _, button in pairs(objectSelection) do -- Loop through buttons
	if button:IsA("TextButton") then
		button.MouseButton1Click:Connect(function() -- If button is clicked
			if placing.Value == false and button:FindFirstChild("AssignedObject").Value then
				selectedObject = button:FindFirstChild("AssignedObject").Value
				objectType = (selectedObject.Parent.Name) -- ObjectType defines how the object's behaviour and where it can be placed
				
				replicatedStorage.Client.Bindables.PlaceGhost:Fire(selectedObject, objectType) -- Fire bindable event
				placing.Value = true
			end
		end)
	end
end

The selected object and object type is received by the main module script through the bindable event.

game:GetService("ReplicatedStorage").Client.Bindables.PlaceGhost.Event:Connect(function(GivenSelectedObject, GivenObjectType)
	-- Object
	local objectType = GivenObjectType
	local object = GivenSelectedObject:Clone() 
	object.Parent = workspace -- Object is now visible to client

Now the player can select an object, but we need to move it to the mouse’s position. If you’re new to scripting you’re probably tempted to use the Mouse API but I recommend learning raycasting as it allows us to do other things.

The other things
  • Getting the normal (surface) of the mouse’s position. This is used to make wall items face the right way.
  • The blacklist option allows for a table to be passed.
  • It makes you feel cooler, arguably the most important feature.

What’s great about the objectType we passed to the module script is now we have the ability to run different functions.

	local objectOptions = { -- Per object property
		["FLOOR"] = function()
			checkMouseSurface("FLOOR")
		end,
		
		["WALL"] = function()
			checkMouseSurface("WALL")
		end,
		
		["CEILING"] = function()
		end,
		
		["DECORATIVE"] = function()
		end,
	}

The checks to see if the player can place are too lengthy for me to explain in this thread. It’s mostly a bunch of if then, if then, if then, if thens. I’ve opensourced the place if you want to check it out and take a deeper look into it.

but here’s a quick summary:

	runServiceConnection = runService.RenderStepped:Connect(function() -- Run every frame
		objectOptions[objectType]() -- Run "custom" function or smthing
		
		moveToMouse() -- Move to mouse's position
		changeVisualColour() -- Change to red if cannot place, default colors if can place
		checkCanPlace() -- This function is the mastermind
	end)

Object canPlace checks showcase:

Now that the ghost objects are working, lets get placing!

-- Local script
	local serverPlaced
	userInputService.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 and placing.Value == true then -- Place object
			if canPlace == true and object ~= nil then
				--                                                                                 Object               Type                   Position              Check
				serverPlaced = replicatedStorage.Client.Remotes.PlaceObject:InvokeServer(GivenSelectedObject, GivenObjectType, object:GetPrimaryPartCFrame(), canPlace)
				if serverPlaced == true then
					-- do something
				end
			end

If all the checks return true and the player can place then we invoke the server with the object’s information

-- Server script
replicatedStorage.Client.Remotes.PlaceObject.OnServerInvoke = function(player, object, objectType, objectPosition, canPlace)
	if canPlace then
		local newObject = object:Clone()
		newObject.Parent = workspace.PlayerObjects:FindFirstChild(objectType)

		newObject:SetPrimaryPartCFrame(objectPosition)
		
		for _, child in pairs(newObject:GetChildren()) do -- Turn on object collision
			if child:IsA("BasePart") then
				child.CanCollide = true
			end
		end
		
		return true -- Has been placed
	end
end

& that’s how you get an object placement system to work! I’ll add an explanation for how the secondary features work (and problems I came across along the way) once I have the free time! Thank you for reading, I hope this helped someone.


Edit:
I’ve learned a lot about OOP in lua and how to apply it. You might see my new system in a future game :eyes: . I highly suggest checking out JonByte’s tutorial! Trust me, metatables are worth learning.

I promised to explain my process for the secondary features (coloring, deleting, cloning and re- moving) so here it is!

Coloring objects
I went for a design directly stolen inspired by Bloxburg, where you could color individual parts of an object.

The logic behind it is to loop through the children of the selected object, creating a new “visual” gui to display the selected color for that individual part.

One problem you might encounter is what if the player chooses to cancel? The colors must return to what they were and to solve that you must save the colors somewhere. In this case I added a Color3 value to each of the object’s children. (please don’t do this) Use module scripts :smiley: .

Upon confirming, the objects colors as well as the stored colors change to what was selected.

Moving and cloning
Going to skip these 2 features, even though they are available. The code isn’t great and it would be teaching some bad practices, haha.


I highly recommend learning about metatables and Object Oriented Programming if you’re looking to expand. Egomoose has a great tutorial for anybody that wants a cleaner advanced version.

Here’s the place if you want to check it out! :v:

37 Likes

Never knew you were a programmer too until today ;p
Great community resource by the way! I recommend you switch the category to #resources:community-resources
Great work!

1 Like

I clicked on this thinking “Oh placement” and was surprised that I saw iamAnix as the person who made it. Anyways thank you for this it was helpful.

1 Like

Good catch. This thread seems appropriate enough for the Resources category, so I’ve moved it over.

Cool Creations can be used to showcase or share resources that are not free or open sourced, while the Resources categories can be used for those kinds of resources or teaching material on significant topics with utility for many developers.

4 Likes

Thank you iamAnix this was very helpful!
I also like your vids

1 Like

That’s amazing, especially for only 1 month. Good job!

1 Like

this is a really good tutorial that will help a lot of people in creating their own building type game, thanks

2 Likes

Wow. Very good I can recommend some stuff to do to it, but it’s really really good.

Im also wondering when you’re going to post videos again

1 Like

@iamAnix : I have a quick suggestion. Since you can’t place a table on top of another table, then I would blacklist the table in the raycasting instead of making it go red. For example, placing two grass right next to each other in bloxburg, makes the other grass snap on top of the grass and say “Can’t place here”. Instead you can ignore that so it will go completely through it instead of going on top.
It does this in the sims 4

Hope this helps!

Edit: Otherwise, Great job!!

1 Like

Definitely agree with you on that.

This might be a bit off topic from your answer but… When it comes to designing building games, I believe minimizing “rules” such as collisions is a GREAT idea. This may sound extremely odd or lazy from a programming point of view but it allows the players to express more creativity in their builds && making the skill ceiling significantly higher, in turn a larger community can grow. That may be a big reason to why Bloxburg has kept it’s impressively large community alive. With game passes and
what seems like small updates lowering the rules on how you can build.

Thanks! :smiley:

3 Likes