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 -- Size of the camera hitbox -- 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 -- For PC Rotation -- 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_Full" 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 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 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.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.Size = UDim2.new(1, 0, 0, 6) seekBarBg.Position = UDim2.new(0, 0, 0, 18) seekBarBg.BackgroundColor3 = Color3.fromRGB(60,60,60) 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.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.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.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.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.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 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.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.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 -- // FIXED COLLISION SYSTEM (Iterative) // if collisionEnabled and moveStep.Magnitude > 0.001 then local params = RaycastParams.new() params.FilterDescendantsInstances = {player.Character} params.FilterType = Enum.RaycastFilterType.Exclude -- Iterate 3 times to handle corners (Wall -> Floor -> Slide) 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 -- Project velocity onto the wall plane (slide) local normal = cast.Normal local proj = moveStep - (normal * moveStep:Dot(normal)) moveStep = proj else break -- No collision found 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 -- Reset mouse 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 local isDraggingSeek = false local function updateSeek(input) 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 not isPlaying and #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; updateSeek(i) end end) seekBarBg.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then isDraggingSeek = false 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") -- // FIXED PC 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 -- LOCK MOUSE 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 -- UNLOCK MOUSE 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 if not isDraggingSeek and not (isDpadMode and input.UserInputType == Enum.UserInputType.Touch and input.Position.X < (camera.ViewportSize.X * 0.45)) then -- Allow rotation if: It is Touch OR (It is Mouse AND Right Button is Held) 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)