-- Aim Assist (for YOUR Roblox game) + GUI + Target Dropdown (fixed layering + stays open) -- StarterPlayerScripts (LocalScript) local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local localPlayer = Players.LocalPlayer local camera = workspace.CurrentCamera -- ========================= -- KEYBINDS (CHANGE ONLY HERE) -- ========================= local KEY_TOGGLE_UI = Enum.KeyCode.P local KEY_DELETE_UI = Enum.KeyCode.M -- ========================= -- SETTINGS -- ========================= local Settings = { Enabled = true, MaxFOV = 8, -- degrees Smoothing = 0.18, -- 0..1 LeadTime = 0.12, -- seconds MaxDistance = 600, -- studs TargetPartName = "HumanoidRootPart", -- or "Head" RequireRightMouse = false, } -- Target selection local SelectedTargetUserId: number? = nil -- nil = Auto (closest in FOV) -- ========================= -- Helpers -- ========================= local function clampNumber(n, min, max) n = tonumber(n) if not n then return nil end if n < min then return min end if n > max then return max end return n end local function safeGetCharacter(plr) if plr and plr.Character and plr.Character.Parent then return plr.Character end end local function getTargetPart(char) return char:FindFirstChild(Settings.TargetPartName) or char:FindFirstChild("Head") end local function angleBetween(a, b) local d = math.clamp(a:Dot(b), -1, 1) return math.deg(math.acos(d)) end local function isAssistActive() if not Settings.Enabled then return false end if not Settings.RequireRightMouse then return true end return UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) end local function isValidTarget(plr, part) if not plr or plr == localPlayer then return false end if not part then return false end local hum = part.Parent and part.Parent:FindFirstChildOfClass("Humanoid") return hum and hum.Health > 0 end local function getPlayerByUserId(userId: number?) if not userId then return nil end for _, p in ipairs(Players:GetPlayers()) do if p.UserId == userId then return p end end return nil end local function predictAimPoint(targetPart) local pos = targetPart.Position local v = targetPart.AssemblyLinearVelocity or Vector3.zero return pos + (v * Settings.LeadTime) end -- ========================= -- GUI + Connections -- ========================= local connections = {} local destroyed = false local gui: ScreenGui? = nil local frame: Frame? = nil local dropdownList: Frame? = nil local dropdownBtn: TextButton? = nil local dropdownOpen = false local function connect(sig, fn) local c = sig:Connect(fn) table.insert(connections, c) return c end local function disconnectAll() for _, c in ipairs(connections) do pcall(function() c:Disconnect() end) end table.clear(connections) end local function setUIVisible(visible: boolean) if frame and frame.Parent then frame.Visible = visible if not visible and dropdownList then dropdownList.Visible = false dropdownOpen = false end end end local function toggleUI() if frame and frame.Parent then setUIVisible(not frame.Visible) end end local function deleteUI() if destroyed then return end destroyed = true disconnectAll() if gui then pcall(function() gui:Destroy() end) end gui, frame, dropdownList, dropdownBtn = nil, nil, nil, nil end local function createGUI() gui = Instance.new("ScreenGui") gui.Name = "AimAssistGUI" gui.ResetOnSpawn = false gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling -- IMPORTANT: lets ZIndex work across siblings gui.Parent = localPlayer:WaitForChild("PlayerGui") frame = Instance.new("Frame") frame.Name = "Panel" frame.Size = UDim2.new(0, 370, 0, 280) frame.Position = UDim2.new(0, 20, 0, 120) frame.BackgroundTransparency = 0.15 frame.BorderSizePixel = 0 frame.ZIndex = 1 frame.Parent = gui local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 12) corner.Parent = frame local stroke = Instance.new("UIStroke") stroke.Thickness = 1 stroke.Transparency = 0.4 stroke.Parent = frame local title = Instance.new("TextLabel") title.Size = UDim2.new(1, -46, 0, 28) title.Position = UDim2.new(0, 8, 0, 6) title.BackgroundTransparency = 1 title.TextXAlignment = Enum.TextXAlignment.Left title.Font = Enum.Font.GothamBold title.TextSize = 16 title.Text = "Aim Assist (Owner Tools)" title.ZIndex = 2 title.Parent = frame local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.new(0, 28, 0, 28) closeBtn.Position = UDim2.new(1, -34, 0, 6) closeBtn.BorderSizePixel = 0 closeBtn.BackgroundTransparency = 0.05 closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = 16 closeBtn.Text = "X" closeBtn.ZIndex = 2 closeBtn.Parent = frame local closeCorner = Instance.new("UICorner") closeCorner.CornerRadius = UDim.new(0, 8) closeCorner.Parent = closeBtn local hint = Instance.new("TextLabel") hint.Size = UDim2.new(1, -12, 0, 18) hint.Position = UDim2.new(0, 8, 0, 34) hint.BackgroundTransparency = 1 hint.TextXAlignment = Enum.TextXAlignment.Left hint.Font = Enum.Font.Gotham hint.TextSize = 12 hint.TextTransparency = 0.2 hint.Text = ("Keys: %s hide/show, %s delete UI. Dropdown refreshes every 10s.") :format(KEY_TOGGLE_UI.Name, KEY_DELETE_UI.Name) hint.ZIndex = 2 hint.Parent = frame local function row(y, labelText, defaultText) local label = Instance.new("TextLabel") label.Size = UDim2.new(0, 180, 0, 26) label.Position = UDim2.new(0, 8, 0, y) label.BackgroundTransparency = 1 label.TextXAlignment = Enum.TextXAlignment.Left label.Font = Enum.Font.Gotham label.TextSize = 13 label.Text = labelText label.ZIndex = 2 label.Parent = frame local box = Instance.new("TextBox") box.Size = UDim2.new(0, 160, 0, 26) box.Position = UDim2.new(0, 200, 0, y) box.BackgroundTransparency = 0.05 box.BorderSizePixel = 0 box.ClearTextOnFocus = false box.Font = Enum.Font.Gotham box.TextSize = 13 box.Text = defaultText box.ZIndex = 2 box.Parent = frame local boxCorner = Instance.new("UICorner") boxCorner.CornerRadius = UDim.new(0, 8) boxCorner.Parent = box local boxStroke = Instance.new("UIStroke") boxStroke.Thickness = 1 boxStroke.Transparency = 0.6 boxStroke.Parent = box return box end -- Enabled toggle local enabledBtn = Instance.new("TextButton") enabledBtn.Size = UDim2.new(0, 160, 0, 28) enabledBtn.Position = UDim2.new(0, 200, 0, 58) enabledBtn.BorderSizePixel = 0 enabledBtn.Font = Enum.Font.GothamBold enabledBtn.TextSize = 13 enabledBtn.ZIndex = 2 enabledBtn.Parent = frame local btnCorner = Instance.new("UICorner") btnCorner.CornerRadius = UDim.new(0, 8) btnCorner.Parent = enabledBtn local function refreshEnabledButton() enabledBtn.Text = Settings.Enabled and "Enabled: ON" or "Enabled: OFF" enabledBtn.BackgroundTransparency = Settings.Enabled and 0.05 or 0.2 end refreshEnabledButton() -- Dropdown label local ddLabel = Instance.new("TextLabel") ddLabel.Size = UDim2.new(0, 180, 0, 26) ddLabel.Position = UDim2.new(0, 8, 0, 92) ddLabel.BackgroundTransparency = 1 ddLabel.TextXAlignment = Enum.TextXAlignment.Left ddLabel.Font = Enum.Font.Gotham ddLabel.TextSize = 13 ddLabel.Text = "Target (dropdown)" ddLabel.ZIndex = 2 ddLabel.Parent = frame -- Dropdown button (base layer) dropdownBtn = Instance.new("TextButton") dropdownBtn.Size = UDim2.new(0, 160, 0, 26) dropdownBtn.Position = UDim2.new(0, 200, 0, 92) dropdownBtn.BackgroundTransparency = 0.05 dropdownBtn.BorderSizePixel = 0 dropdownBtn.Font = Enum.Font.Gotham dropdownBtn.TextSize = 13 dropdownBtn.TextXAlignment = Enum.TextXAlignment.Left dropdownBtn.Text = " Auto (closest in FOV) ▾" dropdownBtn.ZIndex = 5 dropdownBtn.Parent = frame local ddCorner = Instance.new("UICorner") ddCorner.CornerRadius = UDim.new(0, 8) ddCorner.Parent = dropdownBtn local ddStroke = Instance.new("UIStroke") ddStroke.Thickness = 1 ddStroke.Transparency = 0.6 ddStroke.Parent = dropdownBtn -- Dropdown list (HIGH ZINDEX so it overlays everything) dropdownList = Instance.new("Frame") dropdownList.Name = "DropdownList" dropdownList.Size = UDim2.new(0, 160, 0, 0) dropdownList.Position = UDim2.new(0, 200, 0, 120) dropdownList.BackgroundTransparency = 0.05 dropdownList.BorderSizePixel = 0 dropdownList.Visible = false dropdownList.ClipsDescendants = true dropdownList.ZIndex = 50 dropdownList.Parent = frame local listCorner = Instance.new("UICorner") listCorner.CornerRadius = UDim.new(0, 8) listCorner.Parent = dropdownList local listStroke = Instance.new("UIStroke") listStroke.Thickness = 1 listStroke.Transparency = 0.6 listStroke.Parent = dropdownList local uiList = Instance.new("UIListLayout") uiList.SortOrder = Enum.SortOrder.LayoutOrder uiList.Padding = UDim.new(0, 2) uiList.Parent = dropdownList local function setDropdownText(labelText) if dropdownBtn then dropdownBtn.Text = " " .. labelText .. " ▾" end end local function makeOption(text, onClick) local opt = Instance.new("TextButton") opt.Size = UDim2.new(1, -6, 0, 24) opt.BackgroundTransparency = 0.1 opt.BorderSizePixel = 0 opt.Font = Enum.Font.Gotham opt.TextSize = 13 opt.TextXAlignment = Enum.TextXAlignment.Left opt.Text = " " .. text opt.ZIndex = 51 opt.Parent = dropdownList local optCorner = Instance.new("UICorner") optCorner.CornerRadius = UDim.new(0, 6) optCorner.Parent = opt connect(opt.MouseButton1Click, onClick) return opt end -- IMPORTANT: Rebuild WITHOUT closing if it's open local function rebuildDropdownOptions() if not dropdownList or not dropdownList.Parent then return end -- keep open/size state local wasVisible = dropdownList.Visible local priorSize = dropdownList.Size -- clear old buttons only for _, c in ipairs(dropdownList:GetChildren()) do if c:IsA("TextButton") then c:Destroy() end end local options = {} table.insert(options, {name = "Auto (closest in FOV)", userId = nil}) for _, p in ipairs(Players:GetPlayers()) do if p ~= localPlayer then table.insert(options, {name = p.DisplayName .. " (@" .. p.Name .. ")", userId = p.UserId}) end end for i, opt in ipairs(options) do local btn = makeOption(opt.name, function() SelectedTargetUserId = opt.userId setDropdownText(opt.name) -- close on user selection (but NOT on auto-refresh) dropdownList.Visible = false dropdownOpen = false dropdownList.Size = UDim2.new(0, 160, 0, 0) end) btn.LayoutOrder = i end local rows = #options local h = math.min(rows * 26 + 4, 200) -- if it was open, keep it open and keep it expanded if wasVisible then dropdownList.Visible = true dropdownOpen = true dropdownList.Size = UDim2.new(0, 160, 0, h) else -- if it was closed, keep it closed (don’t pop open) dropdownList.Visible = false dropdownOpen = false dropdownList.Size = priorSize -- usually 0 height end end -- Dropdown click toggle connect(dropdownBtn.MouseButton1Click, function() if not dropdownList then return end dropdownOpen = not dropdownOpen dropdownList.Visible = dropdownOpen if dropdownOpen then rebuildDropdownOptions() else dropdownList.Size = UDim2.new(0, 160, 0, 0) end end) -- Auto refresh every 10 seconds (doesn't close it) task.spawn(function() while not destroyed do task.wait(10) if destroyed then break end rebuildDropdownOptions() end end) -- If selected target leaves, revert to Auto (and keep dropdown open state unchanged) connect(Players.PlayerRemoving, function(p) if SelectedTargetUserId and p.UserId == SelectedTargetUserId then SelectedTargetUserId = nil setDropdownText("Auto (closest in FOV)") end end) -- Numeric controls local fovBox = row(128, "Max FOV (degrees)", tostring(Settings.MaxFOV)) local smoothBox = row(158, "Smoothing (0..1)", tostring(Settings.Smoothing)) local leadBox = row(188, "Lead Time (seconds)", tostring(Settings.LeadTime)) local distBox = row(218, "Max Distance (studs)", tostring(Settings.MaxDistance)) connect(enabledBtn.MouseButton1Click, function() Settings.Enabled = not Settings.Enabled refreshEnabledButton() end) connect(fovBox.FocusLost, function() local v = clampNumber(fovBox.Text, 0.5, 60) if v then Settings.MaxFOV = v; fovBox.Text = tostring(v) else fovBox.Text = tostring(Settings.MaxFOV) end end) connect(smoothBox.FocusLost, function() local v = clampNumber(smoothBox.Text, 0, 1) if v then Settings.Smoothing = v; smoothBox.Text = tostring(v) else smoothBox.Text = tostring(Settings.Smoothing) end end) connect(leadBox.FocusLost, function() local v = clampNumber(leadBox.Text, 0, 1.5) if v then Settings.LeadTime = v; leadBox.Text = tostring(v) else leadBox.Text = tostring(Settings.LeadTime) end end) connect(distBox.FocusLost, function() local v = clampNumber(distBox.Text, 20, 5000) if v then Settings.MaxDistance = v; distBox.Text = tostring(v) else distBox.Text = tostring(Settings.MaxDistance) end end) -- Dragging local dragging = false local dragStart, startPos connect(title.InputBegan, function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragStart = input.Position startPos = frame.Position connect(input.Changed, function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end) end end) connect(UserInputService.InputChanged, function(input) if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Position - dragStart frame.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) -- X hides UI connect(closeBtn.MouseButton1Click, function() setUIVisible(false) end) -- Keybinds connect(UserInputService.InputBegan, function(input, gameProcessed) if gameProcessed then return end if input.KeyCode == KEY_TOGGLE_UI then toggleUI() elseif input.KeyCode == KEY_DELETE_UI then deleteUI() end end) end createGUI() -- ========================= -- Target selection + Aim logic -- ========================= local function getBestTargetAuto() local camPos = camera.CFrame.Position local camLook = camera.CFrame.LookVector.Unit local bestPart = nil local bestAngle = Settings.MaxFOV for _, plr in ipairs(Players:GetPlayers()) do if plr ~= localPlayer then local char = safeGetCharacter(plr) if char then local part = getTargetPart(char) if isValidTarget(plr, part) then local toTarget = (part.Position - camPos) local dist = toTarget.Magnitude if dist <= Settings.MaxDistance then local ang = angleBetween(camLook, toTarget.Unit) if ang < bestAngle then bestAngle = ang bestPart = part end end end end end end return bestPart end local function getLockedTargetPart() local plr = getPlayerByUserId(SelectedTargetUserId) if not plr then return nil end local char = safeGetCharacter(plr) if not char then return nil end local part = getTargetPart(char) if not isValidTarget(plr, part) then return nil end -- gating (no yanking) local camPos = camera.CFrame.Position local camLook = camera.CFrame.LookVector.Unit local toTarget = part.Position - camPos if toTarget.Magnitude > Settings.MaxDistance then return nil end if angleBetween(camLook, toTarget.Unit) > Settings.MaxFOV then return nil end return part end connect(RunService.RenderStepped, function() if destroyed then return end if not isAssistActive() then return end local targetPart if SelectedTargetUserId then targetPart = getLockedTargetPart() else targetPart = getBestTargetAuto() end if not targetPart then return end local camPos = camera.CFrame.Position local aimPoint = predictAimPoint(targetPart) local desiredDir = (aimPoint - camPos).Unit local currentDir = camera.CFrame.LookVector.Unit if angleBetween(currentDir, desiredDir) > Settings.MaxFOV then return end local newDir = currentDir:Lerp(desiredDir, Settings.Smoothing) camera.CFrame = CFrame.new(camPos, camPos + newDir) end)