How to make a NPC that will kill every dummy in your game!

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.

Dummytutorial2

First, set some variables. They will be good in the future
(Make sure that the script is looking somehow like this in the Dummy.

Dummytutorial3

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)

Dummytutorial4

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.

Dummytutorial5

(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. :smiley:

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 :
Dummytutorial6

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 :smiley:

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! :heart_eyes:

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!

6 Likes