Currently messing around with some NPCs that attack when they are attacked by the player but I am having trouble with them also hugging the player at the same time.
There might be a simple solution that i am not aware of but currently I am playing with the idea of creating a box around the NPC with a part and having that as the OnTouched detection so the mob will attack from a slightly further distance, this of course doesn’t solve the prob of the mob constantly running into the player, hugging and pushing the player around.
I will provide my 2 scrips below if anyone could offer some advice? Id be happy with the mob simply moved closer to the player without getting really close and then attacking.
Here is my script that handle following the player once attacked.
local larm = script.Parent:FindFirstChild("Left Arm")
local rarm = script.Parent:FindFirstChild("Right Arm")
script.Parent.Humanoid.HealthChanged:Wait()
function findNearestTorso(pos)
local list = game.Workspace:children()
local torso = nil
local dist = 20
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("HumanoidRootPart")
human = temp2:findFirstChild("Humanoid")
if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
if (temp.Position - pos).magnitude < dist then
torso = temp
dist = (temp.Position - pos).magnitude
end
end
end
end
return torso
end
while true do
wait(0.1)
local target = findNearestTorso(script.Parent.HumanoidRootPart.Position)
if target ~= nil then
script.Parent.Humanoid:MoveTo(target.Position, target)
end
end
Here is my script that handle Attacking animation and damage delt
local animation = script:WaitForChild('Animation')
local humanoid = script.Parent.Parent:WaitForChild('Humanoid')
local attackanim = humanoid:LoadAnimation(animation)
local cooldown = 0
function onTouched(hit)
local human = hit.Parent:findFirstChild("Humanoid")
if (human ~= nil) and cooldown == 0 then
human.Health = human.Health - 25
attackanim:Play()
repeat
cooldown = cooldown+1
wait(1)
until cooldown >= 3
cooldown = 0
end
end
script.Parent.Touched:Connect(onTouched)
Check the distance between the NPC and it’s target.
And set the walk speed to 0 or 0.05 (to make the NPC still face the player).
Instead of using touched events you can also check if the target is within a certain attack range (Let’s say 4 - 5 studs depending on sword length) and then do a raycast from the swords handle to the target.
This can be done easily.
Also, I recommend keeping track of all players in a module script table instead of using workspace:children() (children() also is deprecated, use GetChildren() instead).
Getting all children of workspace every 1 - 5 seconds will cause lag if you have a ton of NPCs.
Use workspace.ChildAdded, check if the added child has a humanoid in it, then store it in a table inside a module script.
local fig = script.Parent
local targets = require(game.ServerStorage.Targets) --returns a table containing targets.
local function getnearest()
local distance = 1000
local selected = nil
for x = 1, #targets do
local torso = targets[x]:FindFirstChild("Torso")
if torso and (torso.Position - fig.Torso.Position).magnitude < distance then
distance = (torso.Position - fig.Torso.Position).magnitude
selected = torso
end
end
return torso
end
make sure there is a module script in ServerStorage.
Another script in ServerScriptService must handle the humanoids.
This can be done by doing this.
local targets = require(game.ServerStorage.Targets)
workspace.childAdded:Connect(function(obj)
if obj:IsA("Model") and obj:FindFirstChild("Torso") and obj:FindFirstChild("Humanoid") and game.Players:FindFirstChild(obj.name) then
table.insert(targets, #targets, obj)
end
end
*Don’t forget to remove targets as well to avoid memory leaks.
Hey mate! thanks for the detailed reply. So am I understand this correct.
First off, the ModuleScript named Targets doesn’t require any coding in it by me, it will just store valid targets
The first script you show detects nearby players with the distance value as max distance or is this the distance the mob will stand from the player when attacking?
The second script confirms the torso of players, confirming they are players, stores this as a valid target for script 1 to then confirm with before attacking
does this part put the distance between play and mob into practice to stops the hugs?
local torso = targets[x]:FindFirstChild("Torso")
if torso and (torso.Position - fig.Torso.Position).magnitude < distance then
distance = (torso.Position - fig.Torso.Position).magnitude
selected = torso
I am guessing I could also put the damage and attack animation into the first script is some way or another
I did not include anything with attack distance yet, I was just attempting to show you a improved and more optimized version of seeking the nearest target.
To check if a target is close enough do.
local withinrange = false
while fig.Torso.Parent ~= nil do
if (fig.Torso.Position - Target.Torso.Position).magnitude < 5 then
fig.Humanoid.WalkSpeed = 0.05
withinrange = true
else
fig.Humanoid.WalkSpeed = 16
withinrange = false
end
end
That’s my way of doing it but you can also set walkspeed to 0 and use a BodyGyro instead to make it face it’s target.
For the raycast you can do this.
local ray = Ray.new(fig.Torso.Position, CFrame.new(fig.Torso.Position - Target.Torso.Position).LookVector * 5) --5 is the distance the ray can go.
local hit, pos = workspace:FindPartOnRay(ray, fig, false, true) --false and true not required, it's only for if your game uses Roblox terrain, it's for ignoring water and terrain cells are cubes, etc.
--hit can either be nil or a part we detected.
--pos is the hit position.
if hit then --hit isn't nil?
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid:TakeDamage(Insert_damage_here)
end
end
I did not include debaounces or anything here, I only showed you how raycasts can be done.
The second parameter of Ray.new() is the direction.
I recommend using CFrame.LookVector, it’s easier (for me at least).
And * multiply the LookVector by the distance you want it to go.
Edit: Don’t forget to check if the part that was hit isn’t part of another NPC to avoid NPCs damaging each other by accident.
I am having some issues getting this to work, it would appear that the
Target.Torso.Position
As Torso is “not a valid member of model” but the word Target has a red underline so it isn’t finding its target
in both of the potential attack codes, I’ve played around alittle but cannot seem to find a solution, as I dont fully understand the code haha
loop.
Because x is a number ranging from 1 to table length, it’s a variable used by the loop itself, everytime the loop repeats x is increased by 1
so targets[x] goes on to the next value in the table.
If the loop repeated 5 times then x would be 5.
If you had
targets = { --All targets are players parented in workspace.
workspace.Nooblord,
workspace.iLikeCake,
workspace.SomeRobloxPlayer
}
and did targets[2]
it would return workspace.iLikeCake.
Edit: Ask me if you don’t understand a line of code, I try my best to explain what it does or is for. Sorry if it isn’t clear, I kind of typed this in a hurry.
ohhh ok yeh that makes alot more sense. I am just having trouble assigning the target outside the function so I can then apply the same target to the attack function as well.
if (fig.Torso.Position - Target.Torso.Position).magnitude < 5 then
You see here though, pathfinding merely changes the algorithm that the NPC uses to find and move towards a character - it doesn’t actually solve the root issue. In some cases, you may not even need pathfinding. Keeping the NPC a distance away from the character will work.
I do believe that the Zombie model on the catalog (the one that was part of the Official Model Makers program or whatever) does have an example of this. I don’t quite remember what it does, but it doesn’t have “hugging” behaviour.
Well I think the problem is that its HumanoidRootPart position and as the position is in the right exact middle of the HumanoidRootPart, the npc will keep trying to move to that position. I believe if you make the npc non-collidable with your player, it will just go through the player and be right exact in the middle of the player whilst damaging the player.