Pathfinding & Humanoid Grabbing

Hey to those of you who decided to read this, firstly, thank you. I’m wondering what may be the most efficient and effective way of making a unique wave based system, where within a certain period of time I can retrieve and clone different zombies, that’ll then target different players with in it’s closest range (basically pathfinding)

I haven’t attempted anything I might’ve thought of so far because I lack knowledge and I’d like to know what is suggested despite how complicated it may be, before I begin that is. I can be a bit more descriptive on everything if you’re not quite getting it just let me know.

Before I go further, I would just like to clarify my understanding on what you’re trying to achieve. Are you trying to make zombies (that are added into the map at certain times) that follow the nearest player, and possibly attack them?

Sorry for the late response, thanks for responding. I, preferably, am trying to spawn zombies into the map, throughout a period of time, however there is indeed a cap that they should not go pass (keep spawning upon) during the period of the ongoing wave, and yes they will in fact target the nearest player and attack them.

I’ve been struggling with my intermission script unfortunately, if you’d be willing to help me fix some of my errors, that’d be great.

If all you want is an idea for how this may be done I can layout the steps for you for how I might do it. It may not be 100% accurate and some of what I suggest may not work, but that’s part of the development process in the first place. Resources/examples will be below the listed steps.

So, with that said, here are the steps for how I’d do it:

  1. An important step for performance, you want to have a way to cache the zombies used. This will prevent possible performance issues due to cloning and reparenting a zombie whenever you need a new one.
  2. Second, implement some sort of rag doll system for the zombies that they can recover from. This is necessary to implement resource caching (step one).
  3. You need a system for spawning the zombies in. This can be done using an exponential function and a hard cap for how many zombies you want to spawn in at the moment (as the round progresses) and how many you want in the map at once (in total). You can use PVInstance:PivotTo for spawning the zombies in and moving them back to the cache (step one).
  4. With a system in-place for ragdolling the zombies, now you want a way to damage the zombies without killing them (assuming you want to have the players be able to damage them). This can be done by creating a single function that clamps the health value to prevent the zombie from dying and recognizes when a zombie should be dead.

    4.1. Upon “death”, you can ragdoll the zombie and prevent it from damaging/seeking out players to create the appearance of death. You will also most likely need to use Collision Filtering to keep the corpse from touching the players while in this state.

    4.2. When removing the zombie corpse, add it back into the storage of your resource cache (step one) and then reset its limbs/joints and collision group so that it when it is spawned back into the map, it is already ready to go.
  5. Once the zombie is spawned in, you want to find the closest player to it. There are several ways to do this, but the method remains the same. Iterate through an array containing all the players who are currently playing, check if they have a character, and, if they do, check the distance between them and the zombie. Then return the player with the shortest distance. You may also want to implement some sort of detection system to trigger the pathfinding, but I’ll leave that up to you.
  6. With everything else taken care of, the only thing left is pathfinding. This can be difficult to configure, especially if you are new to this. Instead of providing resources or code samples for this one, I highly recommend you

Resources:
Here are the resources you can implement into your project.

  • Resource-Cacheing: A good resource you can use for this is PartCache. Instead of Instancing/Destroying new/old parts, it instead just CFrames them far away. While some of the performance benefits this is meant to deliver won’t actually be delivered (since we are creating zombies instead of projectiles or the like), it will provide some. Once feature it provides though is dynamic scaling, meaning that it will automatically create more clones if they are needed. This is useful for later rounds when (I’m guessing) the zombie count increases. You will probably need to edit it to work with a model instead of just parts though.
  • Finding the nearest player:
-- Returns the position of the nearest player
local function findNearestPlayer(zombie: Model, activePlayers: {Player}): Vector3
	local zombiePos: Vector3 = zombie:GetPivot().Position
	
	local nearestDistance: number = math.huge
	local nearestPos: Vector3
	
	for _, player in pairs(activePlayers) do
		local character: Model? = player.Character
		
		if character then
			local playerPos: Vector3 = character:GetPivot().Position
			local distance: number = (playerPos - zombiePos).Magnitude
			
			if distance < nearestDistance then
				nearestDistance = distance
				nearestPos = playerPos
			end
		end
	end
	return nearestPos
end
3 Likes

I highly recommend you check out the part-cache resource I linked. It will only take a few modifications to make work for you. If you want to make your own though, then I would recommend storing it in a table and then parenting it to nil, as that will remove all internal event listeners. Otherwise just anchor it and cframe it far away.

Using the global wait command is inefficient and has been superseded by the task library. Use task.wait() instead. Or, alternatively (preferred) you can use RunService.Heartbeat:Connect(function() --code within function-- end)

You should have some variables outside of your loop and use these to keep track of the time of the round and the elapsed time in the round. Then use these for handling the spawning logic.