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.
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
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
-
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.
-
If the player left-clicks and can place is true then fire a remote function to the server confirming its placement
-
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.
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 . 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 .
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!