VR MOBILE (Funciona na maioria dos R15)
Key RequiredUniversal

VR MOBILE (Funciona na maioria dos R15)

Universal ScriptCYGRUSCYGRUS 1.5K views 8.4K executesUpdated Oct 5, 2026
3.3/ 5(3 votes)
2167% Positive
Lua Script•439 lines (12086 chars)
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 = "<b>STEORYHUB · VR</b>",
	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

Features & Information

SteoryHub Mobile VR Sistema de VR para telemóvel com dois joysticks para controlar as mãos (funciona na maioria dos jogos R15). Código aberto, deixe os créditos se for usar em outro script ou publicar, por favor.
#VR#MOBILE#VR MOBILE#SEM KEY

Community Comments (3)

WuzRERBornWuzRERBornOct 11, 2026

é FE?

Replying to @WuzRERBorn
WuzRERBornWuzRERBornOct 11, 2026

na real esse script me deu até uma ideia, valeu por deixar open source :D

Replying to @WuzRERBorn
CYGRUSCYGRUSOct 11, 2026

@WuzRERBorn You're welcome, thank you very much for using.

More Scripts for Universal Scripts