local RS = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local UIS = game:GetService("UserInputService") local Players = game:GetService("Players") local plr = Players.LocalPlayer local cam = workspace.CurrentCamera if shared.SteoryHub_Cleanup then pcall(shared.SteoryHub_Cleanup) end local remote pcall(function() local m = RS:WaitForChild("NexusVRCharacterModel", 8) remote = m:WaitForChild("UpdateInputs", 8) local ready = m:FindFirstChild("ReplicationReady") if ready then ready:FireServer() end end) local CFG = { sendRate = 1 / 30, smooth = 22, reach = 3.5, } local S = { enabled = false, noclip = true, hud = true, } local sin, cos, exp, clamp, abs = math.sin, math.cos, math.exp, math.clamp, math.abs local V3 = Vector3.new local DOWN = V3(0, -1, 0) local conns = {} local function bind(signal, fn) conns[#conns + 1] = signal:Connect(fn) end local function newHand(sign) local rest = V3(sign * 1.0, -0.7, -0.6) return { sign = sign, shoulder = V3(sign * 1.0, 0.6, 0), rest = rest, pos = rest, vel = V3(0, 0, 0), target = rest, roll = 0, rollTarget = 0, cf = CFrame.new(rest), dir = DOWN, } end local hands = {L = newHand(-1), R = newHand(1)} local headSmooth = nil local state = {valid = false, rootCF = CFrame.new(), headCF = CFrame.new(), dirL = DOWN, dirR = DOWN} local function make(class, props, parent) local o = Instance.new(class) for k, v in pairs(props) do o[k] = v end o.Parent = parent return o end local function getParent() if gethui then local ok, g = pcall(gethui) if ok and g then return g end end local ok, core = pcall(function() return game:GetService("CoreGui") end) if ok and core then local tested = pcall(function() local t = Instance.new("Folder") t.Parent = core t:Destroy() end) if tested then return core end end return plr:WaitForChild("PlayerGui") end local parentGui = getParent() if parentGui:FindFirstChild("SteoryHub_MobileJoyFixed") then parentGui.SteoryHub_MobileJoyFixed:Destroy() end local gui = make("ScreenGui", { Name = "SteoryHub_MobileJoyFixed", ResetOnSpawn = false, DisplayOrder = 99999, IgnoreGuiInset = true, }, parentGui) local panel = make("Frame", { Size = UDim2.fromOffset(200, 100), Position = UDim2.fromOffset(16, 16), BackgroundColor3 = Color3.fromRGB(12, 14, 20), BackgroundTransparency = 0.2, BorderSizePixel = 0, Active = true, Draggable = true, }, gui) make("UICorner", {CornerRadius = UDim.new(0, 10)}, panel) make("UIStroke", {Color = Color3.fromRGB(0, 200, 255), Transparency = 0.5, Thickness = 1.2}, panel) local titleLabel = make("TextLabel", { Size = UDim2.new(1, 0, 0, 30), Position = UDim2.fromOffset(0, 5), BackgroundTransparency = 1, Text = "STEORYHUB · VR", TextColor3 = Color3.fromRGB(225, 235, 245), Font = Enum.Font.Code, TextSize = 12, }, panel) local vrToggleBtn = make("TextButton", { Size = UDim2.new(0.9, 0, 0, 45), Position = UDim2.new(0.05, 0, 0.42, 0), BackgroundColor3 = Color3.fromRGB(150, 40, 40), Text = "Ligar Modo VR", TextColor3 = Color3.new(1, 1, 1), Font = Enum.Font.GothamBold, TextSize = 14, BorderSizePixel = 0, }, panel) make("UICorner", {CornerRadius = UDim.new(0, 8)}, vrToggleBtn) local joystickGui = make("Frame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Visible = false, }, gui) local function createBlockedJoystick(position, sideLabel) local blocker = make("TextButton", { Size = UDim2.fromOffset(180, 180), Position = position, AnchorPoint = Vector2.new(0.5, 0.5), BackgroundTransparency = 1, Text = "", AutoButtonColor = false, }, joystickGui) local base = make("Frame", { Size = UDim2.fromOffset(120, 120), Position = UDim2.fromScale(0.5, 0.5), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = Color3.fromRGB(40, 40, 40), BackgroundTransparency = 0.4, BorderSizePixel = 0, }, blocker) make("UICorner", {CornerRadius = UDim.new(1, 0)}, base) local label = make("TextLabel", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = sideLabel, TextColor3 = Color3.fromRGB(200, 200, 200), Font = Enum.Font.GothamBold, TextSize = 13, }, base) local knob = make("Frame", { Size = UDim2.fromOffset(55, 55), Position = UDim2.fromScale(0.5, 0.5), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = Color3.fromRGB(0, 170, 255), BackgroundTransparency = 0.1, BorderSizePixel = 0, }, base) make("UICorner", {CornerRadius = UDim.new(1, 0)}, knob) return blocker, knob end local joyLeftBlocker, joyLeftKnob = createBlockedJoystick(UDim2.new(0.18, 0, 0.72, 0), "Mão Esq") local joyRightBlocker, joyRightKnob = createBlockedJoystick(UDim2.new(0.82, 0, 0.72, 0), "Mão Dir") local leftInputId, rightInputId = nil, nil local leftDelta = Vector2.new(0, 0) local rightDelta = Vector2.new(0, 0) local joyRadius = 50 joyLeftBlocker.InputBegan:Connect(function(input) if not S.enabled then return end if input.UserInputType == Enum.UserInputType.Touch and not leftInputId then leftInputId = input input:Capture() end end) joyRightBlocker.InputBegan:Connect(function(input) if not S.enabled then return end if input.UserInputType == Enum.UserInputType.Touch and not rightInputId then rightInputId = input input:Capture() end end) UIS.InputChanged:Connect(function(input) if not S.enabled then return end if input == leftInputId then local center = joyLeftBlocker.AbsolutePosition + Vector2.new(90, 90) local current = input.Position local diff = Vector2.new(current.X, current.Y) - center if diff.Magnitude > joyRadius then diff = diff.Unit * joyRadius end joyLeftKnob.Position = UDim2.new(0.5, diff.X, 0.5, diff.Y) leftDelta = diff / joyRadius elseif input == rightInputId then local center = joyRightBlocker.AbsolutePosition + Vector2.new(90, 90) local current = input.Position local diff = Vector2.new(current.X, current.Y) - center if diff.Magnitude > joyRadius then diff = diff.Unit * joyRadius end joyRightKnob.Position = UDim2.new(0.5, diff.X, 0.5, diff.Y) rightDelta = diff / joyRadius end end) local function resetJoysticks() leftInputId, rightInputId = nil, nil leftDelta, rightDelta = Vector2.new(0, 0), Vector2.new(0, 0) joyLeftKnob.Position = UDim2.fromScale(0.5, 0.5) joyRightKnob.Position = UDim2.fromScale(0.5, 0.5) end UIS.InputEnded:Connect(function(input) if input == leftInputId then leftInputId = nil leftDelta = Vector2.new(0, 0) joyLeftKnob.Position = UDim2.fromScale(0.5, 0.5) elseif input == rightInputId then rightInputId = nil rightDelta = Vector2.new(0, 0) joyRightKnob.Position = UDim2.fromScale(0.5, 0.5) end end) vrToggleBtn.MouseButton1Click:Connect(function() S.enabled = not S.enabled if S.enabled then vrToggleBtn.BackgroundColor3 = Color3.fromRGB(40, 150, 60) vrToggleBtn.Text = "Desligar VR" joystickGui.Visible = true else vrToggleBtn.BackgroundColor3 = Color3.fromRGB(150, 40, 40) vrToggleBtn.Text = "Ligar Modo VR" joystickGui.Visible = false resetJoysticks() state.valid = false end end) local function makeMarker(color, letter) local f = make("Frame", { Size = UDim2.fromOffset(22, 22), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = color, BackgroundTransparency = 0.55, BorderSizePixel = 0, Visible = false, }, gui) make("UICorner", {CornerRadius = UDim.new(1, 0)}, f) make("UIStroke", {Color = color, Thickness = 2, Transparency = 0.1}, f) make("TextLabel", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = letter, TextColor3 = Color3.new(1, 1, 1), TextSize = 12, Font = Enum.Font.GothamBold, }, f) return f end local markerL = makeMarker(Color3.fromRGB(255, 80, 80), "E") local markerR = makeMarker(Color3.fromRGB(70, 140, 255), "D") local function rotBetween(from, to) local dot = clamp(from:Dot(to), -1, 1) local axis = from:Cross(to) if axis.Magnitude < 1e-4 then if dot > 0 then return CFrame.new() end return CFrame.fromAxisAngle(Vector3.xAxis, math.pi) end return CFrame.fromAxisAngle(axis.Unit, math.acos(dot)) end local joints = {} local function refreshJoints(char) if joints.char == char and joints.neck and joints.neck.Parent then return end joints.char = char joints.neck = char:FindFirstChild("Neck", true) joints.L = char:FindFirstChild("Left Shoulder", true) or char:FindFirstChild("LeftShoulder", true) joints.R = char:FindFirstChild("Right Shoulder", true) or char:FindFirstChild("RightShoulder", true) end local function applyJoints() if not S.enabled or not state.valid then return end local char = plr.Character if not char then return end refreshJoints(char) local n, l, r = joints.neck, joints.L, joints.R if n and n.Part0 then n.Transform = n.C0.Rotation:Inverse() * n.Part0.CFrame.Rotation:Inverse() * state.headCF.Rotation * n.C1.Rotation end if l and l.Part0 then local fr = l.Part0.CFrame * l.C0 l.Transform = rotBetween(DOWN, fr:VectorToObjectSpace(state.dirL)) end if r and r.Part0 then local fr = r.Part0.CFrame * r.C0 r.Transform = rotBetween(DOWN, fr:VectorToObjectSpace(state.dirR)) end end local function stepSpring(h, target, dt) local w = CFG.smooth local e = exp(-w * dt) local x = h.pos - target local tmp = h.vel + x * w h.pos = target + (x + tmp * dt) * e h.vel = (h.vel - tmp * w * dt) * e end local function buildHandCF(h) local v = h.pos - h.shoulder local dir = v.Magnitude > 0.05 and v.Unit or DOWN h.dir = dir local up = abs(dir.Y) > 0.98 and V3(0, 0, -1) or Vector3.yAxis h.cf = CFrame.lookAt(h.pos, h.pos + dir, up) end local lastSend = 0 bind(RunService.RenderStepped, function(dt) dt = math.min(dt, 0.1) cam = workspace.CurrentCamera local char = plr.Character local root = char and char:FindFirstChild("HumanoidRootPart") if not root or not S.enabled then state.valid = false markerL.Visible = false markerR.Visible = false return end local now = os.clock() local rootCF = root.CFrame local chest = rootCF.Position + rootCF.UpVector * 0.5 + rootCF.LookVector * 1.2 local moveL = V3(leftDelta.X * 1.8, -leftDelta.Y * 1.8, 0) local moveR = V3(rightDelta.X * 1.8, -rightDelta.Y * 1.8, 0) local targetL = rootCF:PointToObjectSpace(chest + cam.CFrame:VectorToWorldSpace(V3(-0.8, 0, 0) + moveL)) local targetR = rootCF:PointToObjectSpace(chest + cam.CFrame:VectorToWorldSpace(V3(0.8, 0, 0) + moveR)) hands.L.target = targetL hands.R.target = targetR for _, h in pairs({hands.L, hands.R}) do stepSpring(h, h.target, dt) buildHandCF(h) end local headPos = rootCF.Position + V3(0, 2, 0) local headTarget = CFrame.new(headPos) * cam.CFrame.Rotation if headSmooth then headSmooth = headSmooth:Lerp(headTarget, 1 - exp(-dt * 18)) else headSmooth = headTarget end state.valid = true state.rootCF = rootCF state.headCF = headSmooth state.dirL = rootCF:VectorToWorldSpace(hands.L.dir) state.dirR = rootCF:VectorToWorldSpace(hands.R.dir) applyJoints() if remote and now - lastSend >= CFG.sendRate then lastSend = now remote:FireServer(headSmooth, rootCF * hands.L.cf, rootCF * hands.R.cf) end if S.hud then local pl, onL = cam:WorldToViewportPoint(rootCF:PointToWorldSpace(hands.L.pos)) local pr, onR = cam:WorldToViewportPoint(rootCF:PointToWorldSpace(hands.R.pos)) markerL.Visible = onL markerR.Visible = onR markerL.Position = UDim2.fromOffset(pl.X, pl.Y) markerR.Position = UDim2.fromOffset(pr.X, pr.Y) else markerL.Visible = false markerR.Visible = false end end) local ncAcc = 0 bind(RunService.Stepped, function(_, dt) applyJoints() if S.noclip then ncAcc = ncAcc + dt if ncAcc >= 0.1 then ncAcc = 0 for _, other in ipairs(Players:GetPlayers()) do if other ~= plr and other.Character then for _, p in ipairs(other.Character:GetChildren()) do if p:IsA("BasePart") then p.CanCollide = false end end end end end end end) shared.SteoryHub_Cleanup = function() for _, c in ipairs(conns) do c:Disconnect() end S.enabled = false if gui then gui:Destroy() end shared.SteoryHub_Cleanup = nil end