Welcome to my first tutorial!
Note: I’m not too good at English
Today, we will make a smart zombie, that detects the nearest player, attacks them, animation.
Without any further ado, let’s jump right into it
1. Create a dummy
Open the plugins tab, then find “Build Rig”
Click it, and use R6 or R15.
For me, I want R6
you can customize it, but for me, I’ll leave it as it is.
2. Create a hitbox
First, we need to create a hitbox, this hitbox will damage the player if the player is inside of it.
Cover all the body of the dummy
Make the Transparency = 1, CanCollide = false and Anchored = true
Then, the hitbox will be invisible
Next, weld the Hitbox to the character’s HumanoidRootPart
Create a weld in the HumanoidRootPart, set Part0 to HumanoidRootPart and Part1 to Hitbox.
3. Scripting time!!
Firstly, let’s create a server script in the dummy.
And then, let’s make some variable
local PathfindingService = game:GetService("PathfindingService")
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
we will use PathfindingService to find a path to the player, we are using some WaitForChild, just to be safe.
Next, we need to create a function that gives us the nearest player.
function getClosestPlayer()
local Players = game.Players:GetPlayers()
local nearestPlayer = Players[1]
if #Players > 1 then
for i = 2, #Players do
if nearestPlayer.Character == nil then
nearestPlayer.CharacterAdded:Wait()
end
if Players[i].Character == nil then
Players[i].CharacterAdded:Wait()
end
local distance1 = (nearestPlayer.Character.HumanoidRootPart.Positioon - rootPart.Position).Magnitude
local distance2 = (Players[i].Character.HumanoidRootPart.Position - rootPart.Position).Magnitude
if distance2 < distance1 then
nearestPlayer = Players[i]
end
end
end
return nearestPlayer
end
What did this function do?
well, It will create the nearest player variable. for the first time, it will set it to the first player. After that, it will do a for loop if the player count is greater than 1. Then, It will compare the current nearest player position to a new one, if the current one is greater than the new one then overwrites it, and then that function will return the closest player.
next, create a while loop to repeatedly find a new path and make it walk towards the closest player.
coroutine.wrap(function()
while wait() do
local closestPlayer = getClosestPlayer()
if closestPlayer.Character == nil then
closestPlayer.CharacterAdded:Wait()
end
path:ComputeAsync(rootPart.Position, closestPlayer.Character.HumanoidRootPart.Position)
local waypoints = path:GetWaypoints()
for _, waypoint in pairs(waypoints) do
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
end
end)()
What this will do?
It will compute the path that we just created, and then move the humanoid into each waypoint.
Next, we need to create a region3 that will detect if the player is inside the hitbox.
First, create a while loop
while wait() do
end
now, let’s create the hitbox variable at the top of the script!
local PathfindingService = game:GetService("PathfindingService")
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
local hitbox = character:WaitForChild("Hitbox") -- This is that we just created
Let’s begin detecting players!
local debounce = false
while wait() do
local found = false
local region = Region3.new(hitbox.Position - (hitbox.Size / 2), hitbox.Position + (hitbox.Size / 2))
local parts = game.Workspace:FindPartsInRegion3WithIgnoreList(region, script.Parent:GetDescendants())
local model
for _, part in pairs(parts) do
model = part:FindFirstAncestorOfClass("Model")
if model then
if model:FindFirstChild("Humanoid") then
local player = game.Players:GetPlayerFromCharacter(model)
if player then
found = true
break
end
end
end
end
if found then
if not debounce then
model.Humanoid.Health -= 10
debounce = true
wait(1)
debounce = false
end
end
end
Note: I’m adding debounce variable there
What this will do? It will create a region3 with our hitbox, I won’t explain it now maybe read this would help.
then, we will get every part inside of that region. We will check if the ancestor of that part is a model. If it’s a model, then it will check if that model has a humanoid. If that model has a humanoid, then check if that character is a player. If it’s a player, then set the found variable to true.
Then after that for loop, it will check if the found variable is true. If it’s true, then damage the player.
Test it yourself, my computer can’t upload videos
4. Animating
Next, we need to animate the zombie. The animation you need is Walking and Attack
I’m already making my animation.
To make it yourself, Go to plugins → animation editor
Create new animation.
Animate it.
Before you export it, here is something important
- The Walking animation priority is set to Movement
- The Attack animation priority is set to Action
5. Scripting the animation!
This is the last step.
Go to our script. then write the following
local runId = 0000 -- Replace this with your anim id
local attackId = 0000 -- Replace this also
local run = Instance.new("Animation")
run.Name = "Run"
run.AnimationId = "rbxassetid://"..runId
run.Parent = workspace
run = humanoid:LoadAnimation(run)
local attack = Instance.new("Animation")
attack.Name = "Attack"
attack.AnimationId = "rbxassetid://"..attackId
attack.Parent = workspace
attack = humanoid:LoadAnimation(attack)
run:Play()
attack:Play()
Now! that’s it! I hope you guys enjoyed it!
Here’s our full script
local PathfindingService = game:GetService("PathfindingService")
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
local hitbox = character:WaitForChild("Hitbox")
local runId = 0000 -- Replace this with your anim id
local attackId = 0000 -- Replace this also
local run = Instance.new("Animation")
run.Name = "Run"
run.AnimationId = "rbxassetid://"..runId
run.Parent = workspace
run = humanoid:LoadAnimation(run)
local attack = Instance.new("Animation")
attack.Name = "Attack"
attack.AnimationId = "rbxassetid://"..attackId
attack.Parent = workspace
attack = humanoid:LoadAnimation(attack)
run:Play()
attack:Play()
function getClosestPlayer()
local Players = game.Players:GetPlayers()
local nearestPlayer = Players[1]
if nearestPlayer == nil then return end
if #Players > 1 then
for i = 2, #Players do
if nearestPlayer.Character == nil then
nearestPlayer.CharacterAdded:Wait()
end
if Players[1].Character == nil then
Players[1].CharacterAdded:Wait()
end
local distance1 = (nearestPlayer.Character.HumanoidRootPart.Position - rootPart.Position).Magnitude
local distance2 = (Players[i].Character.HumanoidRootPart.Position - rootPart.Position).Magnitude
if distance1 > distance2 then
nearestPlayer = Players[i]
end
end
end
return nearestPlayer
end
local path = PathfindingService:CreatePath({
["AgentCanJump"] = false,
})
coroutine.wrap(function()
while wait() do
local closestPlayer = getClosestPlayer()
if closestPlayer.Character == nil then
closestPlayer.CharacterAdded:Wait()
end
path:ComputeAsync(rootPart.Position, closestPlayer.Character.HumanoidRootPart.Position)
local waypoints = path:GetWaypoints()
for _, waypoint in pairs(waypoints) do
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
end
end)()
local debounce = false
while wait() do
local found = false
local region = Region3.new(hitbox.Position - (hitbox.Size / 2), hitbox.Position + (hitbox.Size / 2))
local parts = game.Workspace:FindPartsInRegion3WithIgnoreList(region, script.Parent:GetDescendants())
local model
for _, part in pairs(parts) do
model = part:FindFirstAncestorOfClass("Model")
if model then
if model:FindFirstChild("Humanoid") then
local player = game.Players:GetPlayerFromCharacter(model)
if player then
found = true
break
end
end
end
end
if found then
if not debounce then
model.Humanoid.Health -= 10
debounce = true
wait(1)
debounce = false
end
end
end
Use this if you have a problem.
If there is still a problem here’s the .rblx file!
Tutorial.rbxl (45.8 KB)