Creating A Furniture Placement System

I’ve been reordering my code to implement this. It’s slightly better(when you place again its smooth). However, none of the binds work in the second time and I’ve been messing with it to get the binds to work. So far, here’s the current code

onSwitch(nil, Enum.UserInputState.Begin, nil)

	game:GetService("ContextActionService"):BindAction("switch", onSwitch, false, Enum.KeyCode.E)
game:GetService("ContextActionService"):BindAction("rotate", onRotate, false, Enum.KeyCode.R)
game:GetService("ContextActionService"):BindAction("place", onPlace, false, Enum.UserInputType.MouseButton1)
game:GetService("ContextActionService"):BindAction("clear", onClear, false, Enum.KeyCode.C)
game:GetService("ContextActionService"):BindAction("save", onSave, false, Enum.KeyCode.F)



player.playerData.Editing:GetPropertyChangedSignal("Value"):Connect(function()
if player.playerData.Editing.Value == true then
	if model then
		if model.Parent == nil then
			model.Parent = placement.CanvasObjects
		end
	end
elseif player.playerData.Editing.Value == false then
		game:GetService("ContextActionService"):UnbindAction("switch", onSwitch, false, Enum.KeyCode.E)
	game:GetService("ContextActionService"):UnbindAction("rotate", onRotate, false, Enum.KeyCode.R)
	game:GetService("ContextActionService"):UnbindAction("place", onPlace, false, Enum.UserInputType.MouseButton1)
	game:GetService("ContextActionService"):UnbindAction("clear", onClear, false, Enum.KeyCode.C)
	game:GetService("ContextActionService"):UnbindAction("save", onSave, false, Enum.KeyCode.F)
end
end)

 game:GetService("RunService").RenderStepped:Connect(function(dt)
	local cf = placement:CalcPlacementCFrame(model, mouse.Hit.p, rotation)
	model:SetPrimaryPartCFrame(cf)
end)
2 Likes

Wow, thanks so much! I definitely will use this in the future.

What about mobile support, I have a lot of players who are mostly on phone and placing on phone is really terrible, can you show us also how it make mobile support? That would be great, but overall tutorial is pretty well made and explained.

2 Likes

Oh that is because you are unbinding your keys but not binding them again, u can move your bindActions inside the PropertyChanged function (when Editing is true), so it will work only when activated and not from the beginning. Also it will work in the second time because it will be bind again. I hope u find this helpful. :smiley:

3 Likes

I am unsure how I would get started at making a window-feature. Exactly how bloxburg does it. To make a model intersect a wall. I can’t wrap my head around it.

3 Likes

I’m guessing in its simplest form a model is positioned on a given plane, your wall for example, and then parts are then redrawn around the window model based on the given position and sizes of the model. I think roller coast tycoon also does something similar to this with its terrain to generate minimal amounts of parts but i’m not 100%. You also might be able to accomplish this with the real-time CSG but I haven’t really used this so i don’t want to comment too much on it. You could probably try both these options though.

Real-time CSG: New Roblox In-Game CSG API is now available

1 Like

Currently Im working on a system similar to this - bassically when you are on the client you make all the windows not transparent, and on the server just negate the wall with the hit box.

1 Like

I am assuming you are using CSG to accomplish the ‘negate’? Or are you using a method to redraw the triangles of the wall?

1 Like

CSG Yes - else I wouldn’t have to make the windows and such untransparent!

1 Like

I am worried about the performance and unreliability of this. Say for example if you were to let the player preview how it would look and the player adjusts the window’s position, you would have to constantly negate and restore the wall’s previous state.

1 Like

I would say this is separate from a placement system.

Others have asked this question on the forums though. I’ve given my personal answer, but as others have stated csg is also an option.

5 Likes

Sorry for the bump,

But If I want to implement this so it enables by clicking on a button I simply cant get it to work.
It dosen’t show the ghost model and in general it dosen’t work.

I do not like it being enabled all the time.

2 Likes

Extremely Detailed. This helped me so much! Thank you!

4 Likes

Hello, OP. I think the tutorial is a great idea and it’s going to help a lot of people - however, I do think that the tutorial could be more in-depth. A lot of people fail to understand what CFrame actually is and how using it can be manipulated for things such as this very placement system. Additionally, things like matrices may be unheard of to some and therefore this can be quite confusing as to why you do .fromMatrix() and even those who do know what matrices are (like myself), I fail to understand how this works. Perhaps some reference images and an explanation of the outputs of each variable you make for calculating the constraints would be nice. Other than that, great tutorial!

2 Likes

There used to be a lot of information on those types of subjects on the wiki, but they were removed and not replaced when moving to the devhub.

Regarding concepts such as constraints I do have to put a limit on how in depth I explain certain things or else I’d never finish writing anything. Like in the case of a constraint you’re clamping a 2D point into a rectangular 2d box, that’s it.

1 Like

Yes but that’s easier said than done. It’s very easy for someone as experienced as yourself to say “that’s it”. But the people reading this tutorial are most likely not experienced enough to say that; otherwise reviewing the tutorial. Simply saying “that’s it” doesn’t help anyone move forward. Yes, you’re obviously constraining it to the boundaries of your canvas but how, not what. For example, CFrame.fromMatrix() was explained to me like this

CFrame.fromMatrix(
    Vector3.new(x,y,z),
    Vector3.new(r00,r10,r20),
    Vector3.new(r01,r11,r21),
    Vector3.new(r02,r12,r22)
)
-- is equivalent to
CFrame.new(
    x,y,z,
    r00,r01,r02,
    r10,r11,r21,
    r20,r21,r22
)

Unsurprisingly, I was still left clueless as to how this works. Adding a little bit more explanation to actually help people understand the things they’re writing rather than just copying from a website is going to help them much more than simply telling them to repeat after you. Another thing I noticed is that your tutorial is hard to follow and keep up with. A LocalScript can not access ServerScriptService yet you never specified this to clarify this for amateur scripters and rather than explaining this concept or giving them an alternative solution (like putting it in ReplicatedStorage), you left them in the dark. Another example is the very first script you wrote.

-- Server Script

-- the module script from below
local placementClass = require(game:GetService("ReplicatedStorage"):WaitForChild("Placement"))
local placementObjects = {}

local remotes = game:GetService("ReplicatedStorage"):WaitForChild("Remotes")

-- creates the server twin, stores in a table and returns the CanvasObjects property
function remotes.InitPlacement.OnServerInvoke(player, canvasPart)
	placementObjects[player] = placementClass.new(canvasPart)
	return placementObjects[player].CanvasObjects
end

-- finds the server twin and calls a method on it
-- note: b/c we aren't using the standard method syntax we must manually put in the self argument
remotes.InvokePlacement.OnServerEvent:Connect(function(player, func, ...)
	if (placementObjects[player]) then
		placementClass[func](placementObjects[player], ...)
	end
end)

-- Class (Module Script)

local isServer = game:GetService("RunService"):IsServer()

local Placement = {}
Placement.__index = Placement

function Placement.new(canvasPart)
	local self = setmetatable({}, Placement)

	-- the part we are placing models on
	self.CanvasPart = canvasPart
	
	-- custom logic depending on if the sevrer or not
	if (isServer) then
		-- create a folder we'll place the objects in
		self.CanvasObjects = Instance.new("Folder")
		self.CanvasObjects.Name = "CanvasObjects"
		self.CanvasObjects.Parent = canvasPart
	else
		-- initiate the twin on the server
		self.CanvasObjects = initPlacement:InvokeServer(canvasPart)
	end
	
	-- we'll talk about these properties later in the post
	self.Surface = Enum.NormalId.Top
	self.GridUnit = 1

	return self
end

return Placement

A lot of this was left unclarified and was quite hard to follow with no clear, given instructions. No prior instructions were given; for example, you never said make a folder called remotes in ReplicatedStorage; you never specified whether these were to be RemoteEvents or RemoteFunctions. Yes, working in OOP environments is a great practise to try and teach people - however, leaving them in the dark only leads to a spiral of ever-increasing problems. Thank you for your contribution, regardless.

5 Likes

OP used their own unpaid time more than a year ago to create a Community Tutorial that many find interesting and helpful. It is what it is. OP is under no obligation to improve it.

You are suggesting ways this tutorial could be improved, particularly for readers who are not familiar with all the specific details involved.

There is no need to call out OP on what they did do, or what they did not do.

Instead, we can offer constructive criticism in the form:

“This tutorial could be improved by [adding this]”
“It would be helpful to clarify [some subject] by describing all the specific detailed steps involved”

Or reply to the thread simply stating:

“I found that […]”
“Where OP says […], you could [do this]”.

Consider writing your own version of this tutorial “Furniture Placement Detailed Steps in 2020” or “[Username] Explains More About Furniture Placement”.

As a devforum member, you’re a part of the community. It is more beneficial to think about adding to the community rather than calling out what it did not do.

7 Likes

What do you think I did? I simply pointed out areas of improvement. If you actually took the time to read it, as opposed to simply having a go back at me, you would see that I, myself, do not understand how this placement system works. I also thanked him, regardless of the clarity of the tutorial. If I could rewrite this myself and explain it better, I would have done so already but the reason I’m at this tutorial is because I don’t, funnily enough.

1 Like

Thank you for this amazing tutorial! It’s going to help me with future developments!

3 Likes

One of the things he tried to make the reader do is to make the reader go and branch out their knowledge by looking things up in other sources more in-depth. Also, pay heed to that there is a limit to how many characters (32k) he can write in his post.

Contradictory to what you said, I had a hard time understanding what errors you were pointing out. Please reference what he said with “>” or “quote=user” and then elaborate on why it is not sufficient.

If there were things you didn’t understand such as APIs or other CS terms such as OOP, you could’ve looked them up yourself :slight_smile: There are plenty of tutorials here on Roblox and you should really only dive into this tutorial once you have a good knowledge of callback functions and advanced CFrame. Ego and Axis have a lot of good articles on these things; a little of searching would’ve been plenty enough!

2 Likes