Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

How do I make my building system snap on part surfaces and on a grid, like the classic Stamper Tool?

Asked by
Y_VRN 246 Moderation Voter
4 years ago

|If possible please write easy answers for me to understand when it comes to CFrame math! I'm still figuring out how it works. Thanks in advance.|

My goal here is to make a building system where I could snap objects to surfaces of other objects while still following a grid (like the Stamper Tool or something else similar.)

Currently I've only managed to make grid snapping (I deleted that part of the code.) Surface snap only works when I remove the grid snapping functionality and all the workarounds I could think of don't work correctly (or I'm probably just tired when I work with them).

What else can I do to make this happen?:

local PlayerCont = game:GetService("Players")
local Mouse = PlayerCont.LocalPlayer:GetMouse()
local PlacementCursor = Instance.new("Part", workspace) -- Since FilteringEnabled is true, we can
-- make the player's cursor invisible to others.
local SnapByStuds = 4

local States = { -- Non functional
    OFF = 0,
    BUILD = 1,
    EDIT = 2,
    COLOR = 3,
    WIRE = 4,
    CLIPBOARD = 5,
    REMOVE = 6
}

local RotationStates -- does nothing for now

-- Initialization

PlacementCursor.Transparency = 0.8 -- 0.8 for now
PlacementCursor.Anchored = true
PlacementCursor.CanCollide = false
PlacementCursor.CastShadow = false
PlacementCursor.Size = Vector3.new(4, 4, 4)
PlacementCursor.Material = "Neon"

Mouse.TargetFilter = PlacementCursor

local cmath = {}

function cmath.even(x, returnNum, custCheck)
    local custN = custCheck or 1
    if returnNum then
        return (x % custN)
    end
    return (x % custN) == 0
end

function cmath.nearest(x, roundnum)
    local rounding = roundnum or 1
    if typeof(x) == "Vector3" then
        local X, Y, Z = x.X, x.Y, x.Z
        local rX, rY, rZ = cmath.even(X, true, rounding) / rounding, cmath.even(Y, true, rounding) / rounding, cmath.even(Z, true, rounding) / rounding

        if rX > 0.5 then
            X = math.ceil(X / rounding) * rounding + (SnapByStuds/2)
        else
            X = math.floor(X / rounding) * rounding + (SnapByStuds/2)
        end

        if rY > 0.5 then
            Y = math.ceil(Y / rounding) * rounding + (SnapByStuds/2)
        else
            Y = math.floor(Y / rounding) * rounding + (SnapByStuds/2)
        end

        if rZ > 0.5 then
            Z = math.ceil(Z / rounding) * rounding + (SnapByStuds/2)
        else
            Z = math.floor(Z / rounding) * rounding + (SnapByStuds/2)
        end

        return Vector3.new(X, Y, Z)
    else
        -- numbers only not used for now
    end
end

-- ===========================================================
-- =========================================================== ACTUAL FUNCTIONS
-- ===========================================================

function DisplayNewPosition() -- Something to improve on.
    local Normal = Vector3.FromNormalId(Mouse.TargetSurface)
    local Position = Mouse.Hit.p
    local CursorCFrame = Mouse.Hit
    local Anchor = Mouse.Target

    if CursorCFrame ~= nil and Anchor ~= nil then
        local ObjectHit = Anchor.CFrame -- Anchor to center of object + rotation
        local ObjectSpace = Anchor.CFrame:ToObjectSpace(CFrame.new(Position)) * CFrame.Angles(0, 0, 0)
        local ObjectRotation = Anchor.CFrame - Anchor.CFrame.Position
        local RotatedNormal = (ObjectRotation * Normal) * (PlacementCursor.Size / 2)
        --print(RotatedNormal * PlacementCursor.Size)
        local FinalFormulation = (ObjectHit * ObjectSpace) + RotatedNormal

        PlacementCursor.CFrame = FinalFormulation
    end
end

function RequestAddition()
    -- nothing yet
end

Mouse.Move:Connect(DisplayNewPosition)

Thanks (again) in advance!

Answer this question