Spawn System – Customizable Spawn Logic For Roblox Players

This is a small module I created for a few of my games, and thought I would release it here for other developers to use/reference.

About:
This Spawn-System allows you to customize where a Roblox Character will respawn without having to fight the default Roblox Spawn-System.

Toolbox Link:

Usage:
Simply place it inside Server Script Service to run. Require it via:

local SpawnSystem = require(game.ServerScriptService:FindFirstChild("SpawnSystem", true))

By default the Spawn-System attempts to emulate Roblox’s native default spawn logic via:

  1. Check if Player has a RespawnLocation set
  2. Get List of all valid Team spawns and Neutral spawns
  3. Pick spawn randomly from list

Examples:
The logic required to pick a spawn location can be easily overwritten:

-- Custom spawn location
SpawnSystem.ChooseSpawn = function(player: Player) : Vector3|CFrame|SpawnLocation?
	return Vector3.new(0, 100, 0)
end

Additionally, if you wish to run custom logic upon character spawning, this can be done easily as well:

-- Custom teleportation callback (eg. If Humanoid Rootpart is not used)
SpawnSystem.RequestTeleport = function(player: Player, transform: CFrame)
	CustomTeleportRemote:FireClient(player, transform)
end

You can easily query all spawn locations (neutral and team spawns) in the game:

-- Print spawn locations
print(SpawnSystem.SpawnLocations)
print(SpawnSystem.NeutralSpawns)
print(SpawnSystem.TeamSpawns)

For convenience you can also force respawn players:

-- Respawn all players in the game
SpawnSystem:RespawnPlayers(game.Players:GetPlayers())

As an additional example, I am using this module in one of my current projects to “pick a spawn that is farthest away from all players”, great for Free-For-All gamemodes:

local function getFreshSpawnLocation(IgnorePlayers)
	local SpawnsFarEnoughAway = {}
	local FarthestSpawn = nil
	local FarthestDistance = 0
	for _,v in pairs(Spawns) do
		-- Get nearest player TO this spawn
		local NearestPlayer = nil
		local NearestPlayerDistance = math.huge
		for _,p in pairs(game.Players:GetPlayers()) do
			local CanProcess = true
			for _,i in pairs(IgnorePlayers) do
				if ( i == p ) then
					CanProcess = false
					break
				end
			end

			if ( not CanProcess ) then
				continue
			end

			local Position = p.Character and p.Character.PrimaryPart and p.Character.PrimaryPart.Position or Vector3.new()
			local DistanceToSpawn = (Position - v.Position).Magnitude
			if ( DistanceToSpawn < NearestPlayerDistance ) then
				NearestPlayerDistance = DistanceToSpawn
				NearestPlayer = p
			end
		end

		-- Get spawn that is far away
		if ( NearestPlayerDistance > 800 ) then
			SpawnsFarEnoughAway[#SpawnsFarEnoughAway+1] = v
		end

		-- Get spawn with FARTHEST nearest player
		if ( NearestPlayerDistance > FarthestDistance ) then
			FarthestDistance = NearestPlayerDistance
			FarthestSpawn = v
		end
	end

	-- If we have spawns that are "far enough" away, pick one
	if ( #SpawnsFarEnoughAway > 0 ) then
		return SpawnsFarEnoughAway[math.random(#SpawnsFarEnoughAway)].Position
	end

	-- If we get here, no spawns are configured
	if ( not FarthestSpawn ) then
		error("Could not find spawn location for player")
	end

	-- Return the spawn that is farest away (no spawns with a player > 800 studs away OOF)
	return FarthestSpawn.Position
end

SpawnSystem.ChooseSpawn = function(player: Player)
	return getFreshSpawnLocation({player})
end
10 Likes

Pretty neat, i assume i can also use this for non-startercharacter/default character spawning. Like after i clone a rig, assign it to player, give network ownership, etc then use this to get a spawn location and then parent it workspace.

Altrough i suppose spawning a bunch of SpawnLocations would also do the trick, or even having bunch of spawns in a folder, getting all the children and then picking a random one by math.random.

Still this is very nice, good work!

Since default roblox spawning is emulated with this module, you could do something like:

local player = ...
local rig = ... -- Clone & setup your rig
SpawnSystem:RespawnPlayer(player) -- Internally calls ChooseSpawn function, and will teleport character to the spawn location
rig.Parent = workspace

If you wanted to additionally override which spawn is chosen, you can also do:

SpawnSystem.ChooseSpawn = function(player: Player)
	local spawns = workspace.Spawns:GetChildren()
	return spawns[math.random(#spawns)]
end
-- under onRespawn
for i=1,12 do
	module.RequestTeleport(player, spawnTransform)
	game:GetService("RunService").Heartbeat:Wait()
end

Little underknown scripting fact, but you can use task.defer to properly change instances when they are parent locked.

Here is what I do (Make sure to call Player/RequestStreamAroundAsync beforehand if you have StreamingEnabled, otherwise you may get paused gameplay).

task.defer(
	workspace.PivotTo, -- character.PivotTo works too
	character,
	CFrame.new(0, 100, 0)
)
2 Likes

This works well, thank you.

I switched the code to:

task.defer(module.RequestTeleport, player, spawnTransform)

Great system!
This will help alot.

1 Like