-- Smooth Fly GUI — camera-relative movement + auto shift-lock + fixed vertical speed -- Place this LocalScript into StarterGui local Players = game:GetService("Players") local UIS = game:GetService("UserInputService") local RS = game:GetService("RunService") local Workspace = game:GetService("Workspace") local player = Players.LocalPlayer local PlayerGui = player:WaitForChild("PlayerGui") local cam = Workspace.CurrentCamera -- GUI ------------------------------------------------ local gui = Instance.new("ScreenGui") gui.Name = "FlyGUI" gui.ResetOnSpawn = false gui.Parent = PlayerGui local toggleBtn = Instance.new("ImageButton") toggleBtn.Size = UDim2.new(0,64,0,64) toggleBtn.Position = UDim2.new(0.04,0,0.78,0) toggleBtn.BackgroundColor3 = Color3.fromRGB(98,72,255) toggleBtn.Image = "rbxassetid://3926305904" toggleBtn.ImageRectOffset = Vector2.new(364,324) toggleBtn.ImageRectSize = Vector2.new(36,36) toggleBtn.AutoButtonColor = true toggleBtn.Active = true toggleBtn.Draggable = true toggleBtn.Parent = gui Instance.new("UICorner", toggleBtn).CornerRadius = UDim.new(1,0) do local s = Instance.new("UIStroke", toggleBtn); s.Thickness = 2; s.Color = Color3.fromRGB(255,255,255) end local sliderFrame = Instance.new("Frame") sliderFrame.Size = UDim2.new(0,180,0,44) sliderFrame.Position = UDim2.new(0,74,0,-10) sliderFrame.BackgroundColor3 = Color3.fromRGB(40,40,40) sliderFrame.Visible = false sliderFrame.Parent = toggleBtn Instance.new("UICorner", sliderFrame).CornerRadius = UDim.new(0.5,0) local bar = Instance.new("Frame") bar.Size = UDim2.new(1,-26,0,10) bar.Position = UDim2.new(0,13,0.4,-5) bar.BackgroundColor3 = Color3.fromRGB(110,110,110) bar.Parent = sliderFrame Instance.new("UICorner", bar).CornerRadius = UDim.new(1,0) local knob = Instance.new("Frame") knob.Size = UDim2.new(0,18,0,18) knob.Position = UDim2.new(0,0,0.4,-9) knob.BackgroundColor3 = Color3.fromRGB(0,200,255) knob.Parent = bar Instance.new("UICorner", knob).CornerRadius = UDim.new(1,0) local label = Instance.new("TextLabel") label.Size = UDim2.new(1,0,0,16) label.Position = UDim2.new(0,0,0,4) label.BackgroundTransparency = 1 label.TextColor3 = Color3.fromRGB(255,255,255) label.Font = Enum.Font.GothamBold label.TextSize = 14 label.Text = "Speed: 2" label.Parent = sliderFrame local upBtn = Instance.new("TextButton") upBtn.Size = UDim2.new(0,60,0,60) upBtn.Position = UDim2.new(0.88,0,0.65,0) upBtn.Text = "⬆️" upBtn.Font = Enum.Font.GothamBold upBtn.TextSize = 28 upBtn.TextColor3 = Color3.fromRGB(255,255,255) upBtn.BackgroundColor3 = Color3.fromRGB(0,170,255) upBtn.Visible = false upBtn.Parent = gui Instance.new("UICorner", upBtn).CornerRadius = UDim.new(1,0) local downBtn = Instance.new("TextButton") downBtn.Size = UDim2.new(0,60,0,60) downBtn.Position = UDim2.new(0.88,0,0.78,0) downBtn.Text = "⬇️" downBtn.Font = Enum.Font.GothamBold downBtn.TextSize = 28 downBtn.TextColor3 = Color3.fromRGB(255,255,255) downBtn.BackgroundColor3 = Color3.fromRGB(255,80,80) downBtn.Visible = false downBtn.Parent = gui Instance.new("UICorner", downBtn).CornerRadius = UDim.new(1,0) -- Variables & defaults -------------------------------- local flying = false local speedLevel = 1 -- 1..50 slider local speedMultiplier = 2 -- each level adds 2 studs/sec (so level*2 studs/sec) local speed = speedLevel * speedMultiplier local verticalSpeed = 14 -- fixed vertical speed (up/down) local humanoid, root, char local BV, BG -- BodyVelocity & BodyGyro local draggingKnob = false local ascendMobile = false local descendMobile = false local shiftLockActive = false -- Utility: get character references ------------------- local function getCharacter() char = player.Character or player.CharacterAdded:Wait() humanoid = char:WaitForChild("Humanoid") root = char:WaitForChild("HumanoidRootPart") end -- Stop any falling animation & ensure physics state ---- local function stopFallingAnimation() if not humanoid then return end for _, track in ipairs(humanoid:GetPlayingAnimationTracks()) do local n = track.Name or "" if n:lower():find("fall") or n:lower():find("jump") then pcall(function() track:Stop() end) end end pcall(function() humanoid:ChangeState(Enum.HumanoidStateType.Physics) end) end -- Shift-lock emulation: rotate character to face camera forward local function enableShiftLock() shiftLockActive = true end local function disableShiftLock() shiftLockActive = false end -- Start / stop flying --------------------------------- local function startFly() getCharacter() stopFallingAnimation() -- Create BodyVelocity BV = Instance.new("BodyVelocity") BV.Name = "FlyBV" BV.MaxForce = Vector3.new(1e5,1e5,1e5) BV.P = 1700 BV.Velocity = Vector3.zero BV.Parent = root -- Create BodyGyro BG = Instance.new("BodyGyro") BG.Name = "FlyBG" BG.MaxTorque = Vector3.new(4e5,4e5,4e5) BG.P = 4500 BG.D = 200 BG.CFrame = root.CFrame BG.Parent = root humanoid.AutoRotate = false upBtn.Visible = true downBtn.Visible = true enableShiftLock() end local function stopFly() if BV and BV.Parent then BV:Destroy() end if BG and BG.Parent then BG:Destroy() end if humanoid then humanoid.AutoRotate = true pcall(function() humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) end) end upBtn.Visible = false downBtn.Visible = false disableShiftLock() end -- Toggle button -------------------------------------- toggleBtn.MouseButton1Click:Connect(function() flying = not flying sliderFrame.Visible = flying if flying then toggleBtn.BackgroundColor3 = Color3.fromRGB(0,200,100) pcall(startFly) else toggleBtn.BackgroundColor3 = Color3.fromRGB(98,72,255) pcall(stopFly) end end) -- Slider handling ------------------------------------ bar.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then draggingKnob = true end end) UIS.InputChanged:Connect(function(input) if not draggingKnob then return end if input.UserInputType ~= Enum.UserInputType.MouseMovement and input.UserInputType ~= Enum.UserInputType.Touch then return end local rel = math.clamp((input.Position.X - bar.AbsolutePosition.X) / bar.AbsoluteSize.X, 0, 1) knob.Position = UDim2.new(rel, -9, 0.4, -9) speedLevel = math.max(1, math.floor(rel * 49) + 1) speed = speedLevel * speedMultiplier label.Text = "Speed: " .. tostring(speed) end) UIS.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then draggingKnob = false end end) -- Mobile up/down buttons -------------------------------- upBtn.MouseButton1Down:Connect(function() ascendMobile = true end) upBtn.MouseButton1Up:Connect(function() ascendMobile = false end) downBtn.MouseButton1Down:Connect(function() descendMobile = true end) downBtn.MouseButton1Up:Connect(function() descendMobile = false end) -- Main flight loop ------------------------------------ RS.RenderStepped:Connect(function(dt) if not flying then return end if not humanoid or not root or not BV or not BG then return end -- 1) compute camera basis (flat) local camCF = cam.CFrame local camForward = Vector3.new(camCF.LookVector.X, 0, camCF.LookVector.Z) if camForward.Magnitude < 0.001 then camForward = Vector3.new(0,0,-1) end camForward = camForward.Unit local camRight = Vector3.new(camCF.RightVector.X, 0, camCF.RightVector.Z) if camRight.Magnitude < 0.001 then camRight = Vector3.new(1,0,0) end camRight = camRight.Unit -- 2) gather player input: -- keyboard check (prefer keyboard if pressed) local kbX = 0; local kbZ = 0 if UIS:IsKeyDown(Enum.KeyCode.D) or UIS:IsKeyDown(Enum.KeyCode.Right) then kbX = kbX + 1 end if UIS:IsKeyDown(Enum.KeyCode.A) or UIS:IsKeyDown(Enum.KeyCode.Left) then kbX = kbX - 1 end if UIS:IsKeyDown(Enum.KeyCode.W) or UIS:IsKeyDown(Enum.KeyCode.Up) then kbZ = kbZ + 1 end if UIS:IsKeyDown(Enum.KeyCode.S) or UIS:IsKeyDown(Enum.KeyCode.Down) then kbZ = kbZ - 1 end local inputX, inputZ = 0, 0 if math.abs(kbX) > 0.001 or math.abs(kbZ) > 0.001 then inputX = kbX inputZ = kbZ -- normalize diagonal movement local mag = math.sqrt(inputX*inputX + inputZ*inputZ) if mag > 1 then inputX = inputX / mag; inputZ = inputZ / mag end else -- fallback to Humanoid.MoveDirection for joystick/mobile local md = humanoid.MoveDirection or Vector3.new(0,0,0) -- convert md into camera-relative components by dotting with camera basis inputX = md:Dot(camRight) inputZ = md:Dot(camForward) -- if tiny, zero them if math.abs(inputX) < 0.001 then inputX = 0 end if math.abs(inputZ) < 0.001 then inputZ = 0 end -- normalize local mag = math.sqrt(inputX*inputX + inputZ*inputZ) if mag > 1 then inputX = inputX / mag; inputZ = inputZ / mag end end -- 3) build horizontal move vector (camera-relative) local moveVec = (camForward * inputZ) + (camRight * inputX) -- 4) vertical control: fixed 14 studs/sec (keyboard or mobile buttons) local ascend = UIS:IsKeyDown(Enum.KeyCode.Space) or ascendMobile local descend = UIS:IsKeyDown(Enum.KeyCode.LeftControl) or UIS:IsKeyDown(Enum.KeyCode.LeftShift) or descendMobile local yVel = 0 if ascend then yVel = verticalSpeed elseif descend then yVel = -verticalSpeed end -- 5) target velocity (horizontal uses speed slider, vertical is fixed) local horizVel = Vector3.new(0,0,0) if moveVec.Magnitude > 0.001 then horizVel = moveVec.Unit * speed end local target = Vector3.new(horizVel.X, yVel, horizVel.Z) -- 6) smooth the velocity BV.Velocity = BV.Velocity:Lerp(target, math.clamp(12 * dt, 0, 1)) -- 7) rotation: emulate shift-lock while flying (face camera forward horizontally) if shiftLockActive then local look = Vector3.new(camForward.X, 0, camForward.Z) if look.Magnitude > 0.001 then local targetC = CFrame.new(root.Position, root.Position + look.Unit) -- smooth gyro so it doesn't snap local smooth = math.clamp(10 * dt, 0, 1) BG.CFrame = root.CFrame:Lerp(targetC, smooth) end else -- otherwise, if moving, face move direction if moveVec.Magnitude > 0.001 then local targetC = CFrame.new(root.Position, root.Position + moveVec.Unit) BG.CFrame = root.CFrame:Lerp(targetC, math.clamp(10 * dt, 0, 1)) end end end) -- cleanup/respawn handling ---------------------------- player.CharacterAdded:Connect(function() flying = false pcall(stopFly) sliderFrame.Visible = false toggleBtn.BackgroundColor3 = Color3.fromRGB(98,72,255) end) -- finished