Enemy AI System

I seem to be having issues with this code. It’s just not working the expected way, and I’ve tried multiple different ways.

What it’s basically suppose to do is:

  • Start off by roaming (gets a random point within the monsters zone)
  • Move to that random point
  • if at any time a player is detected within it’s range, start moving to that player, and keep checking if the player is in range
  • if a player is not detected, resume to roaming
  • if the monster is at anytime within the damage range of the monster, it should attack (as long as the monsters target it a player)

Note: I am using this pathfinding module: Getting Started - SimplePath

Current code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local PathfindingService = game:GetService("PathfindingService")

local Knit = require(ReplicatedStorage.Packages.knit)
local Component = require(ReplicatedStorage.Packages.component)
local ZonesComponent = require(ServerScriptService.Components.Zones)
local SimplePath = require(ReplicatedStorage.Packages.SimplePath)

local Monster = Component.new({ Tag = "Monster" })

function Monster:Construct()
	self.status = ""
	self.playerDetectionRange = 25
	self.playerDamageRange = 3

	self.path = nil
	self.playerDetected = false
	self.target = nil
end

local function getRoamPoint(self)
	local comp = ZonesComponent:FromInstance(self.Instance.Parent.Parent)
	local monsterZone = comp:GetZoneFromMonster(self.Instance)
	local point = monsterZone:getRandomPoint()
	return point
end

function Monster:Attack()
	print("Monster Attacked")

	local check = self:CheckForDamageDistance()
	if check then
		self:Attack()
	end
end

function Monster:CheckForDamageDistance()
	local closestPlayer = SimplePath.GetNearestCharacter(self.Instance.PrimaryPart.Position)
	if closestPlayer then
		if
			(closestPlayer.PrimaryPart.Position - self.Instance.PrimaryPart.Position).Magnitude
			<= self.playerDamageRange
		then
			return true
		end
	end
end

function Monster:CheckForPlayer()
	local closestPlayer = SimplePath.GetNearestCharacter(self.Instance.PrimaryPart.Position)
	if closestPlayer then
		if
			(closestPlayer.PrimaryPart.Position - self.Instance.PrimaryPart.Position).Magnitude
			<= self.playerDetectionRange
		then
			self.target = closestPlayer:GetPivot().Position
				+ (self.Instance:GetPivot().Position - closestPlayer:GetPivot().Position).Unit * 3
			self.playerDetected = true
		end
	end
end

function Monster:SetState(newState)
	self.status = newState

	if self.path.Status == "Active" then
		self.path:Stop()
	end

	self:CheckForPlayer()
	if self.target then
		self.path:Run(self.target)
	else
		self.target = getRoamPoint(self)
		self.path:Run(self.target)
	end
end

function Monster:Start()
	local _, size = self.Instance:GetBoundingBox()
	local pathParams = {
		["AgentHeight"] = size.Y,
		["AgentRadius"] = size.X / 2,
		["AgentCanJump"] = false,
	}

	self.path = SimplePath.new(self.Instance, pathParams)
	self.path.Visualize = true
	self:SetState("roaming")

	self.path.WaypointReached:Connect(function()
		self.playerDetected = false
		self:CheckForPlayer()

		if self.playerDetected then
			self:Attack()
			self:SetState("following")
		end
	end)

	self.path.Reached:Connect(function()
		self:CheckForPlayer()
		if self.playerDetected then
			self:Attack()
			self:SetState("roaming")
		else
			self.playerDetected = false
			self.target = nil
			self:SetState("roaming")
		end
	end)

	self.path.Blocked:Connect(function()
		self.playerDetected = false
		self.target = nil
		self:SetState("roaming")
	end)
end

function Monster:Stop() end

return Monster
3 Likes

You can try to use the scripts in the zombie model in the official NPC module to achieve your goal.
Modify the NPC script appropriately.
The notes of this script and the introduction of official documents are very detailed.
This is the website of NPC Suite: NPC Kit

5 Likes