Help on figuring out an effective way to do an enemy NPC's attacks

Hello! I am creating a horror game on roblox inspired on Isle and other roblox games, im currently trying to script the main monster of the game, but i never really worked with enemy npcs before

What i am trying to achieve is seeing if i can improve my attack system, im currently using distance checks to see if the player is in enough range, and applying a different cooldown for each of the attacks inside their function, but im sure there is more effective ways to do that

Heres my current script:

local Monster = script.Parent
local MonsterRoot = Monster:FindFirstChild("HumanoidRootPart")
local MonsterHumanoid = Monster:FindFirstChild("Humanoid")

local TweenService = game:GetService("TweenService")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SimplePath = require(ServerStorage.Modules.SimplePath)

------------
-- Functions
------------

local function GetClosestPlayer()
	local ClosestPlayer
	
	for _, Player in pairs(game:GetService("Players"):GetPlayers()) do
		local Character = Player.Character
		
		if Player:DistanceFromCharacter(MonsterRoot.Position) < 500 then
			if not ClosestPlayer or Player:DistanceFromCharacter(MonsterRoot.Position) < ClosestPlayer:DistanceFromCharacter(MonsterRoot.Position) then
				ClosestPlayer = Player
			end
		end
	end
	
	return ClosestPlayer
end


--------------
-- Core Script
--------------

repeat task.wait() until #game:GetService("Players"):GetPlayers() > 0

local Roaming = false

while true do
	local ClosestPlayer = GetClosestPlayer()
	
	if ClosestPlayer then
		local Character = Workspace:WaitForChild(ClosestPlayer.Name)
		coroutine.wrap(function()
			repeat
				if ClosestPlayer:DistanceFromCharacter(MonsterRoot.Position) < 5 then
					--// Slash Attack
				elseif ClosestPlayer:DistanceFromCharacter(MonsterRoot.Position) < 40 then
                   --// Bullet Attack
				end
				
				task.wait()
			until
				not ClosestPlayer or not ClosestPlayer.Character or ClosestPlayer:DistanceFromCharacter(MonsterRoot.Position) > 500 or ClosestPlayer.Character.Humanoid.Health == 0	
		end)()
		
		repeat
			local PathObject = SimplePath.new(Monster)
			
			PathObject.Visualize = true
			
			PathObject.Error:Connect(function()
				PathObject:Run(Character.HumanoidRootPart)
			end)
			
			PathObject:Run(Character.HumanoidRootPart)
			task.wait()
		until
			not ClosestPlayer or not Character or ClosestPlayer:DistanceFromCharacter(MonsterRoot.Position) > 500 or ClosestPlayer.Character.Humanoid.Health == 0
	end
	
	task.wait()
end


Thanks in advance!

1 Like

Both attack styles could end up being solved with Raycasting.
Such as with the “Slash Attack” you could shoot out a Raycast on the initial slash hit, get the character and damage it accordingly.

For the gun attacks it can follow the Raycasting method as well.

There is a wide variety of how you end up using Raycasting, but it is a very helpful WorldRoot function for stuff like attack damage detection. A different approach too could be touch detection with some sort of coordinated Hitbox, but it is honestly up to you.

There is also this amazing community resource you can use as an example/inspiration:
Raycast Hitbox 4.01: For all your melee needs! - Resources / Community Resources - Developer Forum | Roblox

(Edit): Forgot this
WorldRoot | Documentation - Roblox Creator Hub

1 Like

Oh i completely forgot about using raycast for these functions, i already saw that module so im gonna use it, thanks for the tip!
But what i was looking for is an alternative and better way to do the magnitude checks for the attacks, since i am planning to add more attacks

It doesn’t look like there is much more you can do, I would imagine you could cut down all the If statements into some sort of Magnitude sorts into Attack type.

-- maybe some module or table of functions that handle the NPC's attacks

local function NPCAttack(magnitude : number)
  -- code for determining what attack to do based on magnitude
end

Oh alright, ima try doing that, thanks for the help!

the raycast hitbox thing is really cool, but one option is using dot product with magnitude its really cool.
image
and theres also a quicker way to do the math for magnitude checks if all you need is the range
( it doesn’t give the length )

local vector = a-b
if (vector).Magnitude < 5 then
-- OR
if (vector.X^2 + vector.Y^2 + vector.Z^2) < 5^2 then
-- also could do x*x or y*y etc...

I never saw that, how does it work? Seems interesting

the code at the bottom is just a different magnitude check, but you can combine magnitude and dot product to also check if the character is looking in the attacking direction.

^^ this is just an example, in the first picture i sent there is a green cube near my cursor it would turn red if my character was not looking at it because of this code.

basically if the vectors are looking the same direction the dot product returns a value of 1, and
90 degrees is 0 and completely opposite is -1

its a little weird im ngl, but if you mess with the numbers a little bit you might find values you like. They are not exactly 1 to 1 with 360 degrees for the first image the dot product had to be greater than 0.65 to turn the squares on the floor red.

2 Likes

heres a cool link:

talks about dot product and cross product.

2 Likes

Oh so it will only target players that are for example in his field of vision? I already saw that, i will implement it in my script, thanks for the tip!

Also sorry for the late answer

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.