I've made this enemy AI which just follows the player and attacks when close enough. It uses the path finding service, so it's horribly inefficient at getting to a moving target. Is there any better way to balance out efficiency (with pathfinding) and getting to the target as fast as possible?
Here is my code:
local Stats = {walk = 6, run = 22, health = 250, radius = 20, range = 10, id = 2549507798, damage = 10, attackspeed = 1}
local PathfindingService = game:GetService("PathfindingService")
local NpcTarget = require(game.ServerScriptService.Modules.NpcTarget)
local pointModel = Instance.new("Model")
pointModel.Name = "Points"
pointModel.Parent = workspace
local subject = script.Parent
local humanoid = subject.Humanoid
local lastHealth = humanoid.Health
local active = false
local lastFailedDirection = nil
local origin = subject.HumanoidRootPart.Position
local target = nil
local walkCount = 0
local canAttack = true
local dead = false
local startPos = subject.HumanoidRootPart.Position
local rng = Random.new()
local animation = Instance.new("Animation")
animation.AnimationId = "http://www.roblox.com/Asset?ID="..Stats.id
local track = humanoid:LoadAnimation(animation)
function outOfRangeRegen()
for i = 1,5 do
wait(0.1)
humanoid.Health = humanoid.Health + 5
end
end
function attackCooldown()
canAttack = false
coroutine.resume(coroutine.create(function()
wait(Stats.attackspeed)
canAttack = true
end))
end
function findRandomPos()
local pos = Vector3.new(subject.HumanoidRootPart.Position.X + rng:NextInteger(-25,25),
subject.HumanoidRootPart.Position.Y + rng:NextInteger(-5,5),
subject.HumanoidRootPart.Position.Z + rng:NextInteger(-25,25))
if not testOverLand(pos) then
pos = findRandomPos()
end
return pos
end
function calcPath(pos)
local start = subject.HumanoidRootPart.Position
local path = PathfindingService:FindPathAsync(start, pos)
return path
end
function originalPos()
walk(startPos)
end
function findTarget()
local testTarget = NpcTarget.findClosest(subject)
if not testTarget then
active = false
originalPos()
else
--[[if testTarget ~= target then
local onDied = target.Humanoid.Died:Connect(function()
target = findTarget()
end)
end]]
end
return testTarget
end
function detectTargets()
coroutine.resume(coroutine.create(function()
while not active do
wait(1)
for i,v in pairs(game.Players:GetChildren()) do
local char = v.Character
if char then
if (char.HumanoidRootPart.Position - subject.HumanoidRootPart.Position).magnitude < Stats.range then
local target = findTarget()
active = true
end
end
end
end
end))
end
function testOverLand(pos)
local ray = Ray.new(pos, ((pos - Vector3.new(0,100,0)) - pos).unit * 300)
local part, position = workspace:FindPartOnRay(ray, subject, false, true)
if part then
if part.Name == "Baseplate" then
return false
end
else
return false
end
return pos
end
function walk(opPos)
humanoid.WalkSpeed = Stats.walk
local pos = findRandomPos()
if opPos then
pos = opPos
end
local path = calcPath(pos)
if path:CheckOcclusionAsync(1) == -1 then
humanoid:MoveTo(pos)
wait(0.01)
end
local points = path:GetWaypoints()
local distance = 0
for i = 2,#points do
local lastWaypoint = points[i - 1]
local currentWaypoint = points[i]
humanoid:MoveTo(currentWaypoint.Position)
repeat
wait(0.01)
distance = (subject.HumanoidRootPart.Position - currentWaypoint.Position).magnitude
until distance <= 2.5 or active
if currentWaypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
end
return true
end
function testRangeDistance()
if not active then
if (subject.HumanoidRootPart.Position - origin).magnitude > Stats.radius then
return true
end
end
return false
end
function pause()
local w = math.random(1,5)
local count = 0
while not active and count < w do
wait(1)
count = count + 1
target = findTarget()
end
end
function testTargetDistance()
if (target.HumanoidRootPart.Position - subject.HumanoidRootPart.Position).magnitude <= 5 then
return true
end
return false
end
function run()
humanoid.WalkSpeed = 18
local pos = target.HumanoidRootPart.Position
local path = calcPath(pos)
if path.Status == Enum.PathStatus.NoPath then
outOfRangeRegen()
return
end
if path:CheckOcclusionAsync(1) == -1 and not testTargetDistance() then
humanoid:MoveTo(target.HumanoidRootPart.Position)
wait(0.01)
end
local points = path:GetWaypoints()
local distance = 0
local willAttack = false
for i = 2,#points do
local lastWaypoint = points[i - 1]
local currentWaypoint = points[i]
humanoid:MoveTo(currentWaypoint.Position)
repeat
wait(0.05)
distance = (subject.HumanoidRootPart.Position - currentWaypoint.Position).magnitude
willAttack = testTargetDistance()
until distance <= 6 or not active or target.Humanoid.Health <= 0 or willAttack
if willAttack then
humanoid:MoveTo(subject.HumanoidRootPart.Position)
attack()
end
if currentWaypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
end
return true
end
function attack()
if testTargetDistance() and canAttack and not dead then
track:Play()
target.Humanoid:TakeDamage(Stats.damage)
attackCooldown()
end
end
function died()
local function fadePart(part)
coroutine.resume(coroutine.create(function()
for i = 1,25 do
wait(0.01)
part.Transparency = part.Transparency + 0.04
end
end))
end
dead = true
subject.Humanoid.WalkSpeed = 0
subject.Humanoid.JumpPower = 0
local animation = Instance.new("Animation")
animation.AnimationId = "http://www.roblox.com/Asset?ID=2614501451"
local track = subject.Humanoid:LoadAnimation(animation)
track:Play()
track.KeyframeReached:Connect(function(keyframe)
if keyframe == "Dead" then
for i,v in pairs(subject:GetChildren()) do
if v:IsA("BasePart") then
fadePart(v)
end
end
end
game.Debris:AddItem(subject, 1)
end)
end
function tookDamage()
active = true
end
humanoid.HealthChanged:Connect(function(health)
if health <= 1 then
local ff = Instance.new("ForceField")
ff.Parent = subject
ff.Visible = false
died()
end
if health < lastHealth and not active then
tookDamage()
end
end)
while not dead do
wait()
if not active then
detectTargets()
local decision = math.random(1,3)
if decision > 1 and walkCount < 4 then
local success, msg = pcall(function()
walk()
end)
walkCount = walkCount + 1
wait(1)
else
pause()
walkCount = 0
end
if testRangeDistance() then
local success, msg = pcall(function()
walk(origin)
end)
end
else
target = findTarget()
if not target then
active = false
end
local success, msg = pcall(function()
run()
end)
end
end

1 comment
this is too much, good luck on finding an answer xd