How to make an NPC attack you, when you attack it?

I have a npc that randomly wanders around. I do that with a Humanoid:MoveTo() statement, that randomly takes a position.

When the npc takes damage, he should go after the player. I tried to do a Humanoid.HealthChanged and change his MoveTo() location, but the npc has to wait to arrive at the previous location.

I’ve searched around the forum and haven’t found anything. Any ideas?

4 Likes

Can you show the code where you move the NPC?

1 Like

Of course

local TW = game:GetService("TweenService")
local MapBounds = {Vector2.new(0,0),Vector2.new(100,100)}
local MaxHeight	= 100
local isChasing = false
local plrChar

local track1 = script.Parent.Humanoid.Animator:LoadAnimation(script.WalkAnim)
local track2 = script.Parent.Humanoid.Animator:LoadAnimation(script.IdleAnim)

local function getHeightAtPoint(position : Vector2)
 local Pos3D  = Vector3.new(position.X,MaxHeight,position.Y)
 local result = workspace:Raycast(Pos3D,Vector3.new(0,-1,0)*(MaxHeight*1.1))

 if result and result.Position then
  return result.Position.Y
 end
end

local function getRandomPosition()
 local randX  = math.random(MapBounds[1].X,MapBounds[2].X)
 local randZ  = math.random(MapBounds[1].Y,MapBounds[2].Y)
 local height = getHeightAtPoint(Vector2.new(randX,randZ))

 if not height then error("no height found") end 

 return Vector3.new(randX,height+1,randZ)
end

while task.wait(0.1) do
 script.Parent.NPCHit.Event:Connect(function(char)
  isChasing = true
  plrChar = char
 end)
 
 script.Parent.Humanoid.Died:Connect(function()
  for i, part in pairs(script.Parent:GetChildren()) do
   if part:IsA("BasePart") then
    TW:Create(part, TweenInfo.new(0.7), {Transparency=1}):Play()
   end
  end
  task.wait(.7)
  script.Parent.Parent = game.ReplicatedStorage
 end)
 
 if isChasing == false then
  track2:Stop()
  track1:Play()
  task.wait(2)
  repeat
   script.Parent.Humanoid:MoveTo(getRandomPosition())
   script.Parent.Humanoid.MoveToFinished:Wait()
  until isChasing == true
  track1:Stop()
  track2:Play()
 else
  if (script.Parent.HumanoidRootPart.Position - plrChar.HumanoidRootPart.Position).Magnitude < 30 then
   track2:Stop()
   track1:Play() 
   script.Parent.Humanoid.MoveToFinished:Wait()
   script.Parent.Humanoid:MoveTo(plrChar.HumanoidRootPart.Position)
  else
   isChasing = false
   end
 end
end

I got some stuff from somewhere else and i tried to implement like a Chasing thing so he runs after the player. It’s probably not well optimisied at all and the code is pretty ugly.

1 Like

Remove the Humanoid.MoveToFinished events?

1 Like

I’m a bit of a Lua beginner, but wouldn’t it be easier to have an event bound to when you hit the enemy rather than having the event bound to when the enemy gets hit? Not sure how your combat works, but it’s worth a shot to add a remote event that gets fired anytime the combat object touches something and is currently in an attack animation. Might be a simpler way to achieve this, but that’s the first thing that comes to mind

1 Like

I think i am gonna get a execution time out. I have a while loop and that’s the only thing waiting for the npc to finish his movements…

1 Like

Maybe replace the event with task.wait()?

Thing is I don’t know how much the move will take to make. Distance may be different and take more time.

My problem is that I need to break the Humanoid from going to the point when I attack him. He does come after me, but only after he reaches his destanation.

Dosen’t sound bad… but I still don’t know how I would stop his Humanoid:MoveTo() or overide it.

Just track who attacked the player, u need to consider ur attacking and npc system

--attacking system:
function Attack(plrWhoAttacked, Npc)
    Npc:SetAttribute("LastAttacker", plrWhoAttacked.Name)
    Npc.Humanoid.Health -= Damage
end

--Npc System
local NpcHum = Npc.Humanoid
local prevHealth = NpcHum.Health

Npc.Humanoid.HealthChanged:Connect(function(newhealth)
   --checking if npc was damaged instead of healed
   if newhealth < prevHealth then
         --get attacker player
        local AttackPlrName = Npc:GetAttribute("LastAttacker")
        local AttackerPlr = game.Players:FindFirstChild(AttackPlrName)
        if AttackerPlr then
                NpcHum:MoveTo(AttackerPlr.Character.HumanoidRootPart.Position)
        end
   end
   prevHealth = newhealth
end)
1 Like

That would work for the attacking system, but how would i make this compatable with the wandering system

Man, sorry for the dumb comment… this also might sound obvious but have you tried just making an event function house the chase player code instead of it being continuously checked through the while loop? I’d get rid of the while loop altogether and separate some of the stuff in it into its own function, like using a changed event on ischasing to make the moveTo command immediately redirect rather than waiting in the while loop. Then, for the random movement, I’d have a function that starts the random movement, then when it arrives at its location, it would change a “Status” variable, which could be used to fire another function. Sorry if my explanation sucked lol

1 Like

it totally upto urs npc system, u can check it per frame update by check if theres Enemy

RunService.Heartbeat --loop
if CurrentEnemy then
   --follow and attack the player e.g FollowPathToPlr(CurrentEnemy)
else
   --just wander around
1 Like

You can’t use Instance.Changed on a variable (if the variable’s value is not an instance).

1 Like

I came to the conclusion that i’ll just make him attack you when he sees you xD

Thank you guys for the help!

Oh, okay!

1 Like

HOLD MY DRINK
Alright, first let me preface with the fact that I am going to keep this high concept so you can figure out how you want to get this into you’re code.

  • We have an NPC wandering around using the “MoveTo” method within humanoid

  • We wish to capture when Damage happens and begin to aggressively try to attack the player

Your question is asking how would we approach getting this behavior to happen.

  1. MoveTo can be immediately over written with another MoveTo Command or a Move Command, I would switch to using Move when is aggro time. Move has the humanoid go in a direction vs try to approach a position. You can look that up, but that will solve your need to interrupt. Worse case, you may need to call a move command that is vector zero to blank out the movement, then give it a position. You will have to play with it to see what works and looks best for you.

  2. So you need a damage event, and you need to know who did your Damage. I would really consider abstracting this from your on Health change. On health change is the result of something, and while you can have other things happen as a result of, you should process your aggro and logic outside of that. This way you are less reliant on the event itself going off.

— So first, you need to know when the player has performed a hit in the first place. Whenever this happens, you can pass the playerId that performed the damage to into the NPC. You can have your NPC logic periodically checking if it “hitby” is filled out and then begin to perform logic to move towards and attack hitby target.

— Though I would recommend you have some sort of function that is called when target is doing damage on a target, that modifies and tells the target in some way that it has a target that hit it and should now start it’s attack processes.

Again this is high concept, this can be done many different ways. I highly recommend taking the time to get tit working but then spend time systemizing it.

I have wrote many many many tooo many behavior systems in my life time. But another thing is take the time to breath and think through your interaction.

You did a great job explaining what you want and what you want to happen as a result. Just take the time to explore your options and break down the things you need to happen to more discret smaller pieces and build up. You almost have your answer yourself it’s just how you end up doing it.

3 Likes

Woow you rlly took your time xD

Thank you a lot!

1 Like

If you are using any sort of ray to determine “Seeing” that’s pretty wasteful unless you are running your own Oct/Quad tree.

“seeing” is a relative thing, so basically your running an active scan of the enemy. Which eats a lot of resources.

If you are a dead set on using this method, I suggest you flip the switch and your concept of an Enemy seeing the player, is really the player doing periodic range checks and then anything within that range it alerts to it’s presence. This will reduces the number of processes needing to not be NPC limiting.

If you keep your range checks unsquared, and you use a minHeap, it will keep the check REALLY fast. Until you have like 100s of NPCs at that point you really should be using some sort of hash tree system so you can only check distance between things within regions but that’s another day.

1 Like

I thought about just doing a while and checking the distance from the player to the npc.