Hi! I’m Halacs, I’m currently working on my FPS game. For that, I needed a dummy, that can kill other dummies in my game. How did I do that? I’ll explain it this topic. Here is a quick review of how it looks like (For this tutorial I made a very simple but cool dummy, everyone can understand it)
robloxapp-20220203-2016435.wmv (1.0 MB)
So first, you’ll need a dummy and a weapon. I recomend you use an r15 one, but you can do it with r6 too, but you have to be careful with the joints, there will be 1-1 unique scripts for them, that will be the animating.
So create a dummy
His name shouldn’t be too easy like “Dummy” or “Unit”, because he can’t attack his own named units
(So he will not be able to attack the same named npc, dummy can attack myDummy, but dummy can’t attack dummy)
After you spawned it, he should have a weapon. It’s not really needed, but for more realistic, I added one
If it’s a normal gun, aka doesn’t have any Motor6D part, weld the gun’s part to the good hand (I put it in the right, so I welded it to that hand)
If it has Motor6D joints, create a Motor6D inside of the good hand, and select that good hand as the first part, then select the weapon’s main Motor6D part as the second part.
If you are done at the outside, make sure that the dummy’s HumanoidRootPart is not anchored, or it will not be able to move.
HumanoidRootPart is this part near the UpperTorso, and its transparency is 1
–Scripting
If we are done with the looking of the dummy, we can start scripting it.
Create a script in the dummy.
First, set some variables. They will be good in the future
(Make sure that the script is looking somehow like this in the Dummy.
If the script is in the same spot I used, you can say these
--Basics
local neck = script.Parent.Head.Neck
local NPC = script.Parent
local hum = script.Parent.Humanoid
local head = script.Parent.Head
local lt = script.Parent.LowerTorso
local ut = script.Parent.UpperTorso
After this, you have to decide which hand you used. If your dummy kills with its eyes, you can skip these.
local hand = script.Parent.RightHand
local ruarm = script.Parent.RightUpperArm
local weapon = script.Parent.RightHand.M4A1
local canRun = true
You probably wonder what is ‘canRun’. Well, this is a variable that is true, so your dummy can run. If it’s false, he can’t.
In the weapon, you can have sounds. If you don’t have a weapon / sounds, you can skip this.
--Sounds
local sReload = weapon.Handle.ReloadSound
local sFire = weapon.Handle.FireSound
Now it’s time to make the weapon details!
I used an M4A1, which has 30 ammo / mag, so my script looks somehow like this
--Weapon
local fire = false
local reload = false
local ammo = 30
local firerate = 0.125
local toReload = false
local damage = 20
local reloadTime = 3
local killed = true
Do you wonder what is ‘killed’? It’s a variable for later lines, but I used these.
I think it’s cool when if the dummy fires, it summons a fire effect, so I created one (The poision of it is at an attachment, you can create one inside of your weapon, or set it to a weapon-part)
You can change where the script should spawn this fire object, I let it to you. You can also change it to smoke or both, the choice is yours. Make sure it’s not enabled when spawned, or it will be a blowtorcher-dummy.
After that, make the reload!
We already created an ammo variable, we’ll check if that is 0, and if yes, the dummy willl reload.
But at this point, we should stop. We reached the point where we can animate. If you don’t want to, you don’t have to. But I think it’s cooler if the dummy has a reload + fire animation.
–Fire
robloxapp-20220203-2043516.wmv (122.5 KB)
–Reload
robloxapp-20220203-2045586.wmv (278.8 KB)
Make sure that the animations are not looped.
If you are done, create these 2 animations inside the humanoid, name them ‘Fire’ and ‘Reload’, and paste the animationID in.
(The animator is an important object, don’t delete that, it auto-spawns with the dummy)
After that, make the NPCreload local function (the reason why it’s a local function is because we’ll call this in the future, and normal local variables can hold 1-1 lines only).
local function NPCreload()
if ammo == 0 then --If the character runs out of ammo
hum.WalkSpeed = 1 --then he shouldn't run when he reloads, so the speed should be 1, not 0, because that overrides the animation (the idle overrides it, because if the humanoid.WalkSpeed is 0, then the idle will play, which isn't good for us)
reload = true --Make the reload variable true
toReload = true --Helper variable, not important
local animation = script.Parent.Humanoid.Reload --Animation line. First, find the animation
local humanoid = script.Parent.Humanoid --Then, find the humanoid
local dance = humanoid:LoadAnimation(animation) --Last, load it
dance:Play() --And then play it
sReload:Play() --It's the reload sound, you can skip it, or play it
wait(reloadTime) --Wait for the reload to finish
print("Reloaded") --Helper print : it will say when reloaded
ammo = 30 -- It will set back the ammo to normal
reload = false --He is not reloading from here, so set this variable to false (we'll need this reload variable in the future)
toReload = false
fire = false --When the fire variable is true, he can't shoot (interesting)
hum.WalkSpeed = 16 --Set it back to the normal walkspeed
end
end
At the ‘hum.WalkSpeed = 16’ line you can make a walkspeed variable, and past is there instead of the 16, so you can make it as a value
Now, time to start scripting the dummy as a pro scripter.
You probably wonder, it’s one big While loop
–why? Because first, the dummy will locate a player or a NPC, then he will move to it. While moving, he will create a raycast, and then damage the humanoid.
while true do --While loop begins
wait(1) --Delay time between one kill and searching for a new opponent
for i, v in pairs(workspace:GetChildren()) do --Getting the OBJECTS he can attack
if v.ClassName == "Model" and v.Name ~= NPC.Name then --Getting the RIGHT objects he can attack, and stop at this. 'v.Name ~= NPC.Name' NPC means the dummy itself. If there are 2 units with the same name in the baseplate, they will be friendly to each other, this will be good if you create a military-dummy unit group.
local sHuman = v:FindFirstChild("Humanoid") --Finding the Humanoid, and then
local sRoot = v:FindFirstChild("HumanoidRootPart") --The HumanoidRootPart inside the right objects he can attack. You can have humanoids in some models, but not the rootparts. This is just a 2-step NPC / Player verification
if sHuman and sRoot and canRun == true then --If the model constains the humanoid + rootpart, then checks if
if sHuman.Health ~= 0 then --it's health is not 0, aka not dead
This was the firstpart
The 2nd is a local function, called canShoot. In that, we check if the dummy finds the right opponent, and he is not reloading + chilling.
local function canShoot()
if toReload == false and reload == false and fire == false and ammo ~= 0 then --If he isn't reloading, and can fire, and his ammo is not 0
hum.WalkSpeed = 16 --If his walkspeed went to something else, it will change it back, because he will walk later.
local neck = script.Parent.Head.Neck --Head variables
local NPC = script.Parent --NPC again
local cframe0 = neck.C0
--// CONSTANTS \\--
local DISTANCE = 200 -- How far the NPC can see.
--// SERVICES \\--
local Players = game:GetService("Players")
--// VARIABLES \\--
local origin = script.Parent.Head -- Origin of where the raycast is. Yes, we are creating a raycast. ~What is a raycast? A raycast, to translate it to robloxian language, an invinsible, not object line, that can hit parts, opponents. We will use this
--// MAIN CODE \\--
local ray = Ray.new(origin.Position, origin.CFrame.LookVector * DISTANCE) -- Calculating the raycast.
local hit = workspace:FindPartOnRay(ray, origin.Parent) -- Creating a ray to see what the npc sees, aka if the right opponent is in the distance. (Distance is changeable for you)
if hit then --If the raycast hits something, then we check if
for _, player in pairs(workspace:GetChildren()) do -- We're going to loop through all players to see if the npc sees any of them.
if player and player:IsAncestorOf(hit) then --If the ray hit the right object
if player.Humanoid.Health > 0 then --If the part the ray hit isn't dead, aka have more health than 0
if reload == false then --If the dummy doesn't have to reload, then
killed = true
fire = true --Fire = false, so he has to wait until it goes back to false
fireeffect.Enabled = true --You can skip it if you don't have a fire effect. If yes, this is where the fire will show itself
sFire:Play() --Fire sound plays
player.Humanoid:TakeDamage(damage, 1) --Yes, we are damiging the right opponent, we set a value to the damage variable, it will remove that much health.
if player.Humanoid.Health == 0 then --If the right opponent is killed, then the script will go off
script.Disabled = true --here
wait(1) --wait a random time (you can change it, this is a delay)
script.Disabled = false --And then the script will find a new opponent, and start it again
else -- But if the right opponent is alive, then it ends here, and continoues
end
wait(firerate) --Here. It will wait the firerate time
ammo = ammo - 1 --We will remove 1 ammo, because we used 1 ammo
--hum.WalkSpeed = 1 -- I removed this line, not needed
fireeffect.Enabled = false --Fireeffect will go off
-- Animation script
local animation = script.Parent.Humanoid.Fire --Finds the animation inside the Humanoid
local humanoid = script.Parent.Humanoid --Finds the humanoid
local dance = humanoid:LoadAnimation(animation) --Dance variable
dance:Play() --Plays the animation with the dance variable
fire = false
print("hit") -- Helper print
hum.WalkSpeed = 16
else --If reloading, then end these lines
end
end
end
end
end
else -- If reload == true or the ammo is 0, then the Dummy has to reload. We created this local function already, we just call it here
NPCreload()
end
end
Good job! You made it out alive. Great! The only thing left(in this script) is to move the dummy.
local PathfindingService = game:GetService("PathfindingService") --Getting the PathFindingService. It's one of the best service, it has modifiers for parts, check it out, I love it
while killed == true do --Helper variable check
local path = PathfindingService:CreatePath() --Creating the path for the dummy in the future
path:ComputeAsync(NPC.HumanoidRootPart.Position, v.HumanoidRootPart.Position) --Where to where v.HumanoidRootPart means the enemy character's primarypart
local waypoints = path:GetWaypoints() --The road he will use
for i = 1, 5 do --Move-route. He will use this whole to be the best on the road!
if i <= #waypoints then
if waypoints[i].Action == Enum.PathWaypointAction.Jump then
hum:ChangeState(Enum.HumanoidStateType.Jumping)
end
canShoot() --The canShoot variable we created was a local function, but if we have an enemy, that is the v.HumanoidRootPart, then we can check if we can fire!
script.Parent.Humanoid:MoveTo(waypoints[i].Position)
script.Parent.Humanoid.MoveToFinished:Connect(function() --If the dummy moved to the enemy, then we can stop moving
killed = false --The helper variable ends here
end)
end
end
end
end
end
end
end
end --This line closes the whole while loop
We’ll need 2 scripts. I said r15 or r6, it matters
For r15, take this script and put it in the dummy : (3) R15 Animate Script - Roblox
For r6, take this : (3) Animate Script (R6) - Roblox
Put them in like this :
And one more script! This will be good for the head-rotating. Trust me, it’s cool, important. You can create a script in the dummy, and copy this code into it :
local neck = script.Parent.Head.Neck
local NPC = script.Parent
local cframe0 = neck.C0
--// CONSTANTS \\--
local DISTANCE = 20 -- How far the NPC can see.
--// SERVICES \\--
local Players = game:GetService("Players")
--// VARIABLES \\--
local origin = script.Parent.Head -- Origin of where the raycast is.
--// MAIN CODE \\--
while wait(.125) do --Firarate
local ray = Ray.new(origin.Position, origin.CFrame.lookVector * DISTANCE) -- Calculating the raycast.
local hit = workspace:FindPartOnRay(ray, origin.Parent) -- Creating a ray to see what the npc sees.
if hit then
for _, player in pairs(Players:GetPlayers()) do -- We're going to loop through all players to see if the npc sees any of them.
if player.Character and player.Character:IsAncestorOf(hit) then
print("hit") --Helper print
local unit = -(NPC.PrimaryPart.CFrame.p - player.Character.PrimaryPart.Position).unit
neck.C0 = cframe0 * CFrame.new(Vector3.new(0, 0, 0), unit) * CFrame.Angles(0, -math.rad(NPC.PrimaryPart.Orientation.Y), 0) --Head rotating (cool)
end
end
end
end
And well done, you scripted the dummy
robloxapp-20220203-2112076.wmv (1.2 MB)
If you wanna change something like a ragdoll if dead, you can do it, or destroy the dummy if the humanoid’s health is 0. It’s a very easy, but really cute dummy, who will attack you!
This is my first topic, hope you liked it and it was helpful. If yes, take a reply and say what I have to make better next time. For now, bye!