
Key Required VerifiedUniversal

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local HttpService = game:GetService("HttpService")
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local playerModule = require(player:WaitForChild("PlayerScripts"):WaitForChild("PlayerModule"))
local controls = playerModule:GetControls()
-- // SETTINGS & STATE //
local currentSpeed, currentSens, currentFOV = 1.5, 0.4, 70
local slideEnabled, isFreecamEnabled, collisionEnabled = false, false, false
local is180Mode, isDpadMode, rotationLocked = false, false, false
local slipPower, targetFPS, fpsCapEnabled = 1.0, 60, false
local CAM_RADIUS = 1.5
-- Playback State
local isRecording, isPlaying = false, false
local playbackSpeed, isReverse = 1.0, false
local isLooping = false
local playbackTime, totalDuration = 0, 0
local recordedPath = {}
local recStartTime = 0
-- Movement State
local cameraCFrame = CFrame.new()
local yaw, pitch = 0, 0
local velocity = Vector3.new(0,0,0)
local joystickTouch = nil
local gamepadRotation = Vector2.zero
local dpadMove = Vector3.zero
local verticalMove = 0
local dpadState = {Up=false, Down=false, Left=false, Right=false}
local isRightMouseDown = false
-- File System State
local SAVES_FILE = "UltraFreecam_Saves.json"
local savedRecordings = {}
-- // IO HELPER FUNCTIONS //
local function serializeCFrame(cf)
local x, y, z = cf.Position.X, cf.Position.Y, cf.Position.Z
local rx, ry, rz = cf:ToEulerAnglesXYZ()
return {x, y, z, rx, ry, rz}
end
local function deserializeCFrame(data)
return CFrame.new(data[1], data[2], data[3]) * CFrame.Angles(data[4], data[5], data[6])
end
local function saveToDisk()
if not writefile then return end
local dataToSave = {}
for _, rec in ipairs(savedRecordings) do
local cleanPath = {}
for _, node in ipairs(rec.path) do
table.insert(cleanPath, {t = node.t, cf = serializeCFrame(node.cf)})
end
table.insert(dataToSave, {name = rec.name, date = rec.date, dur = rec.dur, path = cleanPath})
end
pcall(function() writefile(SAVES_FILE, HttpService:JSONEncode(dataToSave)) end)
end
local function loadFromDisk()
if not readfile or not isfile or not isfile(SAVES_FILE) then return end
local success, result = pcall(function() return HttpService:JSONDecode(readfile(SAVES_FILE)) end)
if success and result then
savedRecordings = {}
for _, rec in ipairs(result) do
local realPath = {}
for _, node in ipairs(rec.path) do
table.insert(realPath, {t = node.t, cf = deserializeCFrame(node.cf)})
end
table.insert(savedRecordings, {name = rec.name, date = rec.date, dur = rec.dur, path = realPath})
end
end
end
-- // UI SETUP //
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "UltraFreecam_V4_2"
screenGui.ResetOnSpawn = false
screenGui.IgnoreGuiInset = true
screenGui.DisplayOrder = 10
screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
screenGui.Parent = player:WaitForChild("PlayerGui")
loadFromDisk()
local mainFrame = Instance.new("Frame")
mainFrame.Name = "MainFrame"
mainFrame.Size = UDim2.new(0, 160, 0, 450)
mainFrame.Position = UDim2.new(1, -180, 0.5, -225)
mainFrame.BackgroundColor3 = Color3.fromRGB(15, 15, 15)
mainFrame.BackgroundTransparency = 0.1
mainFrame.Active = true -- Important for stopping rotation
mainFrame.Parent = screenGui
Instance.new("UICorner", mainFrame)
local dragHandle = Instance.new("TextButton")
dragHandle.Size = UDim2.new(1, 0, 0, 25)
dragHandle.BackgroundColor3 = Color3.fromRGB(45, 45, 45)
dragHandle.Text = ":: V4.2 MENU ::"
dragHandle.TextColor3 = Color3.new(0.8,0.8,0.8)
dragHandle.Font = Enum.Font.GothamBold
dragHandle.TextSize = 12
dragHandle.Parent = mainFrame
Instance.new("UICorner", dragHandle)
local scrollFrame = Instance.new("ScrollingFrame")
scrollFrame.Size = UDim2.new(1, -10, 1, -40)
scrollFrame.Position = UDim2.new(0, 5, 0, 30)
scrollFrame.BackgroundTransparency = 1
scrollFrame.ScrollBarThickness = 4
scrollFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y
scrollFrame.CanvasSize = UDim2.new(0,0,0,0)
scrollFrame.Active = true
scrollFrame.Parent = mainFrame
local uiList = Instance.new("UIListLayout")
uiList.Parent = scrollFrame
uiList.Padding = UDim.new(0, 5)
uiList.HorizontalAlignment = Enum.HorizontalAlignment.Center
uiList.SortOrder = Enum.SortOrder.LayoutOrder
local function createBtn(text, color, layoutOrder)
local b = Instance.new("TextButton")
b.Size = UDim2.new(0.95, 0, 0, 28)
b.BackgroundColor3 = color or Color3.fromRGB(35, 35, 35)
b.TextColor3 = Color3.new(1,1,1)
b.Text = text
b.Font = Enum.Font.GothamBold
b.TextSize = 12
b.LayoutOrder = layoutOrder or 0
b.Parent = scrollFrame
Instance.new("UICorner", b)
return b
end
local function createBox(defText, layoutOrder)
local b = Instance.new("TextBox")
b.Size = UDim2.new(0.95, 0, 0, 24)
b.BackgroundColor3 = Color3.new(0,0,0)
b.BackgroundTransparency = 0.5
b.TextColor3 = Color3.new(1,1,1)
b.Text = defText
b.Font = Enum.Font.Gotham
b.TextSize = 12
b.LayoutOrder = layoutOrder or 0
b.Parent = scrollFrame
Instance.new("UICorner", b)
return b
end
local function createHeader(text, layoutOrder)
local l = Instance.new("TextLabel")
l.Size = UDim2.new(1, 0, 0, 15)
l.BackgroundTransparency = 1
l.TextColor3 = Color3.fromRGB(150, 150, 150)
l.Text = text
l.Font = Enum.Font.GothamBold
l.TextSize = 10
l.LayoutOrder = layoutOrder
l.Parent = scrollFrame
return l
end
local order = 0
local function inc() order = order + 1 return order end
createHeader("- CAMERA -", inc())
local toggleBtn = createBtn("CAM: OFF", Color3.fromRGB(180, 50, 50), inc())
local modeBtn = createBtn("180°: OFF", nil, inc())
local lockRotBtn = createBtn("ROT LOCK: OFF", nil, inc())
createHeader("- SETTINGS -", inc())
local speedIn = createBox("Speed: 1.5", inc())
local sensIn = createBox("Sens: 0.4", inc())
local fovIn = createBox("FOV: 70", inc())
local colTog = createBtn("COLLIDE: OFF", nil, inc())
createHeader("- MOVEMENT -", inc())
local slideBtn = createBtn("SLIDE: OFF", nil, inc())
local slipIn = createBox("Slip: 1.0", inc())
local dpadTog = createBtn("DPAD: OFF", Color3.fromRGB(150, 80, 0), inc())
createHeader("- RECORDER -", inc())
local recBtn = createBtn("RECORD", Color3.fromRGB(200, 50, 50), inc())
local playBtn = createBtn("PLAY", Color3.fromRGB(50, 150, 50), inc())
local loopBtn = createBtn("LOOP: OFF", nil, inc())
local revBtn = createBtn("REV: OFF", nil, inc())
local savesBtn = createBtn("SHOW SAVES", Color3.fromRGB(200, 120, 0), inc())
local pbSpeedIn = createBox("PB Speed: 1.0", inc())
local seekContainer = Instance.new("Frame")
seekContainer.Size = UDim2.new(0.95, 0, 0, 30)
seekContainer.BackgroundTransparency = 1
seekContainer.LayoutOrder = inc()
seekContainer.Parent = scrollFrame
local seekTimeLabel = Instance.new("TextLabel")
seekTimeLabel.Size = UDim2.new(1, 0, 0, 10)
seekTimeLabel.BackgroundTransparency = 1
seekTimeLabel.Text = "0.0s / 0.0s"
seekTimeLabel.TextColor3 = Color3.new(1,1,1)
seekTimeLabel.TextSize = 10
seekTimeLabel.Font = Enum.Font.Code
seekTimeLabel.Parent = seekContainer
local seekBarBg = Instance.new("Frame")
seekBarBg.Name = "SeekBg"
seekBarBg.Size = UDim2.new(1, 0, 0, 6)
seekBarBg.Position = UDim2.new(0, 0, 0, 18)
seekBarBg.BackgroundColor3 = Color3.fromRGB(60,60,60)
seekBarBg.Active = true -- Block rotation on seek bar
seekBarBg.Parent = seekContainer
Instance.new("UICorner", seekBarBg)
local seekFill = Instance.new("Frame")
seekFill.Size = UDim2.new(0, 0, 1, 0)
seekFill.BackgroundColor3 = Color3.fromRGB(200, 50, 50)
seekFill.Parent = seekBarBg
Instance.new("UICorner", seekFill)
local seekKnob = Instance.new("TextButton")
seekKnob.Size = UDim2.new(0, 14, 0, 14)
seekKnob.Position = UDim2.new(0, 0, 0.5, 0)
seekKnob.AnchorPoint = Vector2.new(0.5, 0.5)
seekKnob.Text = ""
seekKnob.Active = true
seekKnob.Parent = seekBarBg
Instance.new("UICorner", seekKnob)
createHeader("- MISC -", inc())
local fpsTog = createBtn("FPS CAP: OFF", nil, inc())
local fpsIn = createBox("FPS: 60", inc())
local tpBtn = createBtn("TELEPORT", Color3.fromRGB(80, 50, 150), inc())
local hideBtn = createBtn("HIDE MENU", Color3.fromRGB(30, 30, 30), inc())
local resizeHandle = Instance.new("ImageButton")
resizeHandle.Size = UDim2.new(0, 20, 0, 20)
resizeHandle.Position = UDim2.new(1, -20, 1, -20)
resizeHandle.BackgroundTransparency = 1
resizeHandle.Image = "rbxassetid://3577448866"
resizeHandle.ImageColor3 = Color3.fromRGB(100, 100, 100)
resizeHandle.Parent = mainFrame
local showBtn = Instance.new("TextButton")
showBtn.Size = UDim2.new(0, 70, 0, 30)
showBtn.Position = UDim2.new(1, -80, 0, 10)
showBtn.BackgroundColor3 = Color3.new(0,0,0)
showBtn.Text = "SHOW"
showBtn.TextColor3 = Color3.new(1,1,1)
showBtn.Visible = false
showBtn.Active = true
showBtn.Parent = screenGui
Instance.new("UICorner", showBtn)
-- // SAVES UI SYSTEM //
local savesFrame = Instance.new("Frame")
savesFrame.Name = "SavesFrame"
savesFrame.Size = UDim2.new(0, 250, 0, 300)
savesFrame.Position = UDim2.new(0.5, -125, 0.5, -150)
savesFrame.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
savesFrame.Visible = false
savesFrame.Active = true
savesFrame.Parent = screenGui
Instance.new("UICorner", savesFrame)
local savesHeader = Instance.new("TextButton")
savesHeader.Size = UDim2.new(1, 0, 0, 30)
savesHeader.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
savesHeader.Text = "RECORDINGS"
savesHeader.TextColor3 = Color3.new(1,1,1)
savesHeader.Font = Enum.Font.GothamBold
savesHeader.TextSize = 14
savesHeader.Parent = savesFrame
Instance.new("UICorner", savesHeader)
local closeSaves = Instance.new("TextButton")
closeSaves.Size = UDim2.new(0, 25, 0, 25)
closeSaves.Position = UDim2.new(1, -28, 0, 2)
closeSaves.BackgroundColor3 = Color3.fromRGB(180, 50, 50)
closeSaves.Text = "X"
closeSaves.TextColor3 = Color3.new(1,1,1)
closeSaves.Parent = savesHeader
Instance.new("UICorner", closeSaves)
local savesScroll = Instance.new("ScrollingFrame")
savesScroll.Size = UDim2.new(1, -10, 1, -40)
savesScroll.Position = UDim2.new(0, 5, 0, 35)
savesScroll.BackgroundTransparency = 1
savesScroll.ScrollBarThickness = 4
savesScroll.Active = true
savesScroll.Parent = savesFrame
local savesList = Instance.new("UIListLayout")
savesList.Parent = savesScroll
savesList.Padding = UDim.new(0, 4)
savesList.SortOrder = Enum.SortOrder.LayoutOrder
-- Confirm Delete UI
local confirmFrame = Instance.new("Frame")
confirmFrame.Size = UDim2.new(0, 200, 0, 100)
confirmFrame.Position = UDim2.new(0.5, -100, 0.5, -50)
confirmFrame.BackgroundColor3 = Color3.fromRGB(30,30,30)
confirmFrame.Visible = false
confirmFrame.ZIndex = 10
confirmFrame.Active = true
confirmFrame.Parent = screenGui
Instance.new("UICorner", confirmFrame)
local confirmText = Instance.new("TextLabel")
confirmText.Size = UDim2.new(1, 0, 0, 40)
confirmText.BackgroundTransparency = 1
confirmText.Text = "Delete this recording?"
confirmText.TextColor3 = Color3.new(1,1,1)
confirmText.Font = Enum.Font.GothamBold
confirmText.TextSize = 14
confirmText.ZIndex = 11
confirmText.Parent = confirmFrame
local yesBtn = Instance.new("TextButton")
yesBtn.Size = UDim2.new(0, 80, 0, 30)
yesBtn.Position = UDim2.new(0, 10, 0.6, 0)
yesBtn.BackgroundColor3 = Color3.fromRGB(150, 50, 50)
yesBtn.Text = "YES"
yesBtn.TextColor3 = Color3.new(1,1,1)
yesBtn.ZIndex = 11
yesBtn.Parent = confirmFrame
Instance.new("UICorner", yesBtn)
local noBtn = Instance.new("TextButton")
noBtn.Size = UDim2.new(0, 80, 0, 30)
noBtn.Position = UDim2.new(1, -90, 0.6, 0)
noBtn.BackgroundColor3 = Color3.fromRGB(50, 150, 50)
noBtn.Text = "NO"
noBtn.TextColor3 = Color3.new(1,1,1)
noBtn.ZIndex = 11
noBtn.Parent = confirmFrame
Instance.new("UICorner", noBtn)
local itemToDeleteIdx = nil
local function refreshSaves()
for _, v in pairs(savesScroll:GetChildren()) do if v:IsA("Frame") then v:Destroy() end end
for i, save in ipairs(savedRecordings) do
local row = Instance.new("Frame")
row.Size = UDim2.new(1, 0, 0, 40)
row.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
row.Parent = savesScroll
row.Active = true -- Block rotation
Instance.new("UICorner", row)
local nameBox = Instance.new("TextBox")
nameBox.Size = UDim2.new(0.5, 0, 0, 20)
nameBox.Position = UDim2.new(0, 5, 0, 5)
nameBox.BackgroundTransparency = 1
nameBox.Text = save.name
nameBox.TextColor3 = Color3.new(1,1,1)
nameBox.Font = Enum.Font.GothamBold
nameBox.TextXAlignment = Enum.TextXAlignment.Left
nameBox.TextSize = 12
nameBox.Parent = row
nameBox.FocusLost:Connect(function() save.name = nameBox.Text; saveToDisk() end)
local dateLbl = Instance.new("TextLabel")
dateLbl.Size = UDim2.new(0.5, 0, 0, 15)
dateLbl.Position = UDim2.new(0, 5, 0, 22)
dateLbl.BackgroundTransparency = 1
dateLbl.Text = save.date .. " ("..string.format("%.1fs", save.dur)..")"
dateLbl.TextColor3 = Color3.fromRGB(150,150,150)
dateLbl.Font = Enum.Font.Code
dateLbl.TextSize = 10
dateLbl.TextXAlignment = Enum.TextXAlignment.Left
dateLbl.Parent = row
local pBtn = Instance.new("TextButton")
pBtn.Size = UDim2.new(0, 30, 0, 30)
pBtn.Position = UDim2.new(1, -75, 0.5, -15)
pBtn.BackgroundColor3 = Color3.fromRGB(50, 150, 50)
pBtn.Text = "▶"
pBtn.TextColor3 = Color3.new(1,1,1)
pBtn.Parent = row
Instance.new("UICorner", pBtn)
local dBtn = Instance.new("TextButton")
dBtn.Size = UDim2.new(0, 30, 0, 30)
dBtn.Position = UDim2.new(1, -35, 0.5, -15)
dBtn.BackgroundColor3 = Color3.fromRGB(150, 50, 50)
dBtn.Text = "🗑"
dBtn.TextColor3 = Color3.new(1,1,1)
dBtn.Parent = row
Instance.new("UICorner", dBtn)
pBtn.MouseButton1Click:Connect(function()
recordedPath = save.path; totalDuration = save.dur; playbackTime = 0
isPlaying = true; isRecording = false; playBtn.Text = "PAUSE"
playBtn.BackgroundColor3 = Color3.fromRGB(100, 200, 100)
end)
dBtn.MouseButton1Click:Connect(function()
itemToDeleteIdx = i
confirmFrame.Visible = true
end)
end
savesScroll.CanvasSize = UDim2.new(0, 0, 0, #savedRecordings * 44)
end
yesBtn.MouseButton1Click:Connect(function()
if itemToDeleteIdx then
table.remove(savedRecordings, itemToDeleteIdx)
saveToDisk(); refreshSaves()
itemToDeleteIdx = nil
confirmFrame.Visible = false
end
end)
noBtn.MouseButton1Click:Connect(function()
itemToDeleteIdx = nil
confirmFrame.Visible = false
end)
-- // VERTICAL UI //
local vertFrame = Instance.new("Frame")
vertFrame.Size = UDim2.new(0, 50, 0, 110)
vertFrame.Position = UDim2.new(0, 10, 0.5, -55)
vertFrame.BackgroundTransparency = 1
vertFrame.Parent = screenGui
local function makeVertBtn(txt, pos, val)
local b = Instance.new("TextButton")
b.Size = UDim2.new(0, 40, 0, 40)
b.Position = pos
b.BackgroundColor3 = Color3.new(0,0,0)
b.BackgroundTransparency = 0.4
b.Text = txt; b.TextColor3 = Color3.new(1,1,1); b.TextSize = 20; b.Font = Enum.Font.GothamBold
b.Active = true
b.Parent = vertFrame
Instance.new("UICorner", b)
b.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then verticalMove = val; b.BackgroundTransparency = 0.1 end end)
b.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then if verticalMove == val then verticalMove = 0 end; b.BackgroundTransparency = 0.4 end end)
end
makeVertBtn("▲", UDim2.new(0,0,0,0), 1)
makeVertBtn("▼", UDim2.new(0,0,1,-40), -1)
-- // DPAD UI //
local dpadFrame = Instance.new("Frame")
dpadFrame.Size = UDim2.new(0, 180, 0, 180)
dpadFrame.Position = UDim2.new(0, 30, 1, -220)
dpadFrame.BackgroundTransparency = 1
dpadFrame.Visible = false
dpadFrame.Parent = screenGui
local function makeArrow(name, pos, rot)
local btn = Instance.new("ImageButton")
btn.Size = UDim2.new(0, 55, 0, 55); btn.Position = pos
btn.BackgroundColor3 = Color3.new(0,0,0); btn.BackgroundTransparency = 0.4
btn.Image = "rbxassetid://6031091004"; btn.Rotation = rot; btn.Active = true; btn.Parent = dpadFrame
Instance.new("UICorner", btn)
return btn
end
local btnU, btnD, btnL, btnR = makeArrow("U", UDim2.new(0.5, -27, 0, 0), 0), makeArrow("D", UDim2.new(0.5, -27, 1, -55), 180), makeArrow("L", UDim2.new(0, 0, 0.5, -27), -90), makeArrow("R", UDim2.new(1, -55, 0.5, -27), 90)
-- // LOGIC: FPS CAPPER //
task.spawn(function()
while true do
if fpsCapEnabled then
local t = os.clock()
RunService.Heartbeat:Wait()
while (os.clock() - t) < (1/targetFPS) do end
else
RunService.Heartbeat:Wait()
end
end
end)
-- // LOGIC: MOVEMENT & RECORDER //
local function applyRot(delta)
if rotationLocked then return end
if is180Mode then
yaw = yaw - math.rad(delta.X)
pitch = math.clamp(pitch - math.rad(delta.Y), math.rad(-89), math.rad(89))
cameraCFrame = CFrame.new(cameraCFrame.Position) * CFrame.Angles(0, yaw, 0) * CFrame.Angles(pitch, 0, 0)
else
cameraCFrame = cameraCFrame * CFrame.Angles(0, -math.rad(delta.X), 0) * CFrame.Angles(-math.rad(delta.Y), 0, 0)
end
end
local function getFrameAtTime(t)
if #recordedPath < 2 then return cameraCFrame end
t = math.clamp(t, 0, totalDuration)
local idx1, idx2 = 1, #recordedPath
for i = 1, #recordedPath - 1 do if recordedPath[i+1].t >= t then idx1 = i; idx2 = i + 1; break end end
local f1, f2 = recordedPath[idx1], recordedPath[idx2]
local duration = f2.t - f1.t
if duration <= 0 then return f1.cf end
return f1.cf:Lerp(f2.cf, (t - f1.t) / duration)
end
RunService:BindToRenderStep("FreecamCore", Enum.RenderPriority.Camera.Value + 101, function(dt)
if isRecording then
local t = os.clock() - recStartTime
table.insert(recordedPath, {cf = camera.CFrame, t = t})
totalDuration = t
seekTimeLabel.Text = string.format("%.1fs / %.1fs", t, t)
elseif isPlaying then
if totalDuration > 0 then
playbackTime = playbackTime + (dt * playbackSpeed * (isReverse and -1 or 1))
if isLooping then
if playbackTime > totalDuration then playbackTime = 0 elseif playbackTime < 0 then playbackTime = totalDuration end
else
if playbackTime > totalDuration then playbackTime = totalDuration; isPlaying = false; playBtn.Text = "PLAY" end
if playbackTime < 0 then playbackTime = 0; isPlaying = false; playBtn.Text = "PLAY" end
end
camera.CFrame = getFrameAtTime(playbackTime); cameraCFrame = camera.CFrame
local percent = math.clamp(playbackTime / totalDuration, 0, 1)
seekKnob.Position = UDim2.new(percent, 0, 0.5, 0); seekFill.Size = UDim2.new(percent, 0, 1, 0)
seekTimeLabel.Text = string.format("%.1fs / %.1fs", playbackTime, totalDuration)
end
camera.CameraType = Enum.CameraType.Scriptable; return
end
if not isFreecamEnabled then return end
camera.CameraType = Enum.CameraType.Scriptable
local mv = isDpadMode and dpadMove or controls:GetMoveVector()
local yForce = verticalMove
if UserInputService:IsKeyDown(Enum.KeyCode.Q) then yForce = 1 elseif UserInputService:IsKeyDown(Enum.KeyCode.E) then yForce = -1 end
if UserInputService:GetGamepadConnected(Enum.UserInputType.Gamepad1) then
local state = UserInputService:GetGamepadState(Enum.UserInputType.Gamepad1)
for _, input in pairs(state) do
if input.KeyCode == Enum.KeyCode.ButtonL2 and input.Position.Z > 0.1 then yForce = 1 end
if input.KeyCode == Enum.KeyCode.ButtonR2 and input.Position.Z > 0.1 then yForce = -1 end
end
end
if gamepadRotation.Magnitude > 0 then applyRot(gamepadRotation * currentSens) end
-- Calculate Velocity
local dir = (cameraCFrame.RightVector * mv.X) + (cameraCFrame.LookVector * -mv.Z) + (camera.CFrame.UpVector * yForce)
local targetVel = dir * currentSpeed
if slideEnabled then velocity = velocity:Lerp(targetVel, math.clamp(dt * (10/slipPower), 0.0001, 1.0)) else velocity = targetVel end
local moveStep = velocity
-- // COLLISION SOLVER //
if collisionEnabled and moveStep.Magnitude > 0.001 then
local params = RaycastParams.new()
params.FilterDescendantsInstances = {player.Character}
params.FilterType = Enum.RaycastFilterType.Exclude
for i = 1, 3 do
if moveStep.Magnitude < 0.0001 then break end
local cast = workspace:Spherecast(cameraCFrame.Position, CAM_RADIUS, moveStep + (moveStep.Unit * 0.1), params)
if cast and cast.Instance and cast.Instance.CanCollide then
local normal = cast.Normal
moveStep = moveStep - (normal * moveStep:Dot(normal))
else
break
end
end
end
cameraCFrame = cameraCFrame + moveStep
camera.CFrame = cameraCFrame; camera.FieldOfView = currentFOV
end)
-- // UI INTERACTION //
local function sync(box, var, prefix)
box.FocusLost:Connect(function()
local val = tonumber(box.Text:match("[%d%.]+"))
if val then
if var == "speed" then currentSpeed = val elseif var == "sens" then currentSens = val elseif var == "fov" then currentFOV = val
elseif var == "slip" then slipPower = math.max(val, 0.01) elseif var == "fps" then targetFPS = math.clamp(val, 1, 240)
elseif var == "pbs" then playbackSpeed = val end
end
box.Text = prefix .. tostring(val or "??")
end)
end
sync(speedIn, "speed", "Speed: "); sync(sensIn, "sens", "Sens: "); sync(fovIn, "fov", "FOV: ")
sync(slipIn, "slip", "Slip: "); sync(fpsIn, "fps", "FPS: "); sync(pbSpeedIn, "pbs", "PB Speed: ")
toggleBtn.MouseButton1Click:Connect(function()
isFreecamEnabled = not isFreecamEnabled
toggleBtn.Text = isFreecamEnabled and "CAM: ON" or "CAM: OFF"
toggleBtn.BackgroundColor3 = isFreecamEnabled and Color3.new(0, 0.7, 0.3) or Color3.fromRGB(180, 50, 50)
if isFreecamEnabled then
cameraCFrame = camera.CFrame; local look = cameraCFrame.LookVector
yaw = math.atan2(-look.X, -look.Z); pitch = math.asin(math.clamp(look.Y, -0.999, 0.999))
if player.Character then for _, v in pairs(player.Character:GetDescendants()) do if v:IsA("BasePart") then v.Anchored = true end end end
else
isRecording = false; isPlaying = false; camera.CameraType = Enum.CameraType.Custom
if player.Character then for _, v in pairs(player.Character:GetDescendants()) do if v:IsA("BasePart") then v.Anchored = false end end end
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end
end)
recBtn.MouseButton1Click:Connect(function()
if isRecording then
isRecording = false; recBtn.Text = "RECORD"; recBtn.BackgroundColor3 = Color3.fromRGB(200, 50, 50)
table.insert(savedRecordings, {name = "Rec_"..#savedRecordings+1, date = os.date("%d/%m/%y %H:%M"), dur = totalDuration, path = recordedPath})
saveToDisk(); refreshSaves()
else
isRecording = true; isPlaying = false; recBtn.Text = "STOP"; recBtn.BackgroundColor3 = Color3.new(0.8, 0, 0)
recordedPath = {}; recStartTime = os.clock(); playbackTime = 0
end
end)
playBtn.MouseButton1Click:Connect(function()
if isPlaying then isPlaying = false; playBtn.Text = "PLAY"; playBtn.BackgroundColor3 = Color3.fromRGB(50, 150, 50)
elseif #recordedPath > 0 then isPlaying = true; isRecording = false; playBtn.Text = "PAUSE"; playBtn.BackgroundColor3 = Color3.fromRGB(100, 200, 100)
if playbackTime >= totalDuration then playbackTime = 0 end end
end)
loopBtn.MouseButton1Click:Connect(function() isLooping = not isLooping; loopBtn.Text = isLooping and "LOOP: ON" or "LOOP: OFF"; loopBtn.BackgroundColor3 = isLooping and Color3.fromRGB(200, 150, 0) or Color3.fromRGB(35, 35, 35) end)
revBtn.MouseButton1Click:Connect(function() isReverse = not isReverse; revBtn.Text = isReverse and "REV: ON" or "REV: OFF"; revBtn.BackgroundColor3 = isReverse and Color3.fromRGB(200, 100, 0) or Color3.fromRGB(35, 35, 35) end)
savesBtn.MouseButton1Click:Connect(function() savesFrame.Visible = true; refreshSaves() end)
closeSaves.MouseButton1Click:Connect(function() savesFrame.Visible = false end)
-- // SEEK LOGIC (UPDATED) //
local isDraggingSeek = false
local wasPlayingBeforeSeek = false
local function updateSeek(input)
-- FIX: Check vertical distance (100 pixels threshold)
local barCenterY = seekBarBg.AbsolutePosition.Y + (seekBarBg.AbsoluteSize.Y / 2)
local distY = math.abs(input.Position.Y - barCenterY)
if distY > 100 then return end -- Stop scrubbing if finger is too far away
local pct = math.clamp((input.Position.X - seekBarBg.AbsolutePosition.X) / seekBarBg.AbsoluteSize.X, 0, 1)
playbackTime = totalDuration * pct
seekKnob.Position = UDim2.new(pct, 0, 0.5, 0); seekFill.Size = UDim2.new(pct, 0, 1, 0)
seekTimeLabel.Text = string.format("%.1fs / %.1fs", playbackTime, totalDuration)
if #recordedPath > 0 then camera.CameraType = Enum.CameraType.Scriptable; camera.CFrame = getFrameAtTime(playbackTime) end
end
seekBarBg.InputBegan:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then
isDraggingSeek = true
wasPlayingBeforeSeek = isPlaying
isPlaying = false -- Pause while dragging so it doesn't fight the user
playBtn.Text = "PAUSE"
updateSeek(i)
end
end)
seekBarBg.InputEnded:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then
isDraggingSeek = false
if wasPlayingBeforeSeek then isPlaying = true; playBtn.Text = "PAUSE" else playBtn.Text = "PLAY" end
end
end)
UserInputService.InputChanged:Connect(function(i)
if isDraggingSeek and (i.UserInputType == Enum.UserInputType.MouseMovement or i.UserInputType == Enum.UserInputType.Touch) then
updateSeek(i)
end
end)
-- Misc Buttons
modeBtn.MouseButton1Click:Connect(function() is180Mode = not is180Mode; modeBtn.Text = is180Mode and "180°: ON" or "180°: OFF" end)
lockRotBtn.MouseButton1Click:Connect(function() rotationLocked = not rotationLocked; lockRotBtn.Text = rotationLocked and "ROT LOCK: ON" or "ROT LOCK: OFF"; lockRotBtn.BackgroundColor3 = rotationLocked and Color3.fromRGB(200, 50, 50) or Color3.fromRGB(35,35,35) end)
colTog.MouseButton1Click:Connect(function() collisionEnabled = not collisionEnabled; colTog.Text = collisionEnabled and "COLLIDE: ON" or "COLLIDE: OFF" end)
slideBtn.MouseButton1Click:Connect(function() slideEnabled = not slideEnabled; slideBtn.Text = slideEnabled and "SLIDE: ON" or "SLIDE: OFF" end)
dpadTog.MouseButton1Click:Connect(function() isDpadMode = not isDpadMode; dpadTog.Text = isDpadMode and "DPAD: ON" or "DPAD: OFF"; dpadFrame.Visible = isDpadMode; dpadTog.BackgroundColor3 = isDpadMode and Color3.fromRGB(255, 120, 0) or Color3.fromRGB(150, 80, 0); player.DevTouchMovementMode = isDpadMode and Enum.DevTouchMovementMode.Scriptable or Enum.DevTouchMovementMode.UserChoice end)
fpsTog.MouseButton1Click:Connect(function() fpsCapEnabled = not fpsCapEnabled; fpsTog.Text = fpsCapEnabled and "FPS CAP: ON" or "FPS CAP: OFF" end)
tpBtn.MouseButton1Click:Connect(function() if player.Character then player.Character:MoveTo(cameraCFrame.Position) end end)
hideBtn.MouseButton1Click:Connect(function() mainFrame.Visible = false; showBtn.Visible = true end)
showBtn.MouseButton1Click:Connect(function() mainFrame.Visible = true; showBtn.Visible = false end)
-- Dragging Logic
local isDrag, isResize, activeFrame, dStart, sPos, sSize = false, false, nil, nil, nil, nil
dragHandle.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then isDrag=true; activeFrame=mainFrame; dStart=i.Position; sPos=mainFrame.Position end end)
savesHeader.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then isDrag=true; activeFrame=savesFrame; dStart=i.Position; sPos=savesFrame.Position end end)
resizeHandle.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then isResize=true; dStart=i.Position; sSize=mainFrame.Size end end)
UserInputService.InputChanged:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseMovement or i.UserInputType == Enum.UserInputType.Touch then
if isDrag and activeFrame then local delta = i.Position - dStart; activeFrame.Position = UDim2.new(sPos.X.Scale, sPos.X.Offset + delta.X, sPos.Y.Scale, sPos.Y.Offset + delta.Y)
elseif isResize then local delta = i.Position - dStart; mainFrame.Size = UDim2.new(0, math.max(160, sSize.X.Offset + delta.X), 0, math.max(200, sSize.Y.Offset + delta.Y)) end
end
end)
UserInputService.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then isDrag=false; isResize=false; activeFrame=nil end end)
local function upDpad()
local x, z = (dpadState.Right and 1 or 0) - (dpadState.Left and 1 or 0), (dpadState.Down and 1 or 0) - (dpadState.Up and 1 or 0)
dpadMove = Vector3.new(x, 0, z); if dpadMove.Magnitude > 0 then dpadMove = dpadMove.Unit end
end
local function bindD(b, d)
b.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch or i.UserInputType == Enum.UserInputType.MouseButton1 then dpadState[d] = true; upDpad(); b.BackgroundTransparency = 0.1 end end)
b.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch or i.UserInputType == Enum.UserInputType.MouseButton1 then dpadState[d] = false; upDpad(); b.BackgroundTransparency = 0.4 end end)
end
bindD(btnU, "Up"); bindD(btnD, "Down"); bindD(btnL, "Left"); bindD(btnR, "Right")
-- // PC & MOBILE INPUTS //
UserInputService.InputBegan:Connect(function(input, gpe)
if not isFreecamEnabled then return end
if input.UserInputType == Enum.UserInputType.MouseButton2 then
isRightMouseDown = true
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
end
if isDpadMode and input.UserInputType == Enum.UserInputType.Gamepad1 then
local k = input.KeyCode
if k == Enum.KeyCode.DPadUp then dpadState.Up = true elseif k == Enum.KeyCode.DPadDown then dpadState.Down = true elseif k == Enum.KeyCode.DPadLeft then dpadState.Left = true elseif k == Enum.KeyCode.DPadRight then dpadState.Right = true end
upDpad()
end
if input.UserInputType == Enum.UserInputType.Touch and not isDpadMode and input.Position.X < (camera.ViewportSize.X * 0.45) then joystickTouch = input end
end)
UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton2 then
isRightMouseDown = false
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end
if input == joystickTouch then joystickTouch = nil end
if input.KeyCode == Enum.KeyCode.Thumbstick2 then gamepadRotation = Vector2.zero end
if isDpadMode and input.UserInputType == Enum.UserInputType.Gamepad1 then
local k = input.KeyCode
if k == Enum.KeyCode.DPadUp then dpadState.Up = false elseif k == Enum.KeyCode.DPadDown then dpadState.Down = false elseif k == Enum.KeyCode.DPadLeft then dpadState.Left = false elseif k == Enum.KeyCode.DPadRight then dpadState.Right = false end
upDpad()
end
end)
UserInputService.InputChanged:Connect(function(input, gpe)
if not isFreecamEnabled then return end
-- Gamepad Rotation
if input.KeyCode == Enum.KeyCode.Thumbstick2 then
local p = input.Position; gamepadRotation = (p.Magnitude > 0.1) and Vector2.new(p.X * 5, -p.Y * 5) or Vector2.zero
-- PC & Mobile Rotation
elseif input.UserInputType == Enum.UserInputType.MouseMovement or (input.UserInputType == Enum.UserInputType.Touch and input ~= joystickTouch) then
-- // STOP ROTATION IF INTERACTING WITH UI //
if gpe then return end
if not isDraggingSeek and not (isDpadMode and input.UserInputType == Enum.UserInputType.Touch and input.Position.X < (camera.ViewportSize.X * 0.45)) then
if input.UserInputType == Enum.UserInputType.Touch or (input.UserInputType == Enum.UserInputType.MouseMovement and isRightMouseDown) then
applyRot(Vector2.new(input.Delta.X * currentSens, input.Delta.Y * currentSens))
end
end
end
end)No comments yet for this script.



