Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- [[ DELTA RADAR V26: OPTIMIZED PLAYER/NPC RADAR ]] --
- -- Client-side LocalScript
- --
- -- V26 improvements:
- -- • Optimized radar update loop
- -- • 30 FPS radar refresh instead of Heartbeat workload
- -- • No xpcall inside the hot update loop
- -- • Cached character roots
- -- • Cached humanoids
- -- • Event-driven NPC detection
- -- • Lightweight NPC synchronization
- -- • Player/NPC mode toggle
- -- • Player respawn support
- -- • NPC spawn support
- -- • PlayerRemoving cleanup
- -- • Duplicate-dot prevention
- -- • Separate name/distance labels
- -- • Nearby-label collision handling
- -- • Labels hidden when excessively crowded
- -- • Horizontal distance only
- -- • Player-relative coordinates
- -- • Forward = radar top
- -- • Range validation
- -- • Radar resize
- -- • Dragging
- -- • Safe cleanup
- --
- -- Place this in a LocalScript.
- -----------------------------------------------------------
- -- SERVICES
- -----------------------------------------------------------
- local Players = game:GetService("Players")
- local RunService = game:GetService("RunService")
- local UserInputService = game:GetService("UserInputService")
- -----------------------------------------------------------
- -- CONFIGURATION
- -----------------------------------------------------------
- local RADAR_WIDTH = 130
- local RADAR_HEIGHT = 80
- local MAX_DISTANCE = 350
- local DOT_SIZE = 6
- -- Radar update rate.
- -- 30 updates per second is enough for a radar while
- -- considerably reducing GUI/property workload.
- local RADAR_UPDATE_RATE = 1 / 30
- -- Safety synchronization.
- local SAFETY_SCAN_RATE = 5
- local MIN_RADAR_SIZE = 80
- local MAX_RADAR_SIZE = 300
- local MIN_RANGE = 10
- local MAX_RANGE = 10000
- -- Label collision settings.
- local LABEL_COLLISION_DISTANCE = 18
- local LABEL_OFFSET = 9
- -- Maximum number of labels that can be displayed in a
- -- very crowded local area.
- local MAX_LABELS_PER_CLUSTER = 3
- -----------------------------------------------------------
- -- PLAYER
- -----------------------------------------------------------
- local player = Players.LocalPlayer
- local playerGui = player:WaitForChild("PlayerGui")
- -----------------------------------------------------------
- -- STATE
- -----------------------------------------------------------
- local activeDots = {}
- local playerConnections = {}
- local connections = {}
- local destroyed = false
- local dragging = false
- local dragStart = nil
- local startPos = nil
- local currentMode = "Players"
- local radarAccumulator = 0
- local localRoot = nil
- -- Cache target information.
- local targetCache = {}
- -----------------------------------------------------------
- -- SAFE CALL
- -----------------------------------------------------------
- local function safeCall(label, callback, ...)
- local success, result = xpcall(
- callback,
- function(errorMessage)
- return debug.traceback(
- "[" .. tostring(label) .. "] "
- .. tostring(errorMessage),
- 2
- )
- end,
- ...
- )
- if not success then
- warn(
- "[Delta Radar V26] "
- .. tostring(result)
- )
- return false, nil
- end
- return true, result
- end
- -----------------------------------------------------------
- -- CLEANUP OLD RADARS
- -----------------------------------------------------------
- for _, gui in ipairs(playerGui:GetChildren()) do
- if gui:IsA("ScreenGui")
- and gui.Name:find(
- "Delta_Radar",
- 1,
- true
- ) then
- pcall(function()
- gui:Destroy()
- end)
- end
- end
- -----------------------------------------------------------
- -- SCREEN GUI
- -----------------------------------------------------------
- local screenGui = Instance.new("ScreenGui")
- screenGui.Name =
- "Delta_Radar_V26_Optimized"
- screenGui.ResetOnSpawn = false
- screenGui.IgnoreGuiInset = true
- screenGui.Parent = playerGui
- -----------------------------------------------------------
- -- MAIN CONTAINER
- -----------------------------------------------------------
- local mainContainer = Instance.new("Frame")
- mainContainer.Name =
- "MainContainer"
- mainContainer.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- RADAR_HEIGHT + 105
- )
- mainContainer.Position =
- UDim2.new(
- 0.5,
- -RADAR_WIDTH / 2,
- 0.05,
- 0
- )
- mainContainer.BackgroundTransparency = 1
- mainContainer.Active = true
- mainContainer.Parent = screenGui
- -----------------------------------------------------------
- -- MODE BUTTON
- -----------------------------------------------------------
- local modeButton = Instance.new("TextButton")
- modeButton.Name =
- "ModeButton"
- modeButton.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- 28
- )
- modeButton.Position =
- UDim2.new(
- 0,
- 0,
- 0,
- 0
- )
- modeButton.BackgroundColor3 =
- Color3.fromRGB(
- 5,
- 5,
- 10
- )
- modeButton.BackgroundTransparency = 0.15
- modeButton.TextColor3 =
- Color3.fromRGB(
- 0,
- 255,
- 0
- )
- modeButton.TextSize = 12
- modeButton.Font = Enum.Font.Code
- modeButton.Text =
- "MODE: PLAYERS"
- modeButton.AutoButtonColor = true
- modeButton.ZIndex = 30
- modeButton.Parent = mainContainer
- local modeCorner =
- Instance.new("UICorner")
- modeCorner.CornerRadius =
- UDim.new(
- 0,
- 6
- )
- modeCorner.Parent = modeButton
- local modeStroke =
- Instance.new("UIStroke")
- modeStroke.Thickness = 1.5
- modeStroke.Color =
- Color3.fromRGB(
- 0,
- 80,
- 200
- )
- modeStroke.ApplyStrokeMode =
- Enum.ApplyStrokeMode.Border
- modeStroke.Parent = modeButton
- -----------------------------------------------------------
- -- RANGE LABEL
- -----------------------------------------------------------
- local rangeLabel = Instance.new("TextLabel")
- rangeLabel.Name =
- "RangeLabel"
- rangeLabel.Size =
- UDim2.new(
- 0,
- 45,
- 0,
- 25
- )
- rangeLabel.Position =
- UDim2.new(
- 0,
- 0,
- 0,
- 35
- )
- rangeLabel.BackgroundTransparency = 1
- rangeLabel.Text =
- "Range"
- rangeLabel.TextColor3 =
- Color3.fromRGB(
- 255,
- 255,
- 255
- )
- rangeLabel.TextSize = 12
- rangeLabel.Font = Enum.Font.Code
- rangeLabel.TextXAlignment =
- Enum.TextXAlignment.Right
- rangeLabel.Parent = mainContainer
- -----------------------------------------------------------
- -- RANGE BOX
- -----------------------------------------------------------
- local rangeBox = Instance.new("TextBox")
- rangeBox.Name =
- "RangeBox"
- rangeBox.Size =
- UDim2.new(
- 0,
- 80,
- 0,
- 25
- )
- rangeBox.Position =
- UDim2.new(
- 0.5,
- -20,
- 0,
- 35
- )
- rangeBox.BackgroundColor3 =
- Color3.fromRGB(
- 5,
- 5,
- 10
- )
- rangeBox.BackgroundTransparency = 0.2
- rangeBox.TextColor3 =
- Color3.fromRGB(
- 0,
- 255,
- 0
- )
- rangeBox.Font = Enum.Font.Code
- rangeBox.TextSize = 14
- rangeBox.Text =
- tostring(MAX_DISTANCE)
- rangeBox.ClearTextOnFocus = false
- rangeBox.Parent = mainContainer
- local rangeCorner =
- Instance.new("UICorner")
- rangeCorner.CornerRadius =
- UDim.new(
- 0,
- 6
- )
- rangeCorner.Parent = rangeBox
- local rangeStroke =
- Instance.new("UIStroke")
- rangeStroke.Thickness = 1.5
- rangeStroke.Color =
- Color3.fromRGB(
- 0,
- 80,
- 200
- )
- rangeStroke.ApplyStrokeMode =
- Enum.ApplyStrokeMode.Border
- rangeStroke.Parent = rangeBox
- -----------------------------------------------------------
- -- RADAR FRAME
- -----------------------------------------------------------
- local radarFrame = Instance.new("Frame")
- radarFrame.Name =
- "RadarFrame"
- radarFrame.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- RADAR_HEIGHT
- )
- radarFrame.Position =
- UDim2.new(
- 0,
- 0,
- 0,
- 65
- )
- radarFrame.BackgroundColor3 =
- Color3.fromRGB(
- 5,
- 5,
- 10
- )
- radarFrame.BackgroundTransparency = 0.2
- radarFrame.ClipsDescendants = true
- radarFrame.Parent = mainContainer
- local radarCorner =
- Instance.new("UICorner")
- radarCorner.CornerRadius =
- UDim.new(
- 0,
- 8
- )
- radarCorner.Parent = radarFrame
- local radarStroke =
- Instance.new("UIStroke")
- radarStroke.Thickness = 2
- radarStroke.Color =
- Color3.fromRGB(
- 0,
- 80,
- 200
- )
- radarStroke.ApplyStrokeMode =
- Enum.ApplyStrokeMode.Border
- radarStroke.Parent = radarFrame
- -----------------------------------------------------------
- -- CENTER POINTER
- -----------------------------------------------------------
- local centerDot =
- Instance.new("Frame")
- centerDot.Name =
- "CenterPointer"
- centerDot.Size =
- UDim2.new(
- 0,
- 6,
- 0,
- 6
- )
- centerDot.Position =
- UDim2.new(
- 0.5,
- -3,
- 0.5,
- -3
- )
- centerDot.BackgroundTransparency = 1
- centerDot.ZIndex = 20
- centerDot.Parent = radarFrame
- -----------------------------------------------------------
- -- DIRECTION ARROW
- -----------------------------------------------------------
- local directionArrow =
- Instance.new("TextLabel")
- directionArrow.Name =
- "DirectionArrow"
- directionArrow.Size =
- UDim2.new(
- 1,
- 0,
- 1,
- 0
- )
- directionArrow.BackgroundTransparency = 1
- directionArrow.Text =
- "▲"
- directionArrow.TextColor3 =
- Color3.fromRGB(
- 0,
- 255,
- 0
- )
- directionArrow.TextSize = 8
- directionArrow.ZIndex = 21
- directionArrow.Parent = centerDot
- local arrowStroke =
- Instance.new("UIStroke")
- arrowStroke.Thickness = 1
- arrowStroke.Color =
- Color3.new(
- 0,
- 0,
- 0
- )
- arrowStroke.Parent = directionArrow
- -----------------------------------------------------------
- -- SIZE LABEL
- -----------------------------------------------------------
- local sizeLabel =
- Instance.new("TextLabel")
- sizeLabel.Name =
- "SizeLabel"
- sizeLabel.Size =
- UDim2.new(
- 0,
- 65,
- 0,
- 25
- )
- sizeLabel.Position =
- UDim2.new(
- 0,
- 0,
- 0,
- RADAR_HEIGHT + 70
- )
- sizeLabel.BackgroundTransparency = 1
- sizeLabel.Text =
- "Radar Size"
- sizeLabel.TextColor3 =
- Color3.fromRGB(
- 255,
- 255,
- 255
- )
- sizeLabel.TextSize = 11
- sizeLabel.Font = Enum.Font.Code
- sizeLabel.TextXAlignment =
- Enum.TextXAlignment.Right
- sizeLabel.Parent = mainContainer
- -----------------------------------------------------------
- -- SIZE BOX
- -----------------------------------------------------------
- local sizeBox =
- Instance.new("TextBox")
- sizeBox.Name =
- "SizeBox"
- sizeBox.Size =
- UDim2.new(
- 0,
- 70,
- 0,
- 25
- )
- sizeBox.Position =
- UDim2.new(
- 0.5,
- -5,
- 0,
- RADAR_HEIGHT + 70
- )
- sizeBox.BackgroundColor3 =
- Color3.fromRGB(
- 5,
- 5,
- 10
- )
- sizeBox.BackgroundTransparency = 0.2
- sizeBox.TextColor3 =
- Color3.fromRGB(
- 0,
- 255,
- 0
- )
- sizeBox.Font = Enum.Font.Code
- sizeBox.TextSize = 14
- sizeBox.Text =
- tostring(RADAR_WIDTH)
- sizeBox.ClearTextOnFocus = false
- sizeBox.Parent = mainContainer
- local sizeCorner =
- Instance.new("UICorner")
- sizeCorner.CornerRadius =
- UDim.new(
- 0,
- 6
- )
- sizeCorner.Parent = sizeBox
- local sizeStroke =
- Instance.new("UIStroke")
- sizeStroke.Thickness = 1.5
- sizeStroke.Color =
- Color3.fromRGB(
- 0,
- 80,
- 200
- )
- sizeStroke.ApplyStrokeMode =
- Enum.ApplyStrokeMode.Border
- sizeStroke.Parent = sizeBox
- -----------------------------------------------------------
- -- CHARACTER ROOT
- -----------------------------------------------------------
- local function getCharacterRoot(character)
- if not character
- or not character:IsA("Model") then
- return nil
- end
- local root =
- character:FindFirstChild(
- "HumanoidRootPart"
- )
- if root
- and root:IsA("BasePart") then
- return root
- end
- local primary =
- character.PrimaryPart
- if primary
- and primary:IsA("BasePart") then
- return primary
- end
- return nil
- end
- -----------------------------------------------------------
- -- PLAYER VALIDATION
- -----------------------------------------------------------
- local function isTrackablePlayer(targetPlayer)
- if not targetPlayer
- or not targetPlayer:IsA("Player") then
- return false
- end
- if targetPlayer == player then
- return false
- end
- if not targetPlayer.Parent then
- return false
- end
- local character =
- targetPlayer.Character
- if not character
- or not character.Parent then
- return false
- end
- local humanoid =
- character:FindFirstChildOfClass(
- "Humanoid"
- )
- if not humanoid
- or humanoid.Health <= 0 then
- return false
- end
- return getCharacterRoot(
- character
- ) ~= nil
- end
- -----------------------------------------------------------
- -- NPC ROOT
- -----------------------------------------------------------
- local function getNPCRoot(model)
- if not model
- or not model:IsA("Model") then
- return nil
- end
- local root =
- model:FindFirstChild(
- "HumanoidRootPart"
- )
- if root
- and root:IsA("BasePart") then
- return root
- end
- local primary =
- model.PrimaryPart
- if primary
- and primary:IsA("BasePart") then
- return primary
- end
- return nil
- end
- -----------------------------------------------------------
- -- NPC VALIDATION
- -----------------------------------------------------------
- local function isTrackableNPC(model)
- if not model
- or not model:IsA("Model") then
- return false
- end
- if not model.Parent then
- return false
- end
- if Players:GetPlayerFromCharacter(model) then
- return false
- end
- local humanoid =
- model:FindFirstChildOfClass(
- "Humanoid"
- )
- if not humanoid
- or humanoid.Health <= 0 then
- return false
- end
- local root =
- getNPCRoot(model)
- if not root then
- return false
- end
- return not root.Anchored
- end
- -----------------------------------------------------------
- -- TARGET NAME
- -----------------------------------------------------------
- local function getTargetName(target)
- if currentMode == "Players" then
- if target
- and target:IsA("Player") then
- local displayName =
- target.DisplayName
- local username =
- target.Name
- if typeof(displayName) ~= "string"
- or displayName == "" then
- return "@" .. username
- end
- if displayName == username then
- return "@" .. username
- end
- return displayName
- .. " @"
- .. username
- end
- elseif currentMode == "NPC" then
- if target
- and target:IsA("Model") then
- return target.Name
- end
- end
- return "Unknown"
- end
- -----------------------------------------------------------
- -- REMOVE DOT
- -----------------------------------------------------------
- local function removeDot(target)
- local data =
- activeDots[target]
- if not data then
- return
- end
- activeDots[target] = nil
- targetCache[target] = nil
- if data.Dot then
- pcall(function()
- data.Dot:Destroy()
- end)
- end
- end
- -----------------------------------------------------------
- -- REMOVE ALL DOTS
- -----------------------------------------------------------
- local function removeAllDots()
- local targets = {}
- for target in pairs(activeDots) do
- targets[#targets + 1] = target
- end
- for _, target in ipairs(targets) do
- removeDot(target)
- end
- end
- -----------------------------------------------------------
- -- CREATE DOT
- -----------------------------------------------------------
- local function createDot(target)
- if destroyed
- or not target
- or activeDots[target] then
- return
- end
- local valid
- if currentMode == "Players" then
- valid = isTrackablePlayer(target)
- else
- valid = isTrackableNPC(target)
- end
- if not valid then
- return
- end
- -------------------------------------------------------
- -- DOT
- -------------------------------------------------------
- local dot =
- Instance.new("Frame")
- dot.Name =
- "RadarDot"
- dot.Size =
- UDim2.new(
- 0,
- DOT_SIZE,
- 0,
- DOT_SIZE
- )
- if currentMode == "Players" then
- dot.BackgroundColor3 =
- Color3.fromRGB(
- 255,
- 60,
- 60
- )
- else
- dot.BackgroundColor3 =
- Color3.fromRGB(
- 255,
- 170,
- 40
- )
- end
- dot.BorderSizePixel = 0
- dot.ZIndex = 10
- dot.Visible = false
- dot.Parent = radarFrame
- local dotCorner =
- Instance.new("UICorner")
- dotCorner.CornerRadius =
- UDim.new(
- 1,
- 0
- )
- dotCorner.Parent = dot
- -------------------------------------------------------
- -- NAME LABEL
- -------------------------------------------------------
- local nameLabel =
- Instance.new("TextLabel")
- nameLabel.Name =
- "TargetName"
- nameLabel.AnchorPoint =
- Vector2.new(
- 0.5,
- 1
- )
- nameLabel.Size =
- UDim2.new(
- 0,
- 110,
- 0,
- 14
- )
- nameLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 0,
- -3
- )
- nameLabel.BackgroundTransparency = 1
- nameLabel.Text =
- getTargetName(target)
- nameLabel.TextColor3 =
- Color3.fromRGB(
- 255,
- 255,
- 255
- )
- nameLabel.TextSize = 8
- nameLabel.Font =
- Enum.Font.Code
- nameLabel.TextXAlignment =
- Enum.TextXAlignment.Center
- nameLabel.TextTruncate =
- Enum.TextTruncate.AtEnd
- nameLabel.ZIndex = 12
- nameLabel.Parent = dot
- local nameStroke =
- Instance.new("UIStroke")
- nameStroke.Thickness = 1
- nameStroke.Color =
- Color3.new(
- 0,
- 0,
- 0
- )
- nameStroke.Parent = nameLabel
- -------------------------------------------------------
- -- DISTANCE LABEL
- -------------------------------------------------------
- local distanceLabel =
- Instance.new("TextLabel")
- distanceLabel.Name =
- "Distance"
- distanceLabel.AnchorPoint =
- Vector2.new(
- 0.5,
- 0
- )
- distanceLabel.Size =
- UDim2.new(
- 0,
- 45,
- 0,
- 12
- )
- distanceLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 1,
- 3
- )
- distanceLabel.BackgroundTransparency = 1
- distanceLabel.TextColor3 =
- Color3.fromRGB(
- 190,
- 255,
- 190
- )
- distanceLabel.TextSize = 8
- distanceLabel.Font =
- Enum.Font.Code
- distanceLabel.TextXAlignment =
- Enum.TextXAlignment.Center
- distanceLabel.ZIndex = 12
- distanceLabel.Parent = dot
- local distanceStroke =
- Instance.new("UIStroke")
- distanceStroke.Thickness = 1
- distanceStroke.Color =
- Color3.new(
- 0,
- 0,
- 0
- )
- distanceStroke.Parent =
- distanceLabel
- -------------------------------------------------------
- -- CACHE
- -------------------------------------------------------
- local root
- local humanoid
- if currentMode == "Players" then
- local character =
- target.Character
- if character then
- root =
- getCharacterRoot(character)
- humanoid =
- character:FindFirstChildOfClass(
- "Humanoid"
- )
- end
- else
- root =
- getNPCRoot(target)
- humanoid =
- target:FindFirstChildOfClass(
- "Humanoid"
- )
- end
- activeDots[target] = {
- Dot = dot,
- NameLabel = nameLabel,
- DistanceLabel = distanceLabel,
- }
- targetCache[target] = {
- Root = root,
- Humanoid = humanoid,
- LastName = nameLabel.Text,
- LastDistance = "",
- }
- end
- -----------------------------------------------------------
- -- REFRESH TARGET CACHE
- -----------------------------------------------------------
- local function refreshTargetCache(target)
- local cache =
- targetCache[target]
- if not cache then
- return
- end
- if currentMode == "Players" then
- local character =
- target.Character
- if not character then
- cache.Root = nil
- cache.Humanoid = nil
- return
- end
- cache.Root =
- getCharacterRoot(character)
- cache.Humanoid =
- character:FindFirstChildOfClass(
- "Humanoid"
- )
- else
- cache.Root =
- getNPCRoot(target)
- cache.Humanoid =
- target:FindFirstChildOfClass(
- "Humanoid"
- )
- end
- end
- -----------------------------------------------------------
- -- UPDATE LABEL TEXT
- -----------------------------------------------------------
- local function updateLabelText(
- target,
- data,
- cache,
- distance
- )
- if currentMode == "Players" then
- local name =
- getTargetName(target)
- if cache.LastName ~= name then
- data.NameLabel.Text =
- name
- cache.LastName =
- name
- end
- else
- local name =
- getTargetName(target)
- if cache.LastName ~= name then
- data.NameLabel.Text =
- name
- cache.LastName =
- name
- end
- end
- local distanceText =
- tostring(
- math.floor(distance)
- ) .. "m"
- if cache.LastDistance
- ~= distanceText then
- data.DistanceLabel.Text =
- distanceText
- cache.LastDistance =
- distanceText
- end
- end
- -----------------------------------------------------------
- -- UPDATE TARGET POSITION
- -----------------------------------------------------------
- local function calculateTargetPosition(
- target,
- data,
- playerRoot
- )
- local cache =
- targetCache[target]
- if not cache then
- return nil
- end
- local root =
- cache.Root
- local humanoid =
- cache.Humanoid
- if not root
- or not root.Parent then
- refreshTargetCache(target)
- root =
- cache.Root
- humanoid =
- cache.Humanoid
- end
- if not root
- or not root:IsA("BasePart")
- or not root.Parent then
- data.Dot.Visible = false
- return nil
- end
- if humanoid
- and (
- humanoid.Health <= 0
- or not humanoid.Parent
- ) then
- data.Dot.Visible = false
- return nil
- end
- if currentMode == "NPC"
- and root.Anchored then
- data.Dot.Visible = false
- return nil
- end
- -------------------------------------------------------
- -- Local coordinates.
- --
- -- X = right/left
- -- -Z = forward
- -------------------------------------------------------
- local localPosition =
- playerRoot.CFrame:PointToObjectSpace(
- root.Position
- )
- -------------------------------------------------------
- -- Horizontal distance only.
- -------------------------------------------------------
- local distanceSquared =
- localPosition.X
- * localPosition.X
- +
- localPosition.Z
- * localPosition.Z
- local maxDistanceSquared =
- MAX_DISTANCE
- * MAX_DISTANCE
- if distanceSquared
- > maxDistanceSquared then
- data.Dot.Visible = false
- return nil
- end
- local distance =
- math.sqrt(
- distanceSquared
- )
- -------------------------------------------------------
- -- Radar coordinates.
- -------------------------------------------------------
- local xOffset =
- (
- localPosition.X
- / MAX_DISTANCE
- )
- * (RADAR_WIDTH / 2)
- local yOffset =
- (
- -localPosition.Z
- / MAX_DISTANCE
- )
- * (RADAR_HEIGHT / 2)
- -------------------------------------------------------
- -- Clamp dot.
- -------------------------------------------------------
- local maxX =
- math.max(
- 0,
- (RADAR_WIDTH / 2)
- - (DOT_SIZE / 2)
- )
- local maxY =
- math.max(
- 0,
- (RADAR_HEIGHT / 2)
- - (DOT_SIZE / 2)
- )
- xOffset =
- math.clamp(
- xOffset,
- -maxX,
- maxX
- )
- yOffset =
- math.clamp(
- yOffset,
- -maxY,
- maxY
- )
- data.Dot.Visible = true
- data.Dot.Position =
- UDim2.new(
- 0.5,
- xOffset - DOT_SIZE / 2,
- 0.5,
- yOffset - DOT_SIZE / 2
- )
- updateLabelText(
- target,
- data,
- cache,
- distance
- )
- return {
- Target = target,
- Data = data,
- X = xOffset,
- Y = yOffset,
- Distance = distance,
- }
- end
- -----------------------------------------------------------
- -- LABEL COLLISION RESOLUTION
- -----------------------------------------------------------
- local function resolveLabelCollisions(visibleTargets)
- local count =
- #visibleTargets
- if count <= 1 then
- if count == 1 then
- local item =
- visibleTargets[1]
- item.Data.NameLabel.Visible = true
- item.Data.DistanceLabel.Visible = true
- item.Data.NameLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 0,
- -3
- )
- item.Data.DistanceLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 1,
- 3
- )
- end
- return
- end
- for i = 1, count do
- local item =
- visibleTargets[i]
- local closestDistance =
- math.huge
- local closestItem = nil
- for j = 1, count do
- if i ~= j then
- local other =
- visibleTargets[j]
- local dx =
- item.X - other.X
- local dy =
- item.Y - other.Y
- local distance =
- math.sqrt(
- dx * dx
- +
- dy * dy
- )
- if distance < closestDistance then
- closestDistance =
- distance
- closestItem =
- other
- end
- end
- end
- ---------------------------------------------------
- -- Isolated target.
- ---------------------------------------------------
- if not closestItem
- or closestDistance
- > LABEL_COLLISION_DISTANCE then
- item.Data.NameLabel.Visible = true
- item.Data.DistanceLabel.Visible = true
- item.Data.NameLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 0,
- -3
- )
- item.Data.DistanceLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 1,
- 3
- )
- else
- ------------------------------------------------
- -- Nearby target.
- --
- -- Alternate the label vertically so two
- -- neighboring targets don't have identical
- -- label positions.
- ------------------------------------------------
- local targetIndex =
- table.find(
- visibleTargets,
- item
- )
- local side
- if targetIndex % 2 == 0 then
- side = 1
- else
- side = -1
- end
- local verticalOffset =
- LABEL_OFFSET * side
- item.Data.NameLabel.Visible = true
- item.Data.DistanceLabel.Visible = true
- item.Data.NameLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 0,
- -3
- + verticalOffset
- )
- item.Data.DistanceLabel.Position =
- UDim2.new(
- 0.5,
- 0,
- 1,
- 3
- + verticalOffset
- )
- ------------------------------------------------
- -- Extremely crowded areas:
- -- Keep the dots visible but reduce label
- -- clutter.
- ------------------------------------------------
- local crowdedCount = 0
- for j = 1, count do
- if i ~= j then
- local other =
- visibleTargets[j]
- local dx =
- item.X - other.X
- local dy =
- item.Y - other.Y
- local distance =
- math.sqrt(
- dx * dx
- +
- dy * dy
- )
- if distance
- <= LABEL_COLLISION_DISTANCE then
- crowdedCount += 1
- end
- end
- end
- if crowdedCount
- >= MAX_LABELS_PER_CLUSTER then
- -- Show labels for the closest targets
- -- and hide the rest.
- if targetIndex
- > MAX_LABELS_PER_CLUSTER then
- item.Data.NameLabel.Visible = false
- item.Data.DistanceLabel.Visible = false
- end
- end
- end
- end
- end
- -----------------------------------------------------------
- -- UPDATE RADAR
- -----------------------------------------------------------
- local function updateRadar()
- if destroyed then
- return
- end
- local character =
- player.Character
- if not character then
- localRoot = nil
- for _, data in pairs(activeDots) do
- data.Dot.Visible = false
- end
- return
- end
- localRoot =
- getCharacterRoot(character)
- if not localRoot then
- for _, data in pairs(activeDots) do
- data.Dot.Visible = false
- end
- return
- end
- local visibleTargets = {}
- -------------------------------------------------------
- -- Update every tracked target.
- -------------------------------------------------------
- for target, data in pairs(activeDots) do
- if destroyed then
- return
- end
- if not target
- or not target.Parent then
- removeDot(target)
- else
- local position =
- calculateTargetPosition(
- target,
- data,
- localRoot
- )
- if position then
- visibleTargets[
- #visibleTargets + 1
- ] = position
- end
- end
- end
- -------------------------------------------------------
- -- Resolve label collisions only after all positions
- -- are calculated.
- -------------------------------------------------------
- resolveLabelCollisions(
- visibleTargets
- )
- end
- -----------------------------------------------------------
- -- PLAYER REGISTRATION
- -----------------------------------------------------------
- local function unregisterPlayer(targetPlayer)
- removeDot(targetPlayer)
- local connectionData =
- playerConnections[targetPlayer]
- if not connectionData then
- return
- end
- for _, connection in pairs(
- connectionData
- ) do
- if connection then
- pcall(function()
- connection:Disconnect()
- end)
- end
- end
- playerConnections[targetPlayer] = nil
- end
- local function registerPlayer(targetPlayer)
- if destroyed
- or not targetPlayer
- or targetPlayer == player then
- return
- end
- if playerConnections[targetPlayer] then
- return
- end
- local characterConnection =
- targetPlayer.CharacterAdded:Connect(
- function(character)
- if destroyed then
- return
- end
- ------------------------------------------------
- -- Update cache immediately.
- ------------------------------------------------
- local cache =
- targetCache[targetPlayer]
- if cache then
- cache.Root =
- getCharacterRoot(
- character
- )
- cache.Humanoid =
- character:FindFirstChildOfClass(
- "Humanoid"
- )
- end
- if currentMode == "Players" then
- task.defer(function()
- if destroyed
- or not targetPlayer.Parent then
- return
- end
- if isTrackablePlayer(
- targetPlayer
- ) then
- createDot(
- targetPlayer
- )
- end
- end)
- end
- end
- )
- playerConnections[targetPlayer] = {
- CharacterAdded =
- characterConnection
- }
- if currentMode == "Players"
- and isTrackablePlayer(targetPlayer) then
- createDot(targetPlayer)
- end
- end
- -----------------------------------------------------------
- -- PLAYER ADDED
- -----------------------------------------------------------
- connections.PlayerAdded =
- Players.PlayerAdded:Connect(
- function(targetPlayer)
- if destroyed then
- return
- end
- safeCall(
- "Player Added",
- registerPlayer,
- targetPlayer
- )
- end
- )
- -----------------------------------------------------------
- -- PLAYER REMOVING
- -----------------------------------------------------------
- connections.PlayerRemoving =
- Players.PlayerRemoving:Connect(
- function(targetPlayer)
- if destroyed then
- return
- end
- safeCall(
- "Player Removing",
- unregisterPlayer,
- targetPlayer
- )
- end
- )
- -----------------------------------------------------------
- -- REGISTER EXISTING PLAYERS
- -----------------------------------------------------------
- for _, targetPlayer in ipairs(
- Players:GetPlayers()
- ) do
- if targetPlayer ~= player then
- registerPlayer(targetPlayer)
- end
- end
- -----------------------------------------------------------
- -- LOCAL PLAYER RESPAWN
- -----------------------------------------------------------
- connections.CharacterAdded =
- player.CharacterAdded:Connect(
- function(character)
- if destroyed then
- return
- end
- localRoot = nil
- task.defer(function()
- if destroyed
- or not character.Parent then
- return
- end
- localRoot =
- getCharacterRoot(
- character
- )
- updateRadar()
- end)
- end
- )
- -----------------------------------------------------------
- -- NPC MODEL DETECTION
- -----------------------------------------------------------
- local function findPotentialNPC(obj)
- if not obj then
- return nil
- end
- if obj:IsA("Model") then
- return obj
- end
- if obj:IsA("Humanoid")
- or obj:IsA("BasePart") then
- return obj:FindFirstAncestorOfClass(
- "Model"
- )
- end
- return obj:FindFirstAncestorOfClass(
- "Model"
- )
- end
- -----------------------------------------------------------
- -- FAST NPC DETECTION
- -----------------------------------------------------------
- connections.DescendantAdded =
- workspace.DescendantAdded:Connect(
- function(obj)
- if destroyed
- or currentMode ~= "NPC" then
- return
- end
- local npc =
- findPotentialNPC(obj)
- if not npc then
- return
- end
- ------------------------------------------------
- -- Defer only to allow the model's inserted
- -- children to finish replication.
- ------------------------------------------------
- task.defer(function()
- if destroyed
- or currentMode ~= "NPC"
- or not npc.Parent then
- return
- end
- if isTrackableNPC(npc) then
- createDot(npc)
- end
- end)
- end
- )
- -----------------------------------------------------------
- -- REFRESH PLAYERS
- -----------------------------------------------------------
- local function refreshPlayers()
- if destroyed
- or currentMode ~= "Players" then
- return
- end
- for _, targetPlayer in ipairs(
- Players:GetPlayers()
- ) do
- if targetPlayer ~= player then
- if isTrackablePlayer(
- targetPlayer
- ) then
- createDot(
- targetPlayer
- )
- end
- end
- end
- end
- -----------------------------------------------------------
- -- REFRESH NPCS
- -----------------------------------------------------------
- local function refreshNPCs()
- if destroyed
- or currentMode ~= "NPC" then
- return
- end
- -------------------------------------------------------
- -- This only runs during safety synchronization or
- -- initial NPC mode activation.
- -------------------------------------------------------
- for _, obj in ipairs(
- workspace:GetDescendants()
- ) do
- if destroyed
- or currentMode ~= "NPC" then
- break
- end
- if obj:IsA("Model")
- and isTrackableNPC(obj) then
- createDot(obj)
- end
- end
- end
- -----------------------------------------------------------
- -- MODE SWITCH
- -----------------------------------------------------------
- local function switchMode()
- if destroyed then
- return
- end
- if currentMode == "Players" then
- currentMode = "NPC"
- else
- currentMode = "Players"
- end
- -------------------------------------------------------
- -- Clear previous mode.
- -------------------------------------------------------
- removeAllDots()
- -------------------------------------------------------
- -- Update button.
- -------------------------------------------------------
- modeButton.Text =
- "MODE: "
- .. string.upper(
- currentMode
- )
- -------------------------------------------------------
- -- Load new mode.
- -------------------------------------------------------
- if currentMode == "Players" then
- refreshPlayers()
- else
- refreshNPCs()
- end
- updateRadar()
- end
- -----------------------------------------------------------
- -- MODE BUTTON
- -----------------------------------------------------------
- connections.ModeButton =
- modeButton.MouseButton1Click:Connect(
- function()
- if destroyed then
- return
- end
- safeCall(
- "Mode Switch",
- switchMode
- )
- end
- )
- -----------------------------------------------------------
- -- RANGE INPUT
- -----------------------------------------------------------
- connections.RangeFocusLost =
- rangeBox.FocusLost:Connect(
- function()
- if destroyed then
- return
- end
- safeCall(
- "Range Input",
- function()
- local newRange =
- tonumber(
- rangeBox.Text
- )
- if newRange
- and newRange == newRange
- and newRange ~= math.huge
- and newRange ~= -math.huge
- and newRange > 0 then
- MAX_DISTANCE =
- math.clamp(
- math.floor(
- newRange
- ),
- MIN_RANGE,
- MAX_RANGE
- )
- end
- rangeBox.Text =
- tostring(
- MAX_DISTANCE
- )
- updateRadar()
- end
- )
- end
- )
- -----------------------------------------------------------
- -- RADAR SIZE
- -----------------------------------------------------------
- local originalAspectRatio =
- RADAR_HEIGHT / RADAR_WIDTH
- local function updateRadarSize(newSize)
- if destroyed then
- return
- end
- if typeof(newSize) ~= "number"
- or newSize ~= newSize
- or newSize == math.huge
- or newSize == -math.huge then
- sizeBox.Text =
- tostring(
- RADAR_WIDTH
- )
- return
- end
- newSize =
- math.clamp(
- math.floor(
- newSize
- ),
- MIN_RADAR_SIZE,
- MAX_RADAR_SIZE
- )
- RADAR_WIDTH =
- newSize
- RADAR_HEIGHT =
- math.max(
- 1,
- math.floor(
- RADAR_WIDTH
- * originalAspectRatio
- )
- )
- -------------------------------------------------------
- -- Container
- -------------------------------------------------------
- mainContainer.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- RADAR_HEIGHT + 105
- )
- -------------------------------------------------------
- -- Button
- -------------------------------------------------------
- modeButton.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- 28
- )
- -------------------------------------------------------
- -- Radar
- -------------------------------------------------------
- radarFrame.Size =
- UDim2.new(
- 0,
- RADAR_WIDTH,
- 0,
- RADAR_HEIGHT
- )
- -------------------------------------------------------
- -- Maintain horizontal center.
- -------------------------------------------------------
- mainContainer.Position =
- UDim2.new(
- 0.5,
- -RADAR_WIDTH / 2,
- mainContainer.Position.Y.Scale,
- mainContainer.Position.Y.Offset
- )
- -------------------------------------------------------
- -- Bottom controls.
- -------------------------------------------------------
- sizeLabel.Position =
- UDim2.new(
- 0,
- 0,
- 0,
- RADAR_HEIGHT + 70
- )
- sizeBox.Position =
- UDim2.new(
- 0.5,
- -5,
- 0,
- RADAR_HEIGHT + 70
- )
- sizeBox.Text =
- tostring(
- RADAR_WIDTH
- )
- updateRadar()
- end
- connections.SizeFocusLost =
- sizeBox.FocusLost:Connect(
- function()
- if destroyed then
- return
- end
- safeCall(
- "Size Input",
- function()
- local newSize =
- tonumber(
- sizeBox.Text
- )
- if newSize then
- updateRadarSize(
- newSize
- )
- else
- sizeBox.Text =
- tostring(
- RADAR_WIDTH
- )
- end
- end
- )
- end
- )
- -----------------------------------------------------------
- -- DRAGGING
- -----------------------------------------------------------
- connections.MainInputBegan =
- mainContainer.InputBegan:Connect(
- function(input)
- if destroyed then
- return
- end
- if input.UserInputType
- ~= Enum.UserInputType.MouseButton1
- and input.UserInputType
- ~= Enum.UserInputType.Touch then
- return
- end
- if UserInputService:GetFocusedTextBox() then
- return
- end
- dragging = true
- dragStart =
- input.Position
- startPos =
- mainContainer.Position
- end
- )
- connections.InputEnded =
- UserInputService.InputEnded:Connect(
- function(input)
- if input.UserInputType
- == Enum.UserInputType.MouseButton1
- or input.UserInputType
- == Enum.UserInputType.Touch then
- dragging = false
- end
- end
- )
- connections.InputChanged =
- UserInputService.InputChanged:Connect(
- function(input)
- if destroyed
- or not dragging then
- return
- end
- if input.UserInputType
- ~= Enum.UserInputType.MouseMovement
- and input.UserInputType
- ~= Enum.UserInputType.Touch then
- return
- end
- if not dragStart
- or not startPos then
- dragging = false
- return
- end
- local delta =
- input.Position
- - dragStart
- mainContainer.Position =
- UDim2.new(
- startPos.X.Scale,
- startPos.X.Offset
- + delta.X,
- startPos.Y.Scale,
- startPos.Y.Offset
- + delta.Y
- )
- end
- )
- -----------------------------------------------------------
- -- SAFETY SYNCHRONIZATION
- -----------------------------------------------------------
- task.spawn(function()
- while not destroyed
- and screenGui.Parent do
- task.wait(
- SAFETY_SCAN_RATE
- )
- if destroyed
- or not screenGui.Parent then
- break
- end
- safeCall(
- "Safety Synchronization",
- function()
- if currentMode == "Players" then
- ------------------------------------------------
- -- Repair missing player registrations.
- ------------------------------------------------
- local validPlayers = {}
- for _, targetPlayer in ipairs(
- Players:GetPlayers()
- ) do
- if targetPlayer ~= player then
- validPlayers[
- targetPlayer
- ] = true
- if not playerConnections[
- targetPlayer
- ] then
- registerPlayer(
- targetPlayer
- )
- end
- if isTrackablePlayer(
- targetPlayer
- )
- and not activeDots[
- targetPlayer
- ] then
- createDot(
- targetPlayer
- )
- end
- end
- end
- ------------------------------------------------
- -- Remove stale players.
- ------------------------------------------------
- local stale = {}
- for target in pairs(
- activeDots
- ) do
- if not validPlayers[
- target
- ] then
- stale[
- #stale + 1
- ] = target
- end
- end
- for _, target in ipairs(stale) do
- removeDot(target)
- end
- else
- ------------------------------------------------
- -- NPC safety repair.
- --
- -- This is the ONLY place the full workspace
- -- scan occurs.
- ------------------------------------------------
- refreshNPCs()
- ------------------------------------------------
- -- Remove dead/deleted NPCs.
- ------------------------------------------------
- local staleNPCs = {}
- for model in pairs(
- activeDots
- ) do
- if not model
- or not model.Parent
- or not isTrackableNPC(
- model
- ) then
- staleNPCs[
- #staleNPCs + 1
- ] = model
- end
- end
- for _, model in ipairs(
- staleNPCs
- ) do
- removeDot(model)
- end
- end
- updateRadar()
- end
- )
- end
- end)
- -----------------------------------------------------------
- -- OPTIMIZED RADAR LOOP
- -----------------------------------------------------------
- connections.RenderStepped =
- RunService.RenderStepped:Connect(
- function(deltaTime)
- if destroyed then
- return
- end
- radarAccumulator += deltaTime
- if radarAccumulator
- < RADAR_UPDATE_RATE then
- return
- end
- ---------------------------------------------------
- -- Prevent accumulator from growing indefinitely
- -- after frame drops.
- ---------------------------------------------------
- radarAccumulator = 0
- updateRadar()
- end
- )
- -----------------------------------------------------------
- -- GUI CLEANUP
- -----------------------------------------------------------
- connections.GuiDestroying =
- screenGui.Destroying:Connect(
- function()
- if destroyed then
- return
- end
- destroyed = true
- dragging = false
- ---------------------------------------------------
- -- Global connections.
- ---------------------------------------------------
- for name, connection in pairs(
- connections
- ) do
- if connection then
- pcall(function()
- connection:Disconnect()
- end)
- connections[name] = nil
- end
- end
- ---------------------------------------------------
- -- Player connections.
- ---------------------------------------------------
- for targetPlayer, connectionData in pairs(
- playerConnections
- ) do
- for _, connection in pairs(
- connectionData
- ) do
- if connection then
- pcall(function()
- connection:Disconnect()
- end)
- end
- end
- playerConnections[
- targetPlayer
- ] = nil
- end
- ---------------------------------------------------
- -- Remove dots.
- ---------------------------------------------------
- local targets = {}
- for target in pairs(activeDots) do
- targets[#targets + 1] = target
- end
- for _, target in ipairs(targets) do
- local data =
- activeDots[target]
- activeDots[target] = nil
- targetCache[target] = nil
- if data
- and data.Dot then
- pcall(function()
- data.Dot:Destroy()
- end)
- end
- end
- end
- )
- -----------------------------------------------------------
- -- INITIAL LOAD
- -----------------------------------------------------------
- safeCall(
- "Initial Player Load",
- refreshPlayers
- )
- safeCall(
- "Initial Radar Update",
- updateRadar
- )
- -----------------------------------------------------------
- -- FINAL STATUS
- -----------------------------------------------------------
- print(
- "[Delta Radar V26] "
- .. "Optimized radar loaded | "
- .. "Mode: "
- .. currentMode
- .. " | Update: 30Hz"
- )
Advertisement
Add Comment
Please, Sign In to add comment