Best pc script for mobile!!
Key RequiredUniversal

Best pc script for mobile!!

Universal ScriptBodesBodes 515 views 1.3K executesUpdated Sep 10, 2026
Unrated
00
Lua Script•606 lines (20000 chars)
--[[
    Keyboard Layout v3
    - 10 layout slots, named
    - Tap a slot name  -> load + lock (no dragging)
    - Tap ✎           -> load + open edit mode (drag + save)
    - Tap ✕ (x2)      -> delete slot
    - + CREATE NEW / BUILD WITHOUT SAVING
--]]

task.wait(7)

local Players    = game:GetService("Players")
local UIS        = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local VIM        = game:GetService("VirtualInputManager")
local PPromptSvc = game:GetService("ProximityPromptService")
local HttpSvc    = game:GetService("HttpService")

local player    = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local isMobile  = UIS.TouchEnabled and not UIS.KeyboardEnabled

pcall(function() UIS.MouseEnabled    = true end)
pcall(function() UIS.KeyboardEnabled = true end)

----------------------------------------------------------------
-- CONFIG
----------------------------------------------------------------
local CONFIG = {
    KeySize      = isMobile and 72 or 48,
    KeySpacing   = isMobile and 8  or 6,
    CornerR      = isMobile and 12 or 8,
    BaseColor    = Color3.fromRGB(30, 30, 36),
    PressedColor = Color3.fromRGB(90, 160, 255),
    ActiveColor  = Color3.fromRGB(80, 220, 130),
    TextColor    = Color3.fromRGB(235, 235, 245),
    KeyFont      = Enum.Font.GothamBold,
    TextSize     = isMobile and 26 or 18,
    PanelColor   = Color3.fromRGB(20, 20, 26),
    PanelAccent  = Color3.fromRGB(70, 130, 200),
    PanelGreen   = Color3.fromRGB(60, 180, 100),
    PanelRed     = Color3.fromRGB(180, 60, 60),
}

local WASD_POS  = isMobile and UDim2.fromScale(0.20, 0.72) or UDim2.fromScale(0.5,  0.83)
local SPACE_POS = isMobile and UDim2.fromScale(0.80, 0.68) or UDim2.fromScale(0.5,  0.92)
local SHIFT_POS = isMobile and UDim2.fromScale(0.80, 0.85) or UDim2.fromScale(0.62, 0.92)

----------------------------------------------------------------
-- ADDABLE KEYS
----------------------------------------------------------------
local ADDABLE_KEYS = {
    {label="E",code=Enum.KeyCode.E},   {label="Q",code=Enum.KeyCode.Q},
    {label="R",code=Enum.KeyCode.R},   {label="F",code=Enum.KeyCode.F},
    {label="G",code=Enum.KeyCode.G},   {label="C",code=Enum.KeyCode.C},
    {label="V",code=Enum.KeyCode.V},   {label="X",code=Enum.KeyCode.X},
    {label="Z",code=Enum.KeyCode.Z},   {label="T",code=Enum.KeyCode.T},
    {label="Y",code=Enum.KeyCode.Y},   {label="H",code=Enum.KeyCode.H},
    {label="B",code=Enum.KeyCode.B},   {label="N",code=Enum.KeyCode.N},
    {label="M",code=Enum.KeyCode.M},   {label="J",code=Enum.KeyCode.J},
    {label="K",code=Enum.KeyCode.K},   {label="L",code=Enum.KeyCode.L},
    {label="P",code=Enum.KeyCode.P},   {label="O",code=Enum.KeyCode.O},
    {label="I",code=Enum.KeyCode.I},   {label="U",code=Enum.KeyCode.U},
    {label="1",code=Enum.KeyCode.One}, {label="2",code=Enum.KeyCode.Two},
    {label="3",code=Enum.KeyCode.Three},{label="4",code=Enum.KeyCode.Four},
    {label="5",code=Enum.KeyCode.Five},
    {label="LMB",code=Enum.UserInputType.MouseButton1,isMouse=true},
    {label="RMB",code=Enum.UserInputType.MouseButton2,isMouse=true},
    {label="L-Ctrl",code=Enum.KeyCode.LeftControl},
    {label="R-Ctrl",code=Enum.KeyCode.RightControl},
    {label="Alt",code=Enum.KeyCode.LeftAlt},
    {label="Tab",code=Enum.KeyCode.Tab},
    {label="Esc",code=Enum.KeyCode.Escape},
    {label="Enter",code=Enum.KeyCode.Return},
}

----------------------------------------------------------------
-- PERSISTENCE
----------------------------------------------------------------
local FILE_PATH   = "kb_layouts_v2.json"
local layoutsData = {}

local function loadLayouts()
    local ok, content = pcall(function()
        if isfile and isfile(FILE_PATH) then return readfile(FILE_PATH) end
    end)
    if ok and content then
        local ok2, data = pcall(function() return HttpSvc:JSONDecode(content) end)
        if ok2 and type(data) == "table" then layoutsData = data end
    end
    for i = 1, 10 do
        if layoutsData[i] == nil then layoutsData[i] = false end
    end
end

local function saveLayouts()
    pcall(function()
        if writefile then
            writefile(FILE_PATH, HttpSvc:JSONEncode(layoutsData))
        end
    end)
end

loadLayouts()

----------------------------------------------------------------
-- DISABLE DEFAULT MOBILE CONTROLS
----------------------------------------------------------------
if isMobile then
    task.spawn(function()
        local ps = player:WaitForChild("PlayerScripts")
        local mod = require(ps:WaitForChild("PlayerModule"))
        mod:GetControls():Disable()
    end)
    task.spawn(function()
        local touchGui = playerGui:WaitForChild("TouchGui", 5)
        if touchGui then touchGui.Enabled = false end
    end)
end

----------------------------------------------------------------
-- ROOT GUI
----------------------------------------------------------------
local gui = Instance.new("ScreenGui")
gui.Name = "KeyboardLayout"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.DisplayOrder = 100
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
gui.Parent = playerGui

local K, S = CONFIG.KeySize, CONFIG.KeySpacing

----------------------------------------------------------------
-- STATE
----------------------------------------------------------------
local held        = { W=false, A=false, S=false, D=false, Space=false }
local shiftLockOn = false
local setupMode   = true
local dragState   = nil
local pressState  = {}
local containers  = {}

local activeLayoutIndex = nil
local containersById    = {}

----------------------------------------------------------------
-- HELPERS
----------------------------------------------------------------
local function makeContainer(pos, w, h)
    local c = Instance.new("Frame")
    c.BackgroundTransparency = 1
    c.AnchorPoint = Vector2.new(0.5, 0.5)
    c.Position = pos
    c.Size = UDim2.fromOffset(w, h)
    c.Parent = gui
    containers[c] = true
    return c
end

local function makeKey(parent, text, posX, posY, width)
    local key = Instance.new("TextButton")
    key.Text = text
    key.TextColor3 = CONFIG.TextColor
    key.Font = CONFIG.KeyFont
    key.TextSize = CONFIG.TextSize
    key.AutoButtonColor = false
    key.BackgroundColor3 = CONFIG.BaseColor
    key.BorderSizePixel = 0
    key.ZIndex = 2
    key.Selectable = false
    key.Active = true
    key.Size = UDim2.fromOffset(width or K, K)
    key.Position = UDim2.fromOffset(posX, posY)

    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, CONFIG.CornerR)
    corner.Parent = key

    local stroke = Instance.new("UIStroke")
    stroke.Color = Color3.fromRGB(60, 60, 70)
    stroke.Thickness = 1
    stroke.Parent = key

    key.Parent = parent
    return key
end

local function udim2ToTable(u)
    return {u.X.Scale, u.X.Offset, u.Y.Scale, u.Y.Offset}
end

local function tableToUdim2(t)
    if type(t) ~= "table" or #t < 4 then return UDim2.fromScale(0.5, 0.5) end
    return UDim2.new(t[1], t[2], t[3], t[4])
end

----------------------------------------------------------------
-- PROMPT HANDLING
----------------------------------------------------------------
local activePrompts = {}
local heldPrompts   = {}

local function forceCustomStyle(prompt)
    pcall(function()
        if prompt.Style ~= Enum.ProximityPromptStyle.Custom then
            prompt.Style = Enum.ProximityPromptStyle.Custom
        end
    end)
end

local function makePromptBillboard(prompt)
    local target = prompt.Parent
    if not target or not target:IsA("BasePart") then return nil end

    local bb = Instance.new("BillboardGui")
    bb.Name = "KBLayoutPromptHint"
    bb.Size = UDim2.fromOffset(110, 36)
    bb.StudsOffset = Vector3.new(0, 2.5, 0)
    bb.AlwaysOnTop = true
    bb.MaxDistance = prompt.MaxActivationDistance or 10
    bb.Parent = target

    local frame = Instance.new("Frame")
    frame.Size = UDim2.fromScale(1, 1)
    frame.BackgroundColor3 = Color3.fromRGB(20, 20, 26)
    frame.BackgroundTransparency = 0.15
    frame.BorderSizePixel = 0
    frame.Parent = bb

    local fc = Instance.new("UICorner")
    fc.CornerRadius = UDim.new(0, 8)
    fc.Parent = frame

    local label = Instance.new("TextLabel")
    label.Size = UDim2.fromScale(1, 1)
    label.BackgroundTransparency = 1
    label.TextColor3 = Color3.fromRGB(240, 240, 250)
    label.Font = Enum.Font.GothamBold
    label.TextSize = 16
    label.Text = string.format("[%s] %s",
        prompt.KeyboardKeyCode.Name,
        prompt.ActionText ~= "" and prompt.ActionText or "Interact")
    label.Parent = frame

    return bb
end

for _, d in ipairs(game:GetDescendants()) do
    if d:IsA("ProximityPrompt") then forceCustomStyle(d) end
end

game.DescendantAdded:Connect(function(d)
    if d:IsA("ProximityPrompt") then forceCustomStyle(d) end
end)

PPromptSvc.PromptShown:Connect(function(prompt)
    forceCustomStyle(prompt)
    local bb = makePromptBillboard(prompt)
    activePrompts[prompt] = {billboard = bb}
end)

PPromptSvc.PromptHidden:Connect(function(prompt)
    local data = activePrompts[prompt]
    if data and data.billboard then data.billboard:Destroy() end
    activePrompts[prompt] = nil
    heldPrompts[prompt]   = nil
end)

----------------------------------------------------------------
-- BUILD DEFAULT CLUSTERS
----------------------------------------------------------------
local wasdContainer  = makeContainer(WASD_POS,  K*3 + S*2, K*2 + S)
local spaceContainer = makeContainer(SPACE_POS, K, K)
local shiftContainer = makeContainer(SHIFT_POS, K, K)

containersById.wasd  = wasdContainer
containersById.space = spaceContainer
containersById.shift = shiftContainer

local keyW = makeKey(wasdContainer, "W", 1*(K+S), 0)
local keyA = makeKey(wasdContainer, "A", 0,       1*(K+S))
local keyS = makeKey(wasdContainer, "S", 1*(K+S), 1*(K+S))
local keyD = makeKey(wasdContainer, "D", 2*(K+S), 1*(K+S))

local spaceKey = makeKey(spaceContainer, "⤒", 0, 0, K)
spaceKey.TextSize = isMobile and 40 or 24

local shiftKey = makeKey(shiftContainer, "🔒", 0, 0, K)
shiftKey.TextSize = isMobile and 32 or 20

----------------------------------------------------------------
-- ADDED KEYS
----------------------------------------------------------------
local addedKeys = {}

local function findAddedEntry(button)
    for _, e in ipairs(addedKeys) do
        if e.button == button then return e end
    end
    return nil
end

local function addKey(label, code, isMouse, position)
    local c = makeContainer(position or UDim2.fromScale(0.5, 0.4), K, K)
    local btn = makeKey(c, label, 0, 0, K)
    if #label > 2 then
        btn.TextSize = math.max(10, CONFIG.TextSize - 8)
    end

    local isInteract = (code == Enum.KeyCode.E or code == Enum.KeyCode.F) and not isMouse

    local entry = {
        container  = c,
        button     = btn,
        code       = code,
        isMouse    = isMouse,
        isInteract = isInteract,
    }
    table.insert(addedKeys, entry)

    btn.InputBegan:Connect(function(input)
        if input.UserInputType ~= Enum.UserInputType.MouseButton1
        and input.UserInputType ~= Enum.UserInputType.Touch then return end

        pressState[input] = { button = btn, id = "ADDED", wasDrag = false }
        btn.BackgroundColor3 = CONFIG.PressedColor

        if isInteract then
            for prompt in pairs(activePrompts) do
                if prompt.Parent and not heldPrompts[prompt] then
                    heldPrompts[prompt] = true
                    task.spawn(function()
                        pcall(function() prompt:InputHoldBegin() end)
                    end)
                end
            end
        elseif not isMouse then
            VIM:SendKeyEvent(true, code, false, game)
        end

        if setupMode and not dragState then
            dragState = {
                input      = input,
                container  = c,
                startMouse = input.Position,
                startPos   = c.Position,
                moved      = false,
            }
        end
    end)

    return entry
end

local function clearAddedKeys()
    for _, e in ipairs(addedKeys) do
        e.container:Destroy()
    end
    addedKeys = {}
end

----------------------------------------------------------------
-- LAYOUT SNAPSHOT / APPLY
----------------------------------------------------------------
local function snapshotLayout()
    local snap = {containers = {}, addedKeys = {}}
    for id, c in pairs(containersById) do
        snap.containers[id] = udim2ToTable(c.Position)
    end
    for _, e in ipairs(addedKeys) do
        table.insert(snap.addedKeys, {
            label    = e.button.Text,
            codeName = e.code.Name,
            isMouse  = e.isMouse,
            pos      = udim2ToTable(e.container.Position),
        })
    end
    return snap
end

local function applyLayout(layout)
    if not layout then return end

    for id, pos in pairs(layout.containers or {}) do
        local c = containersById[id]
        if c then c.Position = tableToUdim2(pos) end
    end

    clearAddedKeys()

    for _, k in ipairs(layout.addedKeys or {}) do
        local code
        if k.isMouse then
            code = Enum.UserInputType[k.codeName]
        else
            code = Enum.KeyCode[k.codeName]
        end
        if code then
            addKey(k.label, code, k.isMouse, tableToUdim2(k.pos))
        end
    end
end

local function resetToDefault()
    containersById.wasd.Position  = WASD_POS
    containersById.space.Position = SPACE_POS
    containersById.shift.Position = SHIFT_POS
    clearAddedKeys()
end

----------------------------------------------------------------
-- PRESS / RELEASE
----------------------------------------------------------------
local function cancelAllPresses()
    for inputObj, entry in pairs(pressState) do
        entry.button.BackgroundColor3 = CONFIG.BaseColor
        if entry.id ~= "Shift" and held[entry.id] ~= nil then
            held[entry.id] = false
        end
        pressState[inputObj] = nil
    end
end

local function findContainerOf(button)
    local p = button.Parent
    while p and p ~= gui do
        if containers[p] then return p end
        p = p.Parent
    end
    return nil
end

local function bindKey(button, keyId)
    button.InputBegan:Connect(function(input)
        if input.UserInputType ~= Enum.UserInputType.MouseButton1
        and input.UserInputType ~= Enum.UserInputType.Touch then return end

        pressState[input] = { button = button, id = keyId, wasDrag = false }
        button.BackgroundColor3 = CONFIG.PressedColor

        if held[keyId] ~= nil and keyId ~= "Shift" then
            held[keyId] = true
        end

        if setupMode and not dragState then
            local c = findContainerOf(button)
            if c then
                dragState = {
                    input      = input,
                    container  = c,
                    startMouse = input.Position,
                    startPos   = c.Position,
                    moved      = false,
                }
            end
        end
    end)
end

bindKey(keyW, "W")
bindKey(keyA, "A")
bindKey(keyS, "S")
bindKey(keyD, "D")
bindKey(spaceKey, "Space")
bindKey(shiftKey, "Shift")

UIS.InputChanged:Connect(function(input)
    if not dragState then return end
    if input.UserInputType ~= Enum.UserInputType.MouseMovement
    and input.UserInputType ~= Enum.UserInputType.Touch then return end

    local delta = input.Position - dragState.startMouse
    if not dragState.moved and delta.Magnitude > 8 then
        dragState.moved = true
        cancelAllPresses()
    end

    if dragState.moved and setupMode then
        dragState.container.Position = UDim2.new(
            dragState.startPos.X.Scale,
            dragState.startPos.X.Offset + delta.X,
            dragState.startPos.Y.Scale,
            dragState.startPos.Y.Offset + delta.Y
        )
    end
end)

UIS.InputEnded:Connect(function(input)
    local entry = pressState[input]
    if entry then
        local wasDrag = dragState and dragState.moved
        entry.button.BackgroundColor3 = CONFIG.BaseColor

        if entry.id == "Shift" then
            if not wasDrag then
                shiftLockOn = not shiftLockOn
                shiftKey.BackgroundColor3 = shiftLockOn and CONFIG.ActiveColor or CONFIG.BaseColor
                local char = player.Character
                local hum  = char and char:FindFirstChildOfClass("Humanoid")
                if hum then
                    hum.CameraOffset = shiftLockOn and Vector3.new(1.75,0,0) or Vector3.zero
                end
            end
        else
            if held[entry.id] ~= nil then held[entry.id] = false end

            local entryData = findAddedEntry(entry.button)
            if entryData and not wasDrag then
                if entryData.isInteract then
                    for prompt in pairs(heldPrompts) do
                        heldPrompts[prompt] = nil
                        task.spawn(function()
                            pcall(function() prompt:InputHoldEnd() end)
                        end)
                    end
                elseif entryData.isMouse then
                    VIM:SendMouseButtonEvent(0, 0, entryData.code.Value, false, game, 0)
                else
                    VIM:SendKeyEvent(false, entryData.code, false, game)
                end
            end
        end

        pressState[input] = nil
    end

    if dragState and input == dragState.input then
        dragState = nil
    end
end)

----------------------------------------------------------------
-- SETUP UI
----------------------------------------------------------------
local setupUI = Instance.new("Frame")
setupUI.Name = "SetupUI"
setupUI.BackgroundTransparency = 1
setupUI.Size = UDim2.new(1, 0, 0, 120)
setupUI.Position = UDim2.new(0, 0, 0, 0)
setupUI.ZIndex = 10
setupUI.Parent = gui

local hint = Instance.new("TextLabel")
hint.Text = "Drag to position · Tap + to add · Tap DONE to lock"
hint.Size = UDim2.new(0.9, 0, 0, 28)
hint.Position = UDim2.new(0.5, 0, 0, 15)
hint.AnchorPoint = Vector2.new(0.5, 0)
hint.BackgroundTransparency = 1
hint.TextColor3 = Color3.fromRGB(230, 230, 240)
hint.Font = Enum.Font.GothamBold
hint.TextSize = isMobile and 15 or 14
hint.ZIndex = 10
hint.Parent = setupUI

local function makeBtn(text, pos, color, parent)
    local b = Instance.new("TextButton")
    b.Text = text
    b.Size = UDim2.fromOffset(130, 44)
    b.Position = pos
    b.AnchorPoint = Vector2.new(0.5, 0.5)
    b.BackgroundColor3 = color
    b.TextColor3 = Color3.fromRGB(255, 255, 255)
    b.Font = Enum.Font.GothamBold
    b.TextSize = 18
    b.ZIndex = 11
    b.Parent = parent or setupUI

    local c = Instance.new("UICorner")
    c.CornerRadius = UDim.new(0, 10)
    c.Parent = b

    return b
end

local addBtn  = makeBtn("+ ADD",  UDim2.new(0.5, -160, 0, 60), CONFIG.PanelAccent)
local saveBtn = makeBtn("SAVE",   UDim2.new(0.5,      0, 0, 60), CONFIG.PanelAccent)
local doneBtn = makeBtn("DONE",   UDim2.new(0.5,  160, 0, 60), CONFIG.PanelGreen)

----------------------------------------------------------------
-- KEY PICKER
----------------------------------------------------------------
local function openKeyPicker()
    local picker = Instance.new("Frame")
    picker.Size = UDim2.new(0.9, 0, 0.7, 0)
    picker.Position = UDim2.fromScale(0.5, 0.5)
    picker.AnchorPoint = Vector2.new(0.5, 0.5)
    picker.BackgroundColor3 = CONFIG.PanelColor
    picker.ZIndex = 20
    picker.Parent = gui

    local pc = Instance.new("UICorner")
    pc.CornerRadius = UDim.new(0, 14)
    pc.Parent = picker

    local title = Instance.new("TextLabel")
    title.Text = "Pick a key to add"
    title.Size = UDim2.new(1, 0, 0, 40)
    title.BackgroundTransparency = 1
    title.TextColor3 = Color3.fromRGB(230, 230, 240)
    title.Font = Enum.Font.GothamBold
    title.TextSize = 18
    title.ZIndex = 21
    title.Parent = picker

    local close = Instance.new("TextB

Features & Information

This script is one of the best mobile pc script it like tells roblox ur on pc and ect This script has that other dont: -fixed proximity prompt -Layouts -saves(if your executor supports saving) -slots PLSSS DONT REPOST THIS Warning it loads after 5-10 secs and execute your clipboard bcs it doesnt work with other methods

Community Comments (0)

No comments yet for this script.

More Scripts for Universal Scripts