Hey, and thanks for reading in advance.
I’m helping a buddy out with his Outbreak Survival look-alike game called Mutation. We’ve been working on it for a bit, and have decided to advertise once we’ve made a short, visual-learning tutorial.
The Zombie half of the tutorial includes a section that requires the player to overcome 3 - 4 AI controlled humans that present a rather difficult challenge that the player must overcome by experimenting with various mutations and seeing what works.
I’m currently coding the guidance system for the AI that manages vision and detection, but the way it works now - the AI has eyes in the back of its head. I want to impose angular limits on the vision ray it casts so that it can see at about 60 degrees in either direction and 70 degrees up or down, but I’m terrible with trig - if anyone has something I could use as a reference or a simple formula, I’d very much appreciate it!
Relevant Code:
local Character = script.Parent
local Humanoid = Character.Humanoid
local Root = Character:WaitForChild("HumanoidRootPart")
local Head = Character:WaitForChild("Head")
--
local rotator = Instance.new("BodyGyro")
rotator.MaxTorque = Vector3.new(0, 0, 0)
rotator.P = 10000; rotator.D = 400
rotator.CFrame = Root.CFrame
rotator.Parent = Root
--
local CURRENT_AGGRO = 0
local DETECT_THRESH = 200
local ACTIVE_MODE = false
local AGGRO_FALLPOINT = 5
local LAST_SIGHTING = tick()/2
--
local detectParams = RaycastParams.new()
detectParams.FilterDescendantsInstances = {Character}
detectParams.FilterType = Enum.RaycastFilterType.Blacklist
detectParams.IgnoreWater = true
--
function speak(text, color)
game.ReplicatedStorage.Remotes.ServerMessage:FireAllClients(
text, color or Color3.new(1, 1, 1), Character.Name
)
end
-- PRIMARY LOOP
spawn(function()
while Humanoid.Health > 0 and Character.Parent and wait() do
local searchFor = {}; local fireAt = {}
for _,player in pairs(game.Players:GetPlayers()) do
if player.Team == game.Teams.Zombies then
if player.Character and player.Character:IsDescendantOf(workspace)
and player.Character:FindFirstChild("Head")
and player.Character:FindFirstChild("HumanoidRootPart")
and player.Character:FindFirstChild("Humanoid")
and player.Character.Humanoid.Health > 0 then
searchFor[#searchFor + 1] = player.Character
end
end
end
if #searchFor > 0 then
for _,target in pairs(searchFor) do
local visionCheck = workspace:Raycast(Head.Position, (target.Head.Position - Head.Position).Unit * 200, detectParams)
if visionCheck and visionCheck.Instance:IsDescendantOf(target) then
LAST_SIGHTING = tick()
if ACTIVE_MODE then
fireAt[#fireAt + 1] = target
else
local drawDistance = (visionCheck.Position - Head.Position).Magnitude
CURRENT_AGGRO += DETECT_THRESH * (1 - (drawDistance / 200))
if CURRENT_AGGRO >= DETECT_THRESH then
ACTIVE_MODE = true; speak("Undead spotted!")
rotator.MaxTorque = Vector3.new(0, math.huge, 0)
fireAt[#fireAt + 1] = target
end
end
end
end
if ACTIVE_MODE then
if #fireAt > 0 then
table.sort(fireAt, function(a, b)
local distA = (a.Head.Position - Head.Position).Magnitude
local distB = (b.Head.Position - Head.Position).Magnitude
return distA < distB
end)
local target = fireAt[1]
local t_head = target.Head
local t_root = target.HumanoidRootPart
rotator.CFrame = CFrame.new(Root.Position, Vector3.new(t_root.Position.X, Root.Position.Y, t_root.Position.Z))
elseif (tick() - LAST_SIGHTING) >= AGGRO_FALLPOINT then
CURRENT_AGGRO = math.ceil((CURRENT_AGGRO * .92) - 1) * (1/15)
if CURRENT_AGGRO <= 0 then
ACTIVE_MODE = false; speak("I lost 'em..")
rotator.MaxTorque = Vector3.new(0, 0, 0)
rotator.CFrame = Root.CFrame
end
end
end
end
end
end)