Key features: • Reliable NPC detection (using DescendantAdded/Removed + initial scan) • Player-to-NPC distance used for color + thresholds • Name | Distance | Health billboard text (up to 5000 studs) • Highlight outline (up to 2000 studs) • Select Mode: click to toggle selection (multi-select); double-click to unselect • Changeable keybind (default T) to toggle global ESP on/off • Modern draggable GUI • Optimized lazy visuals to reduce lag ]] local Players = game:GetService("Players") local UIS = game:GetService("UserInputService") local RS = game:GetService("RunService") local Workspace = workspace local LocalPlayer = Players.LocalPlayer local Mouse = LocalPlayer:GetMouse() -- Config local TEXT_MAX_DISTANCE = 5000 local OUTLINE_MAX_DISTANCE = 2000 local SELECT_CLICK_MAX_STUDS = 300 -- how far clicking may select local SELECT_CLICK_MAX_SCREEN_DIST = 80 -- pixels threshold to mouse when selecting by screen pos -- State local ESPKey = Enum.KeyCode.T local awaitingKey = false local espEnabled = true -- global ESP toggle local selectMode = false -- when true, clicking toggles selection; you can select many local selectedSet = {} -- [model] = true for forced selection local trackedNPCs = {} -- [model] = true for candidate NPCs (humanoid, not player) local ESPS = {} -- [model] = {highlight, billboard, label, created = true} -- Utility functions local function isPlayerCharacter(model) if not model or not model:IsA("Model") then return false end return Players:GetPlayerFromCharacter(model) ~= nil end local function isNPCModel(model) if not model or not model:IsA("Model") then return false end if isPlayerCharacter(model) then return false end local hum = model:FindFirstChildOfClass("Humanoid") return hum ~= nil end -- GUI creation (modern draggable) local gui = Instance.new("ScreenGui") gui.ResetOnSpawn = false gui.Parent = LocalPlayer:WaitForChild("PlayerGui") local frame = Instance.new("Frame") frame.Size = UDim2.new(0, 300, 0, 170) frame.Position = UDim2.new(0.05, 0, 0.18, 0) frame.BackgroundColor3 = Color3.fromRGB(28,28,28) frame.BorderSizePixel = 0 frame.Parent = gui frame.Active = true local frameCorner = Instance.new("UICorner", frame) frameCorner.CornerRadius = UDim.new(0, 12) local title = Instance.new("TextLabel", frame) title.Size = UDim2.new(1, 0, 0, 32) title.Position = UDim2.new(0, 0, 0, 0) title.BackgroundColor3 = Color3.fromRGB(22,22,22) title.Text = "NPC ESP" title.TextColor3 = Color3.fromRGB(255,255,255) title.Font = Enum.Font.GothamBold title.TextSize = 18 Instance.new("UICorner", title).CornerRadius = UDim.new(0, 12) -- Dragging logic do local dragging, dragStart, startPos = false, nil, nil title.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragStart = input.Position startPos = frame.Position end end) title.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end end) UIS.InputChanged:Connect(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) end -- Helper to create consistent buttons local function newButton(text, y) local b = Instance.new("TextButton", frame) b.Size = UDim2.new(1, -20, 0, 28) b.Position = UDim2.new(0, 10, 0, y) b.BackgroundColor3 = Color3.fromRGB(45,45,45) b.TextColor3 = Color3.fromRGB(255,255,255) b.Font = Enum.Font.Gotham b.TextSize = 15 b.Text = text Instance.new("UICorner", b).CornerRadius = UDim.new(0,8) return b end local changeKeyButton = newButton("Keybind: Change", 38) local keyLabel = Instance.new("TextLabel", frame) keyLabel.Size = UDim2.new(1, -20, 0, 18) keyLabel.Position = UDim2.new(0, 10, 0, 72) keyLabel.BackgroundTransparency = 1 keyLabel.Font = Enum.Font.Gotham keyLabel.TextSize = 14 keyLabel.TextColor3 = Color3.fromRGB(200,200,200) keyLabel.Text = "Current Key: " .. ESPKey.Name local selectModeButton = newButton("Select Mode: OFF", 95) local toggleESPButton = newButton("ESP All NPCs: ON", 130) -- Keybind logic changeKeyButton.MouseButton1Click:Connect(function() awaitingKey = true changeKeyButton.Text = "Press any key..." end) UIS.InputBegan:Connect(function(input, gameProcessed) if awaitingKey and input.KeyCode ~= Enum.KeyCode.Unknown then ESPKey = input.KeyCode keyLabel.Text = "Current Key: " .. ESPKey.Name changeKeyButton.Text = "Keybind: Change" awaitingKey = false return end if input.KeyCode == ESPKey then espEnabled = not espEnabled toggleESPButton.Text = espEnabled and "ESP All NPCs: ON" or "ESP All NPCs: OFF" end end) selectModeButton.MouseButton1Click:Connect(function() selectMode = not selectMode selectModeButton.Text = selectMode and "Select Mode: ON" or "Select Mode: OFF" end) toggleESPButton.MouseButton1Click:Connect(function() espEnabled = not espEnabled toggleESPButton.Text = espEnabled and "ESP All NPCs: ON" or "ESP All NPCs: OFF" end) -- Detection: keep trackedNPCs up-to-date using DescendantAdded/Removed + initial scan local function tryAddTracked(model) if isNPCModel(model) and not trackedNPCs[model] then trackedNPCs[model] = true end end local function tryRemoveTracked(model) if trackedNPCs[model] then trackedNPCs[model] = nil end if selectedSet[model] then selectedSet[model] = nil end if ESPS[model] then local d = ESPS[model] if d.highlight and d.highlight.Parent then d.highlight:Destroy() end if d.billboard and d.billboard.Parent then d.billboard:Destroy() end ESPS[model] = nil end end for _, obj in ipairs(Workspace:GetDescendants()) do if isNPCModel(obj) then trackedNPCs[obj] = true end end Workspace.DescendantAdded:Connect(function(desc) if desc:IsA("Humanoid") then local model = desc.Parent if isNPCModel(model) then trackedNPCs[model] = true end return end if desc:IsA("Model") and isNPCModel(desc) then trackedNPCs[desc] = true end end) Workspace.DescendantRemoving:Connect(function(desc) if desc:IsA("Humanoid") and desc.Parent then tryRemoveTracked(desc.Parent) return end if desc:IsA("Model") then tryRemoveTracked(desc) end end) -- Helper: creates the visual objects (Highlight + Billboard) for an NPC local function createESPVisuals(npc) if ESPS[npc] then return ESPS[npc] end if not npc or not npc.Parent then return nil end local hum = npc:FindFirstChildOfClass("Humanoid") local head = npc:FindFirstChild("Head") or npc:FindFirstChild("head") or (npc.PrimaryPart and npc.PrimaryPart) if not hum or not head then return nil end local h = Instance.new("Highlight") h.Parent = npc h.FillTransparency = 1 h.OutlineTransparency = 0 h.Enabled = false h.OutlineColor = Color3.new(1,1,1) local bg = Instance.new("BillboardGui") bg.Parent = npc bg.Adornee = head bg.Size = UDim2.new(0, 240, 0, 60) bg.StudsOffset = Vector3.new(0, 2.5, 0) bg.AlwaysOnTop = true bg.Enabled = false local label = Instance.new("TextLabel", bg) label.Size = UDim2.new(1,0,1,0) label.BackgroundTransparency = 1 label.Font = Enum.Font.GothamBold label.TextSize = 16 label.TextColor3 = Color3.new(1,1,1) label.TextStrokeTransparency = 0.8 label.Text = "" ESPS[npc] = { highlight = h, billboard = bg, label = label } return ESPS[npc] end local function destroyESPVisuals(npc) local d = ESPS[npc] if not d then return end if d.highlight and d.highlight.Parent then d.highlight:Destroy() end if d.billboard and d.billboard.Parent then d.billboard:Destroy() end ESPS[npc] = nil end local function getColor(dist) if dist >= 65 then return Color3.fromRGB(0,255,0) elseif dist <= 55 then return Color3.fromRGB(255,0,0) elseif dist <= 60 then return Color3.fromRGB(255,255,0) else return Color3.fromRGB(255,165,0) end end -- Click handling for selection (multi-select, works through walls) local function handleClickToggleSelection() if not selectMode then return end -- <-- Only allow selection when Select Mode is ON local playerRoot = LocalPlayer.Character and (LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or LocalPlayer.Character.PrimaryPart) if not playerRoot then return end local bestModel = nil local bestScreenDist = SELECT_CLICK_MAX_SCREEN_DIST + 1 local bestWorldDist = math.huge local cam = Workspace.CurrentCamera if not cam then return end for npcModel,_ in pairs(trackedNPCs) do if npcModel and npcModel.Parent then local head = npcModel:FindFirstChild("Head") or npcModel:FindFirstChild("head") or npcModel.PrimaryPart local hum = npcModel:FindFirstChildOfClass("Humanoid") if head and hum then local worldPos = head.Position local worldDist = (playerRoot.Position - worldPos).Magnitude if worldDist <= SELECT_CLICK_MAX_STUDS then local screenPos, onScreen = cam:WorldToViewportPoint(worldPos) if onScreen then local screenVec = Vector2.new(screenPos.X, screenPos.Y) local mouseVec = Vector2.new(Mouse.X, Mouse.Y) local sd = (screenVec - mouseVec).Magnitude if sd < bestScreenDist or (sd == bestScreenDist and worldDist < bestWorldDist) then bestScreenDist = sd bestModel = npcModel bestWorldDist = worldDist end end end end end end if bestModel then if selectedSet[bestModel] then selectedSet[bestModel] = nil destroyESPVisuals(bestModel) else selectedSet[bestModel] = true createESPVisuals(bestModel) end end end Mouse.Button1Down:Connect(function() handleClickToggleSelection() end) -- Main per-frame update RS.RenderStepped:Connect(function() local char = LocalPlayer.Character local playerRoot = char and (char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart) if not playerRoot then for npc,d in pairs(ESPS) do if d.billboard then d.billboard.Enabled = false end if d.highlight then d.highlight.Enabled = false end end return end for npc,_ in pairs(trackedNPCs) do if not npc or not npc.Parent then trackedNPCs[npc] = nil selectedSet[npc] = nil destroyESPVisuals(npc) else local hum = npc:FindFirstChildOfClass("Humanoid") local head = npc:FindFirstChild("Head") or npc:FindFirstChild("head") or npc.PrimaryPart if not hum or not head then trackedNPCs[npc] = nil selectedSet[npc] = nil destroyESPVisuals(npc) else local dist = (playerRoot.Position - head.Position).Magnitude local shouldShowText = (dist <= TEXT_MAX_DISTANCE) and (espEnabled) local shouldShowOutline = (dist <= OUTLINE_MAX_DISTANCE) and (espEnabled) if selectedSet[npc] then local v = createESPVisuals(npc) if v and v.billboard then v.billboard.Enabled = true v.label.Text = string.format("Name: %s | Distance: %d studs | Health: %d", npc.Name, math.floor(dist), math.floor(hum.Health or 0)) v.label.TextColor3 = getColor(dist) end if v and v.highlight then v.highlight.Enabled = true v.highlight.OutlineColor = getColor(dist) end else if espEnabled and (dist <= TEXT_MAX_DISTANCE or dist <= OUTLINE_MAX_DISTANCE) then local v = createESPVisuals(npc) if v then if dist <= TEXT_MAX_DISTANCE then v.billboard.Enabled = true v.label.Text = string.format("Name: %s | Distance: %d studs | Health: %d", npc.Name, math.floor(dist), math.floor(hum.Health or 0)) v.label.TextColor3 = getColor(dist) else v.billboard.Enabled = false end if dist <= OUTLINE_MAX_DISTANCE then v.highlight.Enabled = true v.highlight.OutlineColor = getColor(dist) else v.highlight.Enabled = false end end else if ESPS[npc] then local v = ESPS[npc] if v.billboard then v.billboard.Enabled = false end if v.highlight then v.highlight.Enabled = false end end end end end end end for npc,d in pairs(ESPS) do if not trackedNPCs[npc] then destroyESPVisuals(npc) end end end) LocalPlayer.CharacterRemoving:Connect(function() for npc,d in pairs(ESPS) do if d.billboard then d.billboard.Enabled = false end if d.highlight then d.highlight.Enabled = false end end end) print("[NPC ESP] Loaded with Select Mode fix, multi-select & double-click to unselect support.")