Here you go. I’d personally recommend and while true do loop, but this should solve your problems. You’ll have to readd your remote event code, I did this quickly so I could test it. Realistic angles of fov, a bar using tween service to go up on down depending on detection, etc.
-- Get necessary services
local RunService = game:GetService("RunService")
local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Set NPC and character, along with their sight and range properties
local npc = workspace.World.Debug.NPC.DetectionTest
local npcSight = 0.7
local npcRange = 100
-- GUI elements and reaction time
local player = PlayersService.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local playerGui = player.PlayerGui
local DetectionBar = playerGui:WaitForChild("DetectionGUI")
local reactionTime = 1.8
-- Create a table to hold TweenInfo
local tweenInfo = TweenInfo.new(
2, -- Time
Enum.EasingStyle.Linear, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
0, -- RepeatCount (0 implies the tween will not repeat)
false, -- Reverses (should the tween reverse once reaching the endpoint)
0 -- DelayTime
)
local characterIsInFov = false
RunService.Heartbeat:Connect(function()
if character and npc then -- Check if the character and the NPC are not null
-- Calculate the vector between the NPC and the character
local npcToCharacter = (head.Position - npc.Head.Position).Unit
local npcLook = npc.Head.CFrame.LookVector
local dotProduct = npcToCharacter:Dot(npcLook)
if dotProduct > npcSight then
--character is in field of view
characterIsInFov = true
else
-- character is not in field of view so we do not need to take any action
characterIsInFov = false
end
-- Prepare parameters for raycasting
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {npc}
raycastParams.IgnoreWater = true
-- Perform a raycast to check if the character is in sight
local ray = workspace:Raycast(npc.PrimaryPart.Position, (character.PrimaryPart.Position - npc.PrimaryPart.Position).Unit * npcRange, raycastParams)
-- Check if the character is in range and in field of view
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
if ray and ray.Instance.Parent == character then
local hitPlayer = PlayersService:GetPlayerFromCharacter(ray.Instance.Parent)
if hitPlayer then
-- Animate the detection bar
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, math.clamp(dotProduct * 2, 0, 1), 0)})
tween:Play()
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
-- Wait for reaction time and check again
task.wait(reactionTime)
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
print('Player detected: ', hitPlayer.Name)
end
end
else
-- Decrease the detection bar when not in sight
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, 0, 0)})
tween:Play()
end
else
-- Decrease the detection bar when not in sight
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, 0, 0)})
tween:Play()
end
end
end)
-- Get necessary services
local RunService = game:GetService("RunService")
local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Set NPC and character, along with their sight and range properties
local npc = workspace.World.Debug.NPC.DetectionTest
local npcSight = 0.7
local npcRange = 100
-- GUI elements and reaction time
local player = PlayersService.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local playerGui = player.PlayerGui
local DetectionBar = playerGui:WaitForChild("DetectionGUI")
local reactionTime = 1.8
-- Create a table to hold TweenInfo
local tweenInfo = TweenInfo.new(
1, -- Time
Enum.EasingStyle.Linear, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
0, -- RepeatCount (0 implies the tween will not repeat)
false, -- Reverses (should the tween reverse once reaching the endpoint)
0 -- DelayTime
)
local characterIsInFov = false
local detectionProgress = 0 -- Variable to keep track of detection progress
RunService.Heartbeat:Connect(function()
if character and npc then -- Check if the character and the NPC are not null
-- Calculate the vector between the NPC and the character
local npcToCharacter = (head.Position - npc.Head.Position).Unit
local npcLook = npc.Head.CFrame.LookVector
local dotProduct = npcToCharacter:Dot(npcLook)
if dotProduct > npcSight then
--character is in field of view
characterIsInFov = true
else
-- character is not in field of view so we do not need to take any action
characterIsInFov = false
end
-- Prepare parameters for raycasting
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {npc}
raycastParams.IgnoreWater = true
-- Perform a raycast to check if the character is in sight
local ray = workspace:Raycast(npc.PrimaryPart.Position, (character.PrimaryPart.Position - npc.PrimaryPart.Position).Unit * npcRange, raycastParams)
-- Check if the character is in range and in field of view
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
if ray and ray.Instance.Parent == character then
local hitPlayer = PlayersService:GetPlayerFromCharacter(ray.Instance.Parent)
if hitPlayer then
-- Increase the detection progress
detectionProgress = math.min(detectionProgress + dotProduct * 2, 1)
-- Animate the detection bar
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, detectionProgress, 0)})
tween:Play()
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
-- Check if the detection bar is filled completely before triggering the NPC detection
if detectionProgress == 1 then
-- Wait for reaction time and check again
task.wait(reactionTime)
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
print('Player detected: ', hitPlayer.Name)
end
end
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05, 0) -- Decrease the detection progress
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, detectionProgress, 0)})
tween:Play()
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05, 0) -- Decrease the detection progress
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, detectionProgress, 0)})
tween:Play()
end
end
end)
-- Get necessary services
local RunService = game:GetService("RunService")
local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Set NPC and character, along with their sight and range properties
local npc = workspace.World.Debug.NPC.DetectionTest
local npcSight = 0.7
local npcRange = 100
-- GUI elements and reaction time
local player = PlayersService.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local playerGui = player.PlayerGui
local DetectionBar = playerGui:WaitForChild("DetectionGUI")
local reactionTime = 1.8
-- Create a table to hold TweenInfo
local tweenInfo = TweenInfo.new(
1.5, -- Time
Enum.EasingStyle.Quad, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
0, -- RepeatCount (0 implies the tween will not repeat)
false, -- Reverses (should the tween reverse once reaching the endpoint)
0 -- DelayTime
)
local characterIsInFov = false
local detectionProgress = 0 -- Variable to keep track of detection progress
local hitPlayer = nil -- Move hitPlayer variable outside the if statement
RunService.Heartbeat:Connect(function()
if character and npc then -- Check if the character and the NPC are not null
-- Calculate the vector between the NPC and the character
local npcToCharacter = (head.Position - npc.Head.Position).Unit
local npcLook = npc.Head.CFrame.LookVector
local dotProduct = npcToCharacter:Dot(npcLook)
if dotProduct > npcSight then
--character is in field of view
characterIsInFov = true
else
-- character is not in field of view so we do not need to take any action
characterIsInFov = false
end
-- Prepare parameters for raycasting
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {npc}
raycastParams.IgnoreWater = true
-- Perform a raycast to check if the character is in sight
local ray = workspace:Raycast(npc.PrimaryPart.Position, (character.PrimaryPart.Position - npc.PrimaryPart.Position).Unit * npcRange, raycastParams)
-- Check if the character is in range and in field of view
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
if ray and ray.Instance.Parent == character then
hitPlayer = PlayersService:GetPlayerFromCharacter(ray.Instance.Parent) -- This will now update the variable declared earlier
if hitPlayer then
-- Increase the detection progress
detectionProgress = math.min(detectionProgress + dotProduct * 2, 1)
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05, 0) -- Decrease the detection progress
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05, 0) -- Decrease the detection progress
end
-- Animate the detection bar according to detection progress
local tween = TweenService:Create(DetectionBar.detectionFrame.detectionBar, tweenInfo, {Size = UDim2.new(1, 0, detectionProgress, 0)})
tween:Play()
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
-- Check if the detection bar is filled completely before triggering the NPC detection
if detectionProgress >= 1 then
detectionProgress = 0 -- Reset detection progress after detection
-- Wait for the bar to finish filling up visually, then check again
tween.Completed:Wait() -- Wait for the current tween to complete
task.wait(reactionTime)
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange and hitPlayer then -- Make sure hitPlayer is not nil
print('Player detected: ', hitPlayer.Name)
end
end
end
end)
-- Get necessary services
local RunService = game:GetService("RunService")
local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Set NPC and character, along with their sight and range properties
local npc = workspace:WaitForChild("Rig")
local npcSight = 0.7
local npcRange = 100
-- GUI elements and reaction time
local player = PlayersService.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local head = character:WaitForChild("Head")
local playerGui = player.PlayerGui
local DetectionBar = playerGui:WaitForChild("DetectionGUI")
local reactionTime = 1.8
local characterIsInFov = false
local detectionProgress = 0 -- Variable to keep track of detection progress
local hitPlayer = nil -- Move hitPlayer variable outside the if statement
RunService.Heartbeat:Connect(function(dt)
if character and npc then -- Check if the character and the NPC are not null
-- Calculate the vector between the NPC and the character
local npcToCharacter = (head.Position - npc.Head.Position).Unit
local npcLook = npc.Head.CFrame.LookVector
local dotProduct = npcToCharacter:Dot(npcLook)
if dotProduct > npcSight then
--character is in field of view
characterIsInFov = true
else
-- character is not in field of view so we do not need to take any action
characterIsInFov = false
end
-- Prepare parameters for raycasting
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {npc}
raycastParams.IgnoreWater = true
-- Perform a raycast to check if the character is in sight
local ray = workspace:Raycast(npc.PrimaryPart.Position, (character.PrimaryPart.Position - npc.PrimaryPart.Position).Unit * npcRange, raycastParams)
-- Check if the character is in range and in field of view
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange then
if ray and ray.Instance.Parent == character then
hitPlayer = PlayersService:GetPlayerFromCharacter(ray.Instance.Parent) -- This will now update the variable declared earlier
if hitPlayer then
-- Increase the detection progress
detectionProgress = math.min(detectionProgress + dotProduct * 2 * dt, 1)
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05 * dt, 0) -- Decrease the detection progress
end
else
-- Decrease the detection bar when not in sight
detectionProgress = math.max(detectionProgress - 0.05 * dt, 0) -- Decrease the detection progress
end
-- Animate the detection bar according to detection progress
local alpha = TweenService:GetValue(detectionProgress / 1, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
DetectionBar.detectionFrame.detectionBar.Size = UDim2.new(1, 0, 0, 0):Lerp(UDim2.new(1, 0, 1, 0), alpha)
DetectionBar.detectionFrame.detectionBar.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
-- Check if the detection bar is filled completely before triggering the NPC detection
if detectionProgress >= 1 then
-- detectionProgress = 0 -- Reset detection progress after detection
-- Wait for the bar to finish filling up visually, then check again
--tween.Completed:Wait() -- Wait for the current tween to complete
task.wait(reactionTime)
if characterIsInFov and (head.Position - npc.PrimaryPart.Position).Magnitude <= npcRange and hitPlayer then -- Make sure hitPlayer is not nil
print('Player detected: ', hitPlayer.Name)
end
end
end
end)
This is a test on one NPC. It will need some love,
but seems to be a viable approach for a template.
(script works as is)
.
A non-local script in ServerScriptService
task.wait(5) -- Stalling so npc is ready
local npc = workspace.Sandy.Head -- NPC's head
local viewRange = 20
-- Function to check if the player is within the NPC's view range
local function isPlayerInNPCViewRange(player)
local npcPosition = npc.Position
local playerPosition = player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.Position
if not playerPosition then
return false
end
local distance = (npcPosition - playerPosition).magnitude
return distance <= viewRange
end
-- Function to check if the player is within the 50-degree angle from the NPC's look vector
local function isPlayerInRaycastAngle(player)
local npcLookDirection = npc.CFrame.LookVector
local playerPosition = player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.Position
if not playerPosition then
return false
end
local directionToPlayer = (playerPosition - npc.Position).unit
local angle = math.acos(npcLookDirection:Dot(directionToPlayer)) * (180 / math.pi)
return angle <= 50 -- # degrees both ways from center 50x2 = 100
end
-- Added to a stepped loop
local RunService = game:GetService("RunService")
RunService.Stepped:connect(function()
for _, player in ipairs(game.Players:GetPlayers()) do
if isPlayerInNPCViewRange(player) and isPlayerInRaycastAngle(player) then
print("Player is within view range and angle!")
end
end
end)
Thank you everyone for your contributions, I really appreciate it!
The solution involves using heartbeat with repeated checks throughout to ensure that a player has or has not been detected, and then update the detection progress based on this information and send that to the actual detection bar.