Placing System Help

				local RaycastParam = RaycastParams.new()
				RaycastParam.IgnoreWater = true
				RaycastParam.FilterType = Enum.RaycastFilterType.Exclude
				RaycastParam.FilterDescendantsInstances = {player.Character, PreviewProp}

				local MouseRay = mouse.UnitRay
				local RayResult = workspace:Raycast(MouseRay.Origin, MouseRay.Direction.unit * maxPlaceDistance, RaycastParam)

				if RayResult then
					local ObjectPosition = RayResult.Position + (RayResult.Normal * (PreviewProp.PrimaryPart.Size / 2))
					local ObjectCFrame = CFrame.new(ObjectPosition.X, HeightingAmount + ObjectPosition.Y, ObjectPosition.Z)
					local ObjectAngles = CFrame.Angles(0, math.rad(RotatingAmount), 0)

					PreviewProp:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
				end

We have a wall i already placed, and a preview model (a block) that im about to place.

When my mouse hovers over ANY side of the wall, the block gets positioned there properly.
But what i want to achieve is the same but i always want the X side of the block being the one attached to the wall/touching it.

if you dont get what i mean:
The block will always touch the wall on it’s X side, but always on a different side of the wall depending where my mouse is

Here is a image, where i place a wall (the transparent part is the body and the red part is the MainPart/PrimaryPart of the preview model), so on the left side you can see how it is right now, it just places randomly. But i want it to be like on the right side, that always the main part / that side of the model is faced towards the part your mouse is pointing at then while its there you can rotate it however you want (i already have the rotation)

im bad at explaining so sorry if its confusing

3 Likes

I was making a placement system very very recently and I found some great math that helped with this situation, I made a function that takes in a part and return a cframe and size and these 2 are then used, here are the important parts in it:

local up = Vector3.new(0, 1, 0)
local back = -Vector3.FromNormalId(normal)

local dot = back:Dot(Vector3.new(0, 1, 0))
local axis = math.abs(dot) == 1 and Vector3.new(-dot, 0, 0) or up

local right = CFrame.fromAxisAngle(axis, math.pi / 2) * back
local top = back:Cross(right).Unit

local cframe = canvas:GetPivot() * CFrame.fromMatrix(-back * canvasSize / 2, right, top, back)
local size = Vector2.new((canvasSize * right).Magnitude, (canvasSize * top).Magnitude)

return cframe, size -- end of the first function here

and the other function had this:

local cframe, size = self:CalculateCanvas(canvas) -- the function above

local _, rotation, _ = self.Rotation:ToEulerAnglesXYZ()

local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * self.Model.hitbox.Size
modelSize = Vector3.new(math.abs(modelSize.X), math.abs(modelSize.Y), math.abs(modelSize.Z))

local lpos = cframe:PointToObjectSpace(position)
local size2 = (size - Vector2.new(modelSize.X, modelSize.Z)) / 2
local x = math.clamp(lpos.X, -size2.X, size2.X)
local y = math.clamp(lpos.Y, -size2.Y, size2.Y)

if GRID_SIZE > 0 then
    x = math.sign(x) * ((math.abs(x) - math.abs(x) % GRID_SIZE) + (size2.X % GRID_SIZE))
    y = math.sign(y) * ((math.abs(y) - math.abs(y) % GRID_SIZE) + (size2.Y % GRID_SIZE))
end

return cframe * CFrame.new(x, y, -modelSize.Y/2) * self.Rotation

I can’t find the original post which provided this but great thanks to that guy, he really saved me (and you) a lot of work! U will obviously need to edit models and self.Rotation to match you’re code but this is the general idea.

2 Likes

i dont quite know how i would apply this to my placing system, i dont have grids or anything

1 Like

If you don’t have grids then remove these lines:

if GRID_SIZE > 0 then
    x = math.sign(x) * ((math.abs(x) - math.abs(x) % GRID_SIZE) + (size2.X % GRID_SIZE))
    y = math.sign(y) * ((math.abs(y) - math.abs(y) % GRID_SIZE) + (size2.Y % GRID_SIZE))
end

The canvas should be the wall/roof/floor and normal should be the normal of the raycast.

2 Likes

what about these

local _, rotation, _ = self.Rotation:ToEulerAnglesXYZ()
self.Model.hitbox.Size
local lpos = cframe:PointToObjectSpace(position) --the position

1 Like

This is for rotation of your model, if you don’t have the rotation than just remove it. lpos one is needed tho.

1 Like

unable to cast vector3 to token error

local back = -Vector3.FromNormalId(normal) --the normal errors

could you try to insert the functions into my code?

1 Like

u can just use raycast.Normal instead of that line.

local back = -raycastResult.Normal

thanks for actively replying and helping

local canvas = mouse.Target
local position = canvas.Position
local cframe, size = CalculateCanvas(RayResult.Normal, canvas)

local _, rotation, _ = canvas:ToEulerAnglesXYZ() – Keeps erroring

1 Like

It’s what I’m here for :).

I told you that you won’t need this so just completely remove it.

				local RaycastParam = RaycastParams.new()
				RaycastParam.IgnoreWater = true
				RaycastParam.FilterType = Enum.RaycastFilterType.Exclude
				RaycastParam.FilterDescendantsInstances = {player.Character, PreviewProp}

				local MouseRay = mouse.UnitRay
				local RayResult = workspace:Raycast(MouseRay.Origin, MouseRay.Direction.unit * maxPlaceDistance, RaycastParam)
				
				local function CalculateCanvas(normal, canvas)
					local canvasSize = canvas.Size
					local up = Vector3.new(0, 1, 0)
					local back = -RayResult.Normal

					local dot = back:Dot(Vector3.new(0, 1, 0))
					local axis = math.abs(dot) == 1 and Vector3.new(-dot, 0, 0) or up

					local right = CFrame.fromAxisAngle(axis, math.pi / 2) * back
					local top = back:Cross(right).Unit

					local cframe = canvas:GetPivot() * CFrame.fromMatrix(-back * canvasSize / 2, right, top, back)
					local size = Vector2.new((canvasSize * right).Magnitude, (canvasSize * top).Magnitude)

					return cframe, size
				end
				
				if RayResult then
					local ObjectPosition = RayResult.Position + (RayResult.Normal * (PreviewProp.PrimaryPart.Size / 2))
					local ObjectCFrame = CFrame.new(ObjectPosition.X, HeightingAmount + ObjectPosition.Y, ObjectPosition.Z)
					local ObjectAngles = CFrame.Angles(0, math.rad(RotatingAmount), 0)

					CanBePlaced = true

					--PreviewProp:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
					local function get()
						local canvas = mouse.Target
						local position = canvas.Position
						local cframe, size = CalculateCanvas(RayResult.Normal, canvas)

						local modelSize = canvas.Size

						local lpos = cframe:PointToObjectSpace(position)
						local size2 = (size - Vector2.new(modelSize.X, modelSize.Z)) / 2
						local x = math.clamp(lpos.X, -size2.X, size2.X)
						local y = math.clamp(lpos.Y, -size2.Y, size2.Y)


						--return cframe * CFrame.new(x, y, -modelSize.Y/2) * canvas.Rotation
						PreviewProp:SetPrimaryPartCFrame(x, y, -modelSize.Y/2)
					end

				end

this is what i came up with so far it doesnt seem to work

I don’t see where you called get(), and what exactly isn’t working?

PreviewProp:SetPrimaryPartCFrame(x, y, -modelSize.Y/2) --Unable to cast double to CoordinateFrame

local y = math.clamp(lpos.Y, -size2.Y, size2.Y) – invalid argument #3 to ‘clamp’ (max must be greater than or equal to min)

PreviewProp:PivotTo(cframe * CFrame.new(x, y, -modelSize.Y/2))

The size should be set to the model your placing and not the canvas.

2nd error still persists and would this work on terrain

local modelSize = PreviewProp.PrimaryPart.Size

Use PreviewProp:GetExtentsSize() instead, that will fix the error.

I never tried.

its being weird
and the 2nd error still persists
its behaving like theres a grid and not free place, i want that you can freely place anywhere with the rotate and height options i have, but that the main part always faces the surface your mouse is hovering over

That’s weird I really don’t know what could cause that problem… For the grid it shouldn’t do that and for the face thing, the code I provided places the front face on the canvas part so make sure ur front face is facing the correct direction.

what do you mean by “make sure ur front face is facing the correct direction”
regarding grids (i mean faces dont work too) its either the code that doesnt work or i did something wrong, could you try assembling it together?


you see how mine works i just want to fix the facing of the preview model