How to make a chasing NPC head like a Running Head?

I’m trying to make an NPC head that will work the same way as in the Escape Running Head game.

There is a Felipe Killer model of such a head in the ToolBox, but this head does not behave exactly as it should. For some reason, this head only chases one player on the server, and also this head does not return to the place where it originally stood when it is not chasing players.

:arrow_forward: This model is from the ToolBox:
Felipe Killer.rbxm (14.2 KB)

:arrow_forward: Here is the script from this model:

--DuruTeru
	
local SearchDistance = 100000 	-- How far a player can be before it detects you

local ZombieDamage = 0		-- How much damage the Zombie inficts towards the player

local canWander = false
local WanderX, WanderZ = 30, 30
-- 	How many studs the zombie can wander on the x and z axis in studs

--]]


function getHumanoid(model)
	for _, v in pairs(model:GetChildren()) do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude


local pfs = game:GetService("PathfindingService")

function GetPlayerNames()
	local players = game:GetService('Players'):GetChildren()
	local name = nil
	for _, v in pairs(players) do
		if v:IsA'Player' then
			name = tostring(v.Name)
		end
	end
	return name
end

function GetPlayersBodyParts(t)
	local torso = t
	if torso then
		local figure = torso.Parent
		for _, v in pairs(figure:GetChildren()) do
			if v:IsA'Part' then
				return v.Name
			end
		end
	else
		return "HumanoidRootPart"
	end
end

function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torso = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v:FindFirstChild'HumanoidRootPart'
			if (charRoot.Position - part).magnitude < SearchDistance then
				torso = charRoot
			end
		end
	end
	return torso
end

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end

-- no touchy
local path
local waypoint
local oldpoints
local isWandering = 0

if canWander then
	spawn(function()
		while isWandering == 0 do
			isWandering = 1
			local desgx, desgz = hroot.Position.x + math.random(-WanderX, WanderX), hroot.Position.z + math.random(-WanderZ, WanderZ)
			human:MoveTo( Vector3.new(desgx, 0, desgz) )
			wait(math.random(4, 6))
			isWandering = 0
		end
	end)
end

while wait() do
	local enemytorso = GetTorso(hroot.Position)	
	if enemytorso ~= nil then -- if player detected
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end
		
		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;
		
		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)
		
		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end
			
			if connection then
				connection:Disconnect()
			end
			
		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then -- if player not detected
		isWandering = 0
		path = nil
		waypoint = nil
		human.MoveToFinished:Wait()
	end
end

Perhaps the script in this model needs to be completely changed… I’ve been trying to do this for a very long time, but it didn’t work out.

:arrow_forward: What exactly do I need?

  1. It is necessary for the NPC head to pursue the one who is closer to it. It is important that players can jump on high blocks or vice versa fall from a high cliff, where the head cannot get, and then the head should immediately go to another player even if he is very far from it, but nothing blocks the way to this player. Similarly, the head should behave if it killed the player it was chasing.

  2. If all the players are out of reach, then the NPC’s head should be able to return to the point where it originally stood (that is, where I put the NPC in roblox studio, the NPC should return there in the game). The head should stay there until the player(s) are in range again.

  3. Of course, when chasing a player, the head must be able to avoid obstacles in its path.

:arrow_forward: Please watch the video, this is how an NPC’s head should behave:

:arrow_forward: To make it even clearer exactly how an NPC’s head should act, try to log into this game:
Escape Running Head

I don’t understand the principle by which NPCs work in this game. Is pathfinding or raycasting used here? Or pathfinding and raycasting together? Or neither at all?

Maybe there are other NPC models with this behavior in the ToolBox.
Thank you so much for any help!:pray:

:arrow_forward: :mailbox:If you would like to offer cooperation in the development of this NPC, be sure to write to me about it in a message!

well, its basically a roblox character with a big head welded to its hum root part that spam walks to your position

1 Like

just find the closest player in view and walk to that position

1 Like