How could i improve my AI?

So, i have this script, that allows an AI that will attack any nearby players/characters with weapons. But there’s a few issues, the core of this script is pretty old, and the NPC can be a bit laggy / jittery, and the code is a bit on the messy side. There are also times where the NPC would just teleport due to lag. How could i improve this?

Humanoid = script.Parent:WaitForChild("Humanoid")
Config = script.Parent:WaitForChild("Config")
AttackPlayersOnly = Config:WaitForChild("AttackPlayersOnly")
AttackDist = Config:WaitForChild("AttackDistance")
UseWeaponDist = Config:WaitForChild("UseWeaponDistance")
HurtAttackDist = Config:WaitForChild("HurtAttackDistance")

OrigWalkSpeed = Humanoid.WalkSpeed

WalkAnim = Humanoid:LoadAnimation(Config:WaitForChild("WalkAnim"))
IdleAnim = Humanoid:LoadAnimation(Config:WaitForChild("IdleAnim"))

function Attack()
	local Item = script.Parent:FindFirstChildOfClass("Tool")
	if Item then
		local Activate = Item:FindFirstChild("ActivateWeapon")
		if Activate and Activate.ClassName == "BoolValue" then -- The AI has a usable weapon
			Activate.Value = true
		end 
	end
end

function findNearestTorso(pos)
	local list = game.Workspace:children()
	local torso = nil
	local temp = nil
	local human = nil
	local temp2 = nil
	for x = 1, #list do
		temp2 = list[x]
		if (temp2.className == "Model") and (temp2 ~= script.Parent) then
			temp = temp2:findFirstChild("Torso")
			human = temp2:findFirstChild("Humanoid")
			
			local function Follow()
				if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
					if (temp.Position - pos).magnitude < AttackDist.Value then
						torso = temp
						--AttackDist.Value = (temp.Position - pos).magnitude
					end
					
					if (temp.Position - pos).magnitude < UseWeaponDist.Value then
						Attack()
					end
				end
			end
			
			if AttackPlayersOnly.Value == true and human then	
				local PlayerCheck = game:GetService("Players"):FindFirstChild(human.Parent.Name)
				if PlayerCheck then
					Follow()
				end
			else
				Follow()
			end
		end
	end
	return torso
end
--wait(math.random(0,5)/10)
while true do
	wait(0.3)
	local target = findNearestTorso(script.Parent.Torso.Position)
	if target ~= nil then
		Humanoid.WalkSpeed = OrigWalkSpeed
		Humanoid:MoveTo(target.Position, target) -- My time has come
		if WalkAnim.IsPlaying == false or IdleAnim.IsPlaying == true then
			IdleAnim:Stop()
			WalkAnim:Play()
		end
	else
		-- Target has walked away, i guess we just stand here now.
		if WalkAnim.IsPlaying == true or IdleAnim.IsPlaying == false then
			WalkAnim:Stop()
			IdleAnim:Play()
		end
		Humanoid.WalkSpeed = 0
	end
end

OldHealth = Humanoid.Health

Humanoid.HealthChanged:Connect(function()
	if Humanoid.Health < OldHealth and Humanoid.Health > 0 then -- Humanoid has been damaged
		local OrigDist = AttackDist.Value
		AttackDist.Value = HurtAttackDist.Value
		wait(15)
		AttackDist.Value = OrigDist
	end
	OldHealth = Humanoid.Health
end)

Shouldn’t this go somewhere like Code Review?

Oh yes, my bad. ill change the category :sweat_smile:

I’ll assume this is a server script, in which case you don’t have to worry about waiting for instances to load in unless they’re added later on by another script in the game. This means anything with a :WaitForChild() should be fine without it unless that particular instance is loaded in later.

However, to fix the issue with lag, the only solution I can think of is to make the NPC’s code and network ownership localized to the player (AKA :SetNetworkOwner(plr) and using local scripts), but then it wouldn’t always be in the same place for all players and would also be way easier for exploiters to abuse, so I highly discourage this unless the game were single-player.

On the other hand, if your animations are initially delayed, I believe you can use :PreloadAsync() to ensure they’re loaded before being played for the first time. This is just a minor improvement, though.

Lastly, the sections of your code which use " == true" or " == false" can be simplified, as well as checking for nil.

Example -

if target ~= nil then 

can be changed to

if target then 

and

if WalkAnim.IsPlaying == false or IdleAnim.IsPlaying == true then 

can be changed to

if not WalkAnim.IsPlaying or IdleAnim.IsPlaying then 
2 Likes

There’s a really simple way to what looks like pathfinding (which you should also add, if you’re confident enough). Record the last target position and draw a ray towards them. If the ray can reach them, walk towards them. If it can’t reach them (aka they went around a corner), then route to the last known location. It’ll be able to “pathfind” one obstacle at a time for a primitive AI.

7 Likes

huh, interesting. I only though that would work for objects. Thanks

Hi,
Did you get this to work, or look into pathfinding?

On the above I have gotten most of the things going, but wondering about this line

function Attack()
local Item = script.Parent:FindFirstChildOfClass(“Tool”)
if Item then
local Activate = Item:FindFirstChild(“ActivateWeapon”) ----- what is this thing in the tool ? to kick it into activate…
if Activate and Activate.ClassName == “BoolValue” then – The AI has a usable weapon
Activate.Value = true
end
end
end

relating to the line
local Activate = Item:FindFirstChild(“ActivateWeapon”)

what is this inside the tool that the NPC has, that then makes the tool kick into effect? something that
kicks off a script?

Thanks!

I am assuming it is a BoolValue , but then what is monitoring that BoolValue to run a script or something…?

mmmm, nevermind, i figured it out…