A few issues with NPCs

Hi,

For a game jam I made a game which uses pathfinding AI, but I wasn’t able to solve it’s issues in time. I’ve reviewed the code now and I still can’t tell what the problems are, so I’m asking for help. I’m noticing:

  1. it will get stuck on objects or walk off the map
  2. sometimes putting the tool up will cause it to get stuck and animations to break
  3. when it misses a shot, it’s not obvious
  4. they shoot through walls sometimes

Here’s the code:

-- darkpixlz 2024
local PathfindingService = game:GetService("PathfindingService")

task.wait(2.5)

local Humanoid = script.Parent.Humanoid
Humanoid:EquipTool(script.Parent.Gun)

while task.wait(script.Parent:GetAttribute("TickTime")) do
	local PartToTrack = workspace[script.Parent:GetAttribute("Target")]
	--Humanoid:MoveTo(PartToTrack.Position)
	
	local RootPart = script.Parent:FindFirstChild("HumanoidRootPart")
	
	local RootPos = Vector3.new(0, 0, 0)
	
	if RootPart ~= nil then
		RootPos = RootPart.Position
	end
	
	local TrackPos = PartToTrack.Position
	
	if math.abs((RootPos - TrackPos).Magnitude) <= 500 then
		local path = PathfindingService:CreatePath()
		path:ComputeAsync(RootPos, (TrackPos- Vector3.new(-10, 12, 0)))
		local waypoints = path:GetWaypoints()
		
		for i, waypoint in pairs(waypoints) do
			script.Parent:FindFirstChildWhichIsA("Humanoid"):MoveTo(waypoint.Position)
		end
	end
	
	if not script.Parent:FindFirstChild("HumanoidRootPart") then
		print("NPC died, killing this script off...")
		break
	end

	-- Check distance
	local function IsWithinRange()
		if RootPart ~= nil then
			RootPos = RootPart.Position
		end
		
		if math.abs((RootPos - PartToTrack.Position).Magnitude) <= script.Parent:GetAttribute("Range") then
			return true
		else
			return false
		end
	end



	if IsWithinRange() == true then
		-- Shoot
		print("shooting")
		local tool = script.Parent.Gun
		local beam = Instance.new("Part", game.Workspace)
		beam.Color = Color3.new(1,1,1)
		beam.Material = Enum.Material.Neon
		beam.Transparency = .3
		beam.Anchored = true
		beam.Locked = true
		beam.CanCollide = false

		local distance = (
			tool.Dispenser.CFrame.p
			- 
			PartToTrack.CFrame.p
		).magnitude
		beam.Size = Vector3.new(.3, .3, distance)
		beam.CFrame = CFrame.new(tool.Dispenser.CFrame.p, PartToTrack.Position) * CFrame.new(0, 0, -distance / 2)
		game:GetService("Debris"):AddItem(beam, 0.05)
		
		-- chance to miss
		if math.random(1, 5) == 3 then
			print("Shot missed...")
			beam.Position = Vector3.new(beam.Position.X + math.random(1,3), beam.Position.Y + math.random(1,3), beam.Position.Z + math.random(1,3))
			continue
		end
		local HP = PartToTrack:GetAttribute("Health")

		PartToTrack:SetAttribute("Health", HP - script.Parent:GetAttribute("Damage"))
	end
end

Any help is appreciated, thank you!

2 Likes

Bad practice

while task.wait(script.Parent:GetAttribute("TickTime")) do

I highly recommend re-doing this part entirely. This is where most of your problems are coming from I am 99% sure. I think if you deconstruct your huge function and use something like rendered.stepped with different functions called into that you’d see a lot of fixes in your bugs. This also helps you later on, because you can pick and choose what functions to keep or test, thus making the hardest part of coding easy. Just simple deduction on what function is causing the problem, right now, you just got a big huge while do loop…

2 Likes

Updated to a RunService event, they seem to still be getting stuck.

-- darkpixlz 2024
local PathfindingService = game:GetService("PathfindingService")

task.wait(2.5)

local Humanoid = script.Parent.Humanoid
Humanoid:EquipTool(script.Parent.Gun)

local PartToTrack = workspace[script.Parent:GetAttribute("Target")]
local RootPart = script.Parent:FindFirstChild("HumanoidRootPart")
local RootPos = Vector3.new(0, 0, 0)

local function FindDrone()
	if RootPart ~= nil then
		RootPos = RootPart.Position
	end
	local TrackPos = PartToTrack.Position

	if math.abs((RootPos - TrackPos).Magnitude) <= 500 then
		local path = PathfindingService:CreatePath()
		path:ComputeAsync(RootPos, (TrackPos- Vector3.new(-10, 12, 0)))
		local waypoints = path:GetWaypoints()

		for i, waypoint in pairs(waypoints) do
			script.Parent:FindFirstChildWhichIsA("Humanoid"):MoveTo(waypoint.Position)
		end
	end
end

local function Shoot()
	local function IsWithinRange()
		if RootPart ~= nil then
			RootPos = RootPart.Position
		end

		if math.abs((RootPos - PartToTrack.Position).Magnitude) <= script.Parent:GetAttribute("Range") then
			return true
		else
			return false
		end
	end



	if IsWithinRange() == true then
		-- Shoot
		print("shooting")
		local tool = script.Parent.Gun
		local beam = Instance.new("Part", game.Workspace)
		beam.Color = Color3.new(1,1,1)
		beam.Material = Enum.Material.Neon
		beam.Transparency = .3
		beam.Anchored = true
		beam.Locked = true
		beam.CanCollide = false

		local distance = (
			tool.Dispenser.CFrame.p
			- 
				PartToTrack.CFrame.p
		).magnitude
		beam.Size = Vector3.new(.3, .3, distance)
		beam.CFrame = CFrame.new(tool.Dispenser.CFrame.p, PartToTrack.Position) * CFrame.new(0, 0, -distance / 2)
		game:GetService("Debris"):AddItem(beam, 0.05)

		local HP = PartToTrack:GetAttribute("Health")

		PartToTrack:SetAttribute("Health", HP - script.Parent:GetAttribute("Damage"))
	end
end

game:GetService("RunService").Heartbeat:Connect(function()
	task.wait(script.Parent:GetAttribute("TickTime"))

	FindDrone()

	if math.random(1, 5) ~= 1 then
		Shoot()
	end
end)
2 Likes

Just set it up like this.

while true do
    task.wait(5) --or some other length of time
    --do code
end

Or like this.

while true do
    local t = 0
    while true do
        t += task.wait()
        if t > script:GetAttribute("Time") then break end --break out of loop
    end
    --do code
end
2 Likes