just a bit of info, you can lock it to a grid by doing
vector3.new(math.ceil(position.x/3), math.ceil(position.y/3), math.ceil(position.z/3))*3
to get the block normal via a camera raycast you can use this function
(mouse.hit.position is good and all but this gives a concrete ignore list)
local UIS = game:GetService("UserInputService")
function RaycastToScreenPos(screenpos, ignorelist, ignoretype)
local camray = workspace.CurrentCamera:ScreenPointToRay(screenpos.x, screenpos.y)
local CastInfo = RaycastParams.new()
CastInfo.FilterType = ignoretype or Enum.RaycastFilterType.Blacklist
--blacklist ignores all in your ignore list, whitelist only goes for the ones in the list
CastInfo.FilterDescendantsInstances = ignorelist or {}
local NewRayResult = workspace:Raycast(camray.Position, camray.Direction * 100000, CastInfo)
--long ray
return NewRayResult
end
local result = RaycastToScreenPos(UIS:GetMouseLocation(), {--[[stuff to ignore goes in here]] }, Enum.RaycastFilterType.Blacklist)
result.Normal = direction of the face you hit (normal)
result.Instance = the part you hit
result.Position = the position of your hit
result.Material = the material of what you hit
putting this all together you can make a placement system with this function in a serverscript
local TemplateContainer = folder where you keep your block templates
local BlocksFolder = folder where blocks are put in Workspace
local PlaceBlockRemote = place block remote
function PlaceBlock(plr, templatename, Normal, Position, hitinst)
local Block = TemplateContainer:FindFirstChild(templatename):Clone()
Block.Position = hitinst.Position+Normal.Unit*3
Block.Parent = BlocksFolder
end
PlaceBlockRemote.OnServerEvent:Connect(function(plr, templatename, Normal, Pos, Hitinst)
--put your checks here
PlaceBlock(plr, templatename, Normal, Pos, Hitinst)
end)
above is easily hackable by firing a remote, add some server checks (ie if player is in range, if player has enough blocks in slot, etc)
and you need the placement system for client
local UIS = game:GetService("UserInputService")
local PlacementRemote = remote for placement
function RaycastToScreenPos(screenpos, ignorelist, ignoretype)
local camray = workspace.CurrentCamera:ScreenPointToRay(screenpos.x, screenpos.y)
local CastInfo = RaycastParams.new()
CastInfo.FilterType = ignoretype or Enum.RaycastFilterType.Blacklist
--blacklist ignores all in your ignore list, whitelist only goes for the ones in the list
CastInfo.FilterDescendantsInstances = ignorelist or {}
local NewRayResult = workspace:Raycast(camray.Position, camray.Direction * 100000, CastInfo)
--long ray
return NewRayResult
end
function PlaceBlock()
local result = RaycastToScreenPos(UIS:GetMouseLocation(), {--[[stuff to ignore goes in here]] }, Enum.RaycastFilterType.Blacklist)
--im not going to add ghost blocks because i have worked on this for a bit longer than i would have liked to
PlacementRemote:FireServer("TemplateName(set this to template name)", result.Normal, result.Position, result.Instance)
end
--bind above to mouse button click or smth
this is just concept code and im not 100% sure it will work. hopefully it will tho