-- AimAssistMenu v2 (LocalScript) -- Place in StarterPlayer > StarterPlayerScripts inside YOUR OWN game in Roblox Studio. -- RightShift (or the gear button on mobile) opens/closes the menu. -- Aim: hold Right Mouse / Left Trigger (gamepad) / AIM button (mobile), or set Mode to "Always". local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local player = Players.LocalPlayer ---------------------------------------------------------------- -- Settings ---------------------------------------------------------------- local Settings = { Enabled = true, Mode = "Hold", -- "Hold" or "Always" FOV = 150, -- radius in pixels Smoothness = 0.60, -- 0.05 slow ... 1.00 instant (hard lock) TargetNPCs = true, -- also lock onto dummies / NPCs (not just players) Prediction = 0.00, -- seconds of lead for moving targets TargetPart = "Head", -- "Head" or "HumanoidRootPart" TeamCheck = true, WallCheck = true, Sticky = true, -- keep current target until it leaves FOV or dies ShowFOV = true, } ---------------------------------------------------------------- -- UI ---------------------------------------------------------------- local PURPLE = Color3.fromRGB(140, 70, 255) local DARK = Color3.fromRGB(15, 15, 18) local ROW = Color3.fromRGB(28, 28, 34) local OFF = Color3.fromRGB(70, 70, 80) local gui = Instance.new("ScreenGui") gui.Name = "AimAssistMenu" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true -- GUI coords now match viewport coords gui.DisplayOrder = 100 gui.Parent = player:WaitForChild("PlayerGui") local panel = Instance.new("Frame") panel.AutomaticSize = Enum.AutomaticSize.Y panel.Size = UDim2.fromOffset(270, 0) panel.Position = UDim2.new(0, 20, 0.5, -200) panel.BackgroundColor3 = DARK panel.BorderSizePixel = 0 panel.Active = true panel.Draggable = true panel.Parent = gui Instance.new("UICorner", panel).CornerRadius = UDim.new(0, 10) local stroke = Instance.new("UIStroke", panel) stroke.Color = PURPLE stroke.Thickness = 2 local pad = Instance.new("UIPadding", panel) pad.PaddingTop, pad.PaddingBottom = UDim.new(0, 8), UDim.new(0, 10) pad.PaddingLeft, pad.PaddingRight = UDim.new(0, 10), UDim.new(0, 10) local layout = Instance.new("UIListLayout", panel) layout.Padding = UDim.new(0, 6) layout.SortOrder = Enum.SortOrder.LayoutOrder local title = Instance.new("TextLabel") title.Size = UDim2.new(1, 0, 0, 24) title.BackgroundTransparency = 1 title.Text = "AIM ASSIST" title.Font = Enum.Font.GothamBold title.TextSize = 16 title.TextColor3 = PURPLE title.LayoutOrder = 0 title.Parent = panel local order = 0 local function makeRow(height) order += 1 local row = Instance.new("Frame") row.Size = UDim2.new(1, 0, 0, height) row.BackgroundColor3 = ROW row.BorderSizePixel = 0 row.LayoutOrder = order row.Parent = panel Instance.new("UICorner", row).CornerRadius = UDim.new(0, 6) return row end local function makeLabel(parent, text, size, align) local l = Instance.new("TextLabel") l.BackgroundTransparency = 1 l.Size = size l.Position = UDim2.fromOffset(8, 0) l.TextXAlignment = align or Enum.TextXAlignment.Left l.Text = text l.Font = Enum.Font.Gotham l.TextSize = 13 l.TextColor3 = Color3.new(1, 1, 1) l.Parent = parent return l end local function makeButton(parent, text, width) local b = Instance.new("TextButton") b.Size = UDim2.fromOffset(width, 24) b.AnchorPoint = Vector2.new(1, 0.5) b.Position = UDim2.new(1, -6, 0.5, 0) b.BackgroundColor3 = PURPLE b.BorderSizePixel = 0 b.Text = text b.Font = Enum.Font.GothamBold b.TextSize = 12 b.TextColor3 = Color3.new(1, 1, 1) b.AutoButtonColor = true b.Parent = parent Instance.new("UICorner", b).CornerRadius = UDim.new(0, 5) return b end local function addToggle(name, key) local row = makeRow(34) makeLabel(row, name, UDim2.new(0.6, 0, 1, 0)) local b = makeButton(row, "", 60) local function refresh() b.Text = Settings[key] and "ON" or "OFF" b.BackgroundColor3 = Settings[key] and PURPLE or OFF end b.Activated:Connect(function() Settings[key] = not Settings[key] refresh() end) refresh() end local function addCycle(name, key, options) local row = makeRow(34) makeLabel(row, name, UDim2.new(0.5, 0, 1, 0)) local b = makeButton(row, Settings[key], 110) b.Activated:Connect(function() local i = table.find(options, Settings[key]) or 1 Settings[key] = options[i % #options + 1] b.Text = Settings[key] end) end local function addSlider(name, key, min, max, fmt) local row = makeRow(48) makeLabel(row, name, UDim2.new(0.6, 0, 0, 24)) local valueLabel = makeLabel(row, "", UDim2.new(1, -16, 0, 24), Enum.TextXAlignment.Right) local track = Instance.new("Frame") track.Size = UDim2.new(1, -20, 0, 6) track.Position = UDim2.new(0, 10, 0, 33) track.BackgroundColor3 = OFF track.BorderSizePixel = 0 track.Parent = row Instance.new("UICorner", track).CornerRadius = UDim.new(1, 0) local fill = Instance.new("Frame") fill.BackgroundColor3 = PURPLE fill.BorderSizePixel = 0 fill.Parent = track Instance.new("UICorner", fill).CornerRadius = UDim.new(1, 0) -- larger invisible hit area so it is easy to grab local hit = Instance.new("TextButton") hit.BackgroundTransparency = 1 hit.Text = "" hit.Size = UDim2.new(1, 0, 0, 24) hit.Position = UDim2.fromOffset(0, -9) hit.Parent = track local function setFromAlpha(a) a = math.clamp(a, 0, 1) Settings[key] = min + (max - min) * a fill.Size = UDim2.new(a, 0, 1, 0) valueLabel.Text = string.format(fmt, Settings[key]) end setFromAlpha((Settings[key] - min) / (max - min)) local dragging = false local function update(x) setFromAlpha((x - track.AbsolutePosition.X) / track.AbsoluteSize.X) end hit.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true panel.Draggable = false update(input.Position.X) end end) UserInputService.InputChanged:Connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then update(input.Position.X) end end) UserInputService.InputEnded:Connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then dragging = false panel.Draggable = true end end) end addToggle("Enabled", "Enabled") addCycle("Mode", "Mode", { "Hold", "Always" }) addSlider("FOV", "FOV", 30, 500, "%d px") addSlider("Smoothness", "Smoothness", 0.05, 1, "%.2f") addSlider("Prediction", "Prediction", 0, 0.3, "%.2f s") addCycle("Target Part", "TargetPart", { "Head", "HumanoidRootPart" }) addToggle("Target NPCs", "TargetNPCs") addToggle("Team Check", "TeamCheck") addToggle("Wall Check", "WallCheck") addToggle("Sticky Target", "Sticky") addToggle("Show FOV", "ShowFOV") UserInputService.InputBegan:Connect(function(input, processed) if not processed and input.KeyCode == Enum.KeyCode.RightShift then panel.Visible = not panel.Visible end end) -- FOV circle local circle = Instance.new("Frame") circle.AnchorPoint = Vector2.new(0.5, 0.5) circle.BackgroundTransparency = 1 circle.Parent = gui Instance.new("UICorner", circle).CornerRadius = UDim.new(1, 0) local circleStroke = Instance.new("UIStroke", circle) circleStroke.Color = PURPLE circleStroke.Thickness = 1.5 -- Mobile: gear button + hold-to-aim button local touchAiming = false if UserInputService.TouchEnabled then local gear = Instance.new("TextButton") gear.Size = UDim2.fromOffset(44, 44) gear.Position = UDim2.new(1, -60, 0, 60) gear.BackgroundColor3 = DARK gear.Text = "⚙" gear.TextSize = 24 gear.TextColor3 = PURPLE gear.Parent = gui Instance.new("UICorner", gear).CornerRadius = UDim.new(1, 0) gear.Activated:Connect(function() panel.Visible = not panel.Visible end) panel.Visible = false local aim = Instance.new("TextButton") aim.Size = UDim2.fromOffset(70, 70) aim.Position = UDim2.new(1, -170, 1, -170) aim.BackgroundColor3 = PURPLE aim.BackgroundTransparency = 0.3 aim.Text = "AIM" aim.Font = Enum.Font.GothamBold aim.TextSize = 16 aim.TextColor3 = Color3.new(1, 1, 1) aim.Parent = gui Instance.new("UICorner", aim).CornerRadius = UDim.new(1, 0) aim.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch then touchAiming = true end end) aim.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch then touchAiming = false end end) end ---------------------------------------------------------------- -- Aim assist logic ---------------------------------------------------------------- local currentTarget = nil -- BasePart local function isAimHeld() if Settings.Mode == "Always" then return true end return UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) or UserInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonL2) or touchAiming end local function getAimOrigin(camera) -- Mouse position normally; screen center when the mouse is locked or on gamepad local lastInput = UserInputService:GetLastInputType() if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter or lastInput == Enum.UserInputType.Gamepad1 or lastInput == Enum.UserInputType.Touch then return camera.ViewportSize / 2 end return UserInputService:GetMouseLocation() end -- returns the 2D distance from the aim origin if this part is a valid target, else nil local function evaluate(part, camera, origin, maxDist) if not part or not part.Parent then return nil end local char = part.Parent local hum = char:FindFirstChildOfClass("Humanoid") if not hum or hum.Health <= 0 then return nil end if char == player.Character then return nil end local owner = Players:GetPlayerFromCharacter(char) if not owner and not Settings.TargetNPCs then return nil end if owner and Settings.TeamCheck and player.Team ~= nil and owner.Team == player.Team then return nil end local worldPos = part.Position + part.AssemblyLinearVelocity * Settings.Prediction local pos, onScreen = camera:WorldToViewportPoint(worldPos) if not onScreen then return nil end local dist = (Vector2.new(pos.X, pos.Y) - origin).Magnitude if dist > maxDist then return nil end if Settings.WallCheck then local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Exclude params.FilterDescendantsInstances = { player.Character, char } params.RespectCanCollide = true local from = camera.CFrame.Position if workspace:Raycast(from, worldPos - from, params) then return nil end end return dist end -- Cached list of character models (players + NPCs), refreshed once a second local candidates, lastScan = {}, 0 local function getCandidates() if os.clock() - lastScan < 1 then return candidates end lastScan = os.clock() candidates = {} for _, other in ipairs(Players:GetPlayers()) do if other ~= player and other.Character then table.insert(candidates, other.Character) end end if Settings.TargetNPCs then for _, inst in ipairs(workspace:GetDescendants()) do if inst:IsA("Humanoid") then local model = inst.Parent if model and model:IsA("Model") and model ~= player.Character and not Players:GetPlayerFromCharacter(model) then table.insert(candidates, model) end end end end return candidates end local function findTarget(camera, origin) local best, bestDist = nil, Settings.FOV for _, model in ipairs(getCandidates()) do local part = model:FindFirstChild(Settings.TargetPart) local d = evaluate(part, camera, origin, bestDist) if d and d < bestDist then best, bestDist = part, d end end return best end -- Runs right AFTER the default camera script, otherwise the default camera overwrites our aim every frame RunService:BindToRenderStep("AimAssist", Enum.RenderPriority.Camera.Value + 1, function(dt) local camera = workspace.CurrentCamera if not camera then return end local origin = getAimOrigin(camera) circle.Visible = Settings.ShowFOV and Settings.Enabled circle.Size = UDim2.fromOffset(Settings.FOV * 2, Settings.FOV * 2) circle.Position = UDim2.fromOffset(origin.X, origin.Y) if not (Settings.Enabled and isAimHeld()) then currentTarget = nil return end -- Sticky: keep the current target while it stays valid (slightly larger FOV so it doesn't flicker) if currentTarget and not (Settings.Sticky and evaluate(currentTarget, camera, origin, Settings.FOV * 1.25) and currentTarget.Name == Settings.TargetPart) then currentTarget = nil end if not currentTarget then currentTarget = findTarget(camera, origin) end if not currentTarget then return end local aimPos = currentTarget.Position + currentTarget.AssemblyLinearVelocity * Settings.Prediction local goal = CFrame.new(camera.CFrame.Position, aimPos) -- frame-rate independent smoothing so it feels the same at 30, 60 or 144 FPS local alpha = 1 - (1 - Settings.Smoothness) ^ (dt * 60) camera.CFrame = camera.CFrame:Lerp(goal, math.clamp(alpha, 0, 1)) end)