How do i achieve this?

I want to achieve this in my building system and how to do this like the block is not going outside?

do i need math here and modify the code?

Code:

-- // SERVICES \\ --
local Players = game:GetService("Players")
local repStorage = game:GetService("ReplicatedStorage")
local UIS = game:GetService("UserInputService")
local CAS = game:GetService("ContextActionService")
local runService = game:GetService("RunService")
local workspace = game.Workspace
-- // FOLDERS \\ --
local Events = repStorage:WaitForChild("Events")
local Models = repStorage:WaitForChild("Models")
-- // WORKSPACE FOLDERS
local IgnoredBlocks = workspace.Base:WaitForChild("IgnoredBlocks")
local PlacedBlocks = workspace.Base:WaitForChild("PlacedBlocks")
-- // EVENTS \\ --
local PlaceBlock = Events:WaitForChild("PlaceBlock")
local RemoveBlock = Events:WaitForChild("RemoveBlock")
local GetSelectedBlock = Events:WaitForChild("GetSelectedBlock")
-- // VARIABLES \\ --
local SelectionBox = repStorage:WaitForChild("RemoveBox")
local ignoreList = {IgnoredBlocks}
local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()
local target
local pos
local norm
local isEnabled = false
local canPlace = false
local rotation = 0
local selectedBlock
local modeldata = nil
local selectedpos = 1
local mode = "build"
local MAX_PLACEMENT_DISTANCE = 360
local RemoveBox = RemoveBlock:Clone()
local fakeBlock
-- // MAIN \\ --
Mouse.TargetFilter = IgnoredBlocks
Mouse.Icon = "rbxasset://textures/ArrowCursor.png"
function doFakeMouseStuff()
	local mouseRay = Mouse.UnitRay
	local newRay = Ray.new(mouseRay.Origin, mouseRay.Direction.unit * MAX_PLACEMENT_DISTANCE)
	target, pos, norm = workspace:FindPartOnRayWithIgnoreList(newRay, ignoreList)
end

function getblockPos(vec, block)
	local posx = math.floor(vec.X / block.PrimaryPart.Size.X + .5) * block.PrimaryPart.Size.X 
	local posy = math.floor(vec.Y / block.PrimaryPart.Size.Y + .5) * block.PrimaryPart.Size.Y
	local posz = math.floor(vec.Z / block.PrimaryPart.Size.Z + .5) * block.PrimaryPart.Size.Z
	return Vector3.new(posx, posy, posz)
end

function placeBlock(actionName, inputState, inputObj)
	if inputState == Enum.UserInputState.Begin then
		if isEnabled == true then
			if mode == "build" and canPlace == true then -- if the mode is build mode
				PlaceBlock:FireServer(selectedBlock, fakeBlock.PrimaryPart.CFrame) -- fires the event to place a block
			elseif mode == "remove" then -- if the mode is remove mode
				if Mouse.Target ~= workspace and Mouse.Target ~= "Base" and Mouse.Target ~= nil then -- detects if the Target is nil
					if Mouse.Target.Parent:IsA("Model") then -- detects if the Target instance is a Model
						RemoveBlock:FireServer(Mouse.Target.Parent) -- fires the event and remove the block
					end
				end
			end
		end
	end
end

function enablePlacing(actionName, inputState, inputObj)
	if inputState == Enum.UserInputState.Begin then
		if isEnabled == true then
			isEnabled = false
			Mouse.Icon = "rbxasset://textures/ArrowCursor.png"
		else
			isEnabled = true
			if mode == "build" then
				Mouse.Icon = "rbxassetid://66887745"
			else
				Mouse.Icon = "rbxassetid://72539748" 
			end
		end
	end
end

function changeMode(actionName, inputState, inputObj)
	if inputState == Enum.UserInputState.Begin then
		if isEnabled == true then
			if mode == "build" then
				mode = "remove"
				Mouse.Icon = "rbxassetid://72539748"
			else
				mode = "build"
				Mouse.Icon = "rbxassetid://66887745"
			end
		end
	end
end

function openBlockMenu(actionName, inputState, inputObj)
	if inputState == Enum.UserInputState.Begin then
		if isEnabled == true and mode == "build" and modeldata ~= nil then
			selectedBlock = Models:WaitForChild(modeldata[selectedpos].Name)
			fakeBlock:Destroy()
			fakeBlock = selectedBlock:Clone()
			if selectedpos == #modeldata then
				selectedpos = 1
			else
				selectedpos = selectedpos + 1
			end
		end
	end
end

function rotateBlock(actionName, inputState, inputObj)
	if inputState == Enum.UserInputState.Begin then
		if fakeBlock.Parent ~= nil then -- detects if the hoverblock is nil
			fakeBlock:SetPrimaryPartCFrame(fakeBlock.PrimaryPart.CFrame * CFrame.Angles(math.rad(fakeBlock.PrimaryPart.Orientation.X), math.rad(rotation + 90), math.rad(fakeBlock.PrimaryPart.Orientation.Z))) -- rotates the block
		end
	end
end

CAS:BindAction("changeMode", changeMode, false, Enum.KeyCode.X)
CAS:BindAction("rotateBlock", rotateBlock, false, Enum.KeyCode.R)
CAS:BindAction("enablePlacing", enablePlacing, false, Enum.KeyCode.E)
CAS:BindAction("openBlockMenu", openBlockMenu, false, Enum.KeyCode.Q)
CAS:BindAction("placeBlock", placeBlock, false, Enum.UserInputType.MouseButton1)

GetSelectedBlock.Event:Connect(function(block)
	if fakeBlock ~= nil then
		fakeBlock:Destroy()
	end
	selectedBlock = Models:WaitForChild(block)
	fakeBlock = selectedBlock:Clone()
end)
GetSelectedBlock:Fire("Block")
modeldata = {}
for i, v in pairs(Models:GetChildren()) do
	table.insert(modeldata, v)
end

runService.RenderStepped:Connect(function()
	for i, plr in pairs(Players:GetPlayers()) do
		table.insert(ignoreList, plr.Character)
	end
	doFakeMouseStuff()
	if isEnabled == true then
		if fakeBlock.Parent == nil then
			fakeBlock = selectedBlock:Clone()
			fakeBlock.Parent = IgnoredBlocks
		end
		if mode == "build" then
			RemoveBox:Destroy()
			if fakeBlock.Parent == nil then
				fakeBlock = selectedBlock:Clone()
				fakeBlock.Parent = IgnoredBlocks
			end
			if fakeBlock and pos and norm then
				for i,v in pairs(fakeBlock:GetChildren()) do
					v.CanCollide = false
					v.Transparency = .6
					if i > 1 then
						fakeBlock.PrimaryPart.Transparency = 1
					end
					if Mouse.Target ~= nil then
						v.Color = Color3.fromRGB(9, 137, 207)
						canPlace = true
					else
						v.Color = Color3.fromRGB(170, 0, 0)
						canPlace = false
					end
				end
				doFakeMouseStuff()
				local endPos = getblockPos(
					pos + (	
						norm * (fakeBlock.PrimaryPart.Size * .25)
					), 
					fakeBlock
				)
				fakeBlock:SetPrimaryPartCFrame(
					CFrame.new(endPos) * CFrame.Angles(
						math.rad(fakeBlock.PrimaryPart.Orientation.X), 
						math.rad(fakeBlock.PrimaryPart.Orientation.Y), 
						math.rad(fakeBlock.PrimaryPart.Orientation.Z)
					)
				)
			end
		elseif mode == "remove" then
			fakeBlock:Destroy()
			if RemoveBox.Parent == nil then
				RemoveBox = SelectionBox:Clone()
				RemoveBox.Parent = IgnoredBlocks
			end
			if Mouse.Target ~= game.Workspace.Base and Mouse.Target ~= nil then
				if Mouse.Target.Parent:IsA("Model") and Mouse.Target.Parent.PrimaryPart ~= nil and Mouse.Target.Parent:FindFirstChild("Placer") then
					local bbOrientation, bbSize = Mouse.Target.Parent:GetBoundingBox()
					RemoveBox:SetPrimaryPartCFrame(Mouse.Target.Parent.PrimaryPart.CFrame)
					RemoveBox.PrimaryPart.Size = Vector3.new(bbSize.X + .001, bbSize.Y + .001, bbSize.Z + .001)
				else
					RemoveBox:Destroy()
				end
			else
				RemoveBox:Destroy()
			end
		end
	else
		if selectedBlock ~= nil then
			fakeBlock:Destroy()
			RemoveBox:Destroy()
		end
	end
	if fakeBlock.Parent ~= nil or RemoveBox.Parent ~= nil then
		if Player.Character.Humanoid.Health < 1 then
			game:GetService("Debris"):AddItem(fakeBlock and RemoveBox, 0)
			isEnabled = false
		end
	end
end)
1 Like

yes you need math so that if the mouse position is out of bounds with the area then it would not follow the mouse

the worst possible way to do this is to detect wether the mouse is currently targetting the part or not

but what math formula to achieve that? (im bad at math)

Get the region3 of the part, if it’s not in region3 cannot place

The script you provided uses ray casts to check if the mouse is over the platform.
If it is it creates a fake block on the client which then it calculates where it should sit based on the mouse hit position.

local endPos = getblockPos(
					pos + (	
						norm * (fakeBlock.PrimaryPart.Size * .25)
					), 
					fakeBlock
				)
				fakeBlock:SetPrimaryPartCFrame(
					CFrame.new(endPos) * CFrame.Angles(
						math.rad(fakeBlock.PrimaryPart.Orientation.X), 
						math.rad(fakeBlock.PrimaryPart.Orientation.Y), 
						math.rad(fakeBlock.PrimaryPart.Orientation.Z)
					)
				)```
Edit: it also uses the surface normal to align the block with the surface of the platform

what do you mean?
(30 chars why you do this!???)

you could use Magnitude

30char

but where to put?

spoiler alert 30 chars

just check if their distance is like 50 studs away or any studs you would like if its more than that distance i would not allow them placing the blocks

What do you not understand specifically?
You cast a ray from the players mouse and check if the hit object is the platform, if it is you create a fake box which you then, using the position of the hit, the raycast returned you align with the building platform. After you align the position you then align the part with the surface of the platform.

2 Likes

You could read EgoMooses’s article in which you got that example video from.

1 Like

Magnitude, I would not recommend as it is the centre of the part that is the position so it would be hard to do, as corners etc will be different

1 Like

any other techniques?
(30 chars why you do this!!!)

Either raycasting or region3 if your not sure on how they work check the wiki or youtube