I want to make a simple placement system but the CurrentBlock is shaking. I know the cause which is SnapOnTop function.
Code:
--!nonstrict
--Grandpa mod
--Placement stuff
--Error messages
local ERROR_MESSAGE_INVAILD_BLOCK = "Given block called %s is invaild (%s does not exist)."
local ERROR_MESSAGE_NO_ACTIVATION = "Forgot to run :Activate() thus can't place block or preview placing block."
local ERROR_MESSAGE_REINIT = "Cannot re-initialize singleton class 'PlacementSystem'"
--Numbers
local GRID_SIZE = 4
local LERP_SIZE = 0
--Strings
local CANCEL_KEY = "C"
--Services
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
--Instances
local Blocks = script.Parent:WaitForChild("Blocks")
local Camera = workspace.CurrentCamera
--Variables
local MousePosition
local Placing = false
local Deleting = false
local CurrentBlock
local function SnapToGrid(Number)
--Grid snapping
local x = (Number / GRID_SIZE) + 0.5
return math.floor(x) * GRID_SIZE
end
local function SnapOnTop(Y)
--Snap on top of parts
assert(CurrentBlock, ERROR_MESSAGE_NO_ACTIVATION)
local YPos = Y
if #CurrentBlock.PrimaryPart:GetTouchingParts() > 0 then
for index, part in pairs(CurrentBlock.PrimaryPart:GetTouchingParts()) do
YPos += (2 * part.Position.Y) --Same thing as (part.Position.Y + part.Position.Y) but shorter.
end
end
return YPos
end
local function GetMouseWorldPosition(MousePosition)
--Gets a Vector2 mouse position and spits out a Vector3 one.
--First we get a UnitRay using Camera:ViewportPointToRay.
--Then we raycast using the UnitRay and we return the raycast position
local UnitRay = Camera:ViewportPointToRay(MousePosition.X, MousePosition.Y + 36, 0)
local Raycast = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 500)
if Raycast then
return Raycast.Position
else
return Vector3.new(0, 0.5, 0)
end
end
local PlacementSystem = {}
local function KeyCancelBind(ActionName, InputState)
if ActionName ~= "CancelPlacement" then
return
end
if InputState == Enum.UserInputState.End then
PlacementSystem:Deactivate()
end
end
function PlacementSystem.Initialize(cancelKey, gridSize, lerpSize)
CANCEL_KEY = cancelKey
GRID_SIZE = gridSize
LERP_SIZE = lerpSize
PlacementSystem.Initialize = function() error(ERROR_MESSAGE_REINIT) end
ContextActionService:BindAction("CancelPlacement", KeyCancelBind, false, Enum.KeyCode[CANCEL_KEY])
end
function PlacementSystem:Activate(blockName)
--Turns on placing mode
local block = Blocks:FindFirstChild(blockName)
assert(block, ERROR_MESSAGE_INVAILD_BLOCK:format(blockName, blockName))
block = block:Clone()
Placing = true
CurrentBlock = block
block.Parent = workspace:WaitForChild("PlacementBlocks")
end
function PlacementSystem:Deactivate()
--Turns off placing mode and destroys the block
--ONLY SHOULD BE USED WHEN CANCELING A PLACEMENT!!!!
if Deleting then
return
end
local block = CurrentBlock --Simpler and less error-prone
assert(block, ERROR_MESSAGE_NO_ACTIVATION)
Deleting = true
block.PrimaryPart.Delete:Play()
block.PrimaryPart.Delete.Ended:Wait()
Placing = false
CurrentBlock = nil
block:Destroy()
Deleting = false
end
local function UpdatePlacing(dt) --A better method name
--Update the position of the CurrentBlock
if not Placing or not CurrentBlock then --[[If we're placing but CurrentBlock is nil, surely something is wrong?]]
return
end
local MousePosition = UserInputService:GetMouseLocation()
MousePosition = GetMouseWorldPosition(MousePosition)
local PosX, PosY, PosZ = SnapToGrid(MousePosition.X), SnapOnTop(0.5), SnapToGrid(MousePosition.Z)
CurrentBlock:PivotTo(CurrentBlock.PrimaryPart.CFrame:Lerp(CFrame.new(PosX, PosY, PosZ), LERP_SIZE))
end
RunService:BindToRenderStep("Placing", Enum.RenderPriority.Input.Value, UpdatePlacing)
return PlacementSystem
robloxapp-20221230-1513184.wmv (708.5 KB)