Spawning the Player

In this lesson, you will add a spawn point to your map. This can be done by adding a new id value to the ASCII table. This could be set up to handle multiple spawn points, but in this case, it will just be one.

Update ASCII Map

The id for the ASCII map should be something that represents what it is. For a spawn point, we will use the S.

  1. Open up the GenerateMap script.
  2. Modify the ASCII Map by adding a spawn point using the id value S.
map = [[

	1111111111
	1100001101
	1001100101
	1111101101
	1100000001
	1001111001
	1011001001
	1011101011
	1S10000011
	1111111111

]]

Update Tiles Data Table

The Tiles data table will need a new row and a new column for the spawn point. The new column will be used to set the rotation of the spawn so the player is facing the right way. This could be done automatically based on the tiles around the spawn point, but it is nice having full control over it using a preset rotation.

  1. Add a new column called rotation and set the type to Rotation.
  2. Add a new row with an id of S and a key of Spawn.

Update MapBuilder API Script

The MapBuilder API script will need to be updated to communicate to the PlayerServer script by a broadcast to handle the spawning. Later in the course, this method will be changed so that players only spawn after the map and all of its components have been initialized.

Create SpawnPlayers Function

Create a new function called SpawnPlayers that will check the spawn point is valid and then broadcast to the PlayerServer script. Setting a player’s position needs to be done on the server, and because the GenerateMap script is in a Static Context that is requiring the MapBuilder script you can do an environment check to just return if it is not the script running on the server.

When a script is in a Local Context or a Static Context, that script has a copy on the server and the client. Some environment functions that are normally available to you will not work in these contexts. So a broadcast to a script that is in the same environment but outside that context will need to be done. In this case, a broadcast to the PlayerServer script is done because the PlayerServer script is in the server environment, meaning it has permission to set a player’s position.

function MapBuilder.SpawnPlayers(spawnPoint)
	if spawnPoint == nil or not Environment.IsServer() then
		return
	end

	Task.Wait()
	Events.Broadcast("SpawnPlayers", spawnPoint, TILES[MapBuilder.Type.Spawn].rotation)
end
Code language: Lua (lua)

Update Spawn Function

The Spawn function will find a spawn point for the player, and then at the end of the generation will call the SpawnPlayers function. When a tile is spawned the data for the current tile will be checked to see if it a spawn point. If it is a spawn point, then the position of that tile is used as the spawn point. Because the position has already been stored in the params table, then it can be reused for the spawn point.

function MapBuilder.Spawn(map, width, height, size, container)
	if not Environment.IsServer() then
		return
	end

	local spawnPoint = nil

	for row = 1, height do
		for col = 1, width do
			local tileRow, rotation, extraTile
			local neighbors = MapBuilder.GetNeighbors(map, row, col)

			tileRow, rotation, extraTile = MapBuilder.CheckForDeadEnd(map, neighbors, row, col)
			
			if tileRow == nil then
				tileRow, rotation, extraTile = MapBuilder.CheckForWallCorner(map, neighbors, row, col)
			end

			if tileRow == nil then
				tileRow = TILES[map[row][col]]
			end

			if tileRow ~= nil then
				local scale = Vector3.New(size / 100, size / 100, size / 100)

				if tileRow.floor then
					scale.z = 1
				end

				local x = -(row * size) + (height * size) / 2
				local y = (col * size) - (width * size) / 2

				local params = {

					scale = scale,
					position = Vector3.New(x, y, 0),
					rotation = rotation

				}

				container:SpawnSharedAsset(tileRow.tile, params)

				if map[row][col] == MapBuilder.Type.Spawn then
					spawnPoint = params.position
				end	

				if extraTile ~= nil then
					MapBuilder.SpawnExtraTile(extraTile, params, container)
				end
			end
		end
	end

	MapBuilder.SpawnPlayers(spawnPoint)
end
Code language: Lua (lua)

The MapBuilder Script

local ASCIIParser = require(script:GetCustomProperty("ASCIIParser"))
local TILES = require(script:GetCustomProperty("Tiles"))

local MapBuilder = {

	Type = {}

}

for key, row in pairs(TILES) do
	MapBuilder.Type[row.key] = row.id
end

function MapBuilder.Build(opts)
	local map, mapStr = ASCIIParser.BuildMap(opts.map, opts.width, opts.height)

	MapBuilder.Spawn(map, opts.width, opts.height, opts.size, opts.container)
end

function MapBuilder.GetTile(map, row, col)
	local foundRow = map[row]

	if foundRow then
		return foundRow[col]
	end

	return nil
end

function MapBuilder.GetNeighbors(map, row, column)
	return {

		NORTH = MapBuilder.GetTile(map, row - 1, column),
		SOUTH = MapBuilder.GetTile(map, row + 1, column),
		EAST = MapBuilder.GetTile(map, row, column + 1),
		WEST = MapBuilder.GetTile(map, row, column - 1)

	}
end

function MapBuilder.CheckForWallCorner(map, neighbors, row, col)
	local tileRow = nil
	local extraTile = nil
	local rotation = Rotation.New()

	if neighbors.WEST == MapBuilder.Type.Wall and neighbors.NORTH == MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall and neighbors.EAST ~= nil and neighbors.SOUTH ~= nil then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = 0
	elseif neighbors.NORTH == MapBuilder.Type.Wall and neighbors.EAST == MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall and neighbors.WEST ~= nil and neighbors.SOUTH ~= nil then
		tileRow = TILES[MapBuilder.Type.WallCorner]
	 	rotation.z = 90
	elseif neighbors.EAST == MapBuilder.Type.Wall and neighbors.SOUTH == MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall and neighbors.NORTH ~= nil and neighbors.WEST ~= nil then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = 180
	elseif neighbors.SOUTH == MapBuilder.Type.Wall and neighbors.WEST == MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall and neighbors.EAST ~= nil and neighbors.NORTH ~= nil  then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = 270
	end

	if tileRow ~= nil then
		extraTile = TILES[MapBuilder.Type.Floor]
	end

	return tileRow, rotation, extraTile
end

function MapBuilder.CheckForDeadEnd(map, neighbors, row, col)
	local tileRow = nil
	local extraTile = nil
	local rotation = Rotation.New()

	if neighbors.WEST == MapBuilder.Type.Wall and neighbors.NORTH == MapBuilder.Type.Wall and neighbors.EAST == MapBuilder.Type.Wall and neighbors.SOUTH ~= MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = 45
	elseif neighbors.NORTH == MapBuilder.Type.Wall and neighbors.EAST == MapBuilder.Type.Wall and neighbors.SOUTH == MapBuilder.Type.Wall and neighbors.WEST ~= MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = 135
	elseif neighbors.EAST == MapBuilder.Type.Wall and neighbors.SOUTH == MapBuilder.Type.Wall and neighbors.WEST == MapBuilder.Type.Wall and neighbors.NORTH ~= MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = -135
	elseif neighbors.SOUTH == MapBuilder.Type.Wall and neighbors.WEST == MapBuilder.Type.Wall and neighbors.NORTH == MapBuilder.Type.Wall and neighbors.EAST ~= MapBuilder.Type.Wall and map[row][col] ~= MapBuilder.Type.Wall then
		tileRow = TILES[MapBuilder.Type.WallCorner]
		rotation.z = -45
	end

	if tileRow ~= nil then
		extraTile = TILES[MapBuilder.Type.Floor]
	end

	return tileRow, rotation, extraTile
end

function MapBuilder.SpawnExtraTile(extraTile, params, container)
	if extraTile == nil then
		return
	end

	if MapBuilder.Type.Floor == extraTile.id then
		params.scale.z = 1
		params.rotation = Rotation.New()
	end

	container:SpawnSharedAsset(extraTile.tile, params)
end

function MapBuilder.SpawnPlayers(spawnPoint)
	if spawnPoint == nil or not Environment.IsServer() then
		return
	end

	Task.Wait()
	Events.Broadcast("SpawnPlayers", spawnPoint, TILES[MapBuilder.Type.Spawn].rotation)
end

function MapBuilder.Spawn(map, width, height, size, container)
	if not Environment.IsServer() then
		return
	end

	local spawnPoint = nil

	for row = 1, height do
		for col = 1, width do
			local tileRow, rotation, extraTile
			local neighbors = MapBuilder.GetNeighbors(map, row, col)

			tileRow, rotation, extraTile = MapBuilder.CheckForDeadEnd(map, neighbors, row, col)
			
			if tileRow == nil then
				tileRow, rotation, extraTile = MapBuilder.CheckForWallCorner(map, neighbors, row, col)
			end

			if tileRow == nil then
				tileRow = TILES[map[row][col]]
			end

			if tileRow ~= nil then
				local scale = Vector3.New(size / 100, size / 100, size / 100)

				if tileRow.floor then
					scale.z = 1
				end

				local x = -(row * size) + (height * size) / 2
				local y = (col * size) - (width * size) / 2

				local params = {

					scale = scale,
					position = Vector3.New(x, y, 0),
					rotation = rotation

				}

				container:SpawnSharedAsset(tileRow.tile, params)

				if map[row][col] == MapBuilder.Type.Spawn then
					spawnPoint = params.position
				end	

				if extraTile ~= nil then
					MapBuilder.SpawnExtraTile(extraTile, params, container)
				end
			end
		end
	end

	MapBuilder.SpawnPlayers(spawnPoint)
end

return MapBuilder
Code language: Lua (lua)

Update PlayerServer Script

The PlayerServer script needs to be updated so it can listen for the broadcast and handle spawning the players at the position and rotation passed in.

When testing the game after modifying the PlayerServer script, it is recommended to test in Single Player Preview mode for now. Later on in the course you will be modifying this script again that will work better for Multiplayer. If you do test in Multiplayer Preview Mode, you may find the player character spawning under the generated map.

Create SpawnPlayers Function

Create a new function called SpawnPlayers that will receive the position and rotation. The function will loop through all the players in the game and set the world position and world rotation. A small up direction of 150 is added to the position to make sure the player is above the floor tile, or they will fall through.

local function SpawnPlayers(position, rotation)
	for _, player in ipairs(Game.GetPlayers()) do
		player:SetWorldPosition(position + (Vector3.UP * 150))
		player:SetWorldRotation(rotation)
	end
end
Code language: Lua (lua)

Connect SpawnPlayers Event

Connect up the SpawnPlayers event to call the SpawnPlayers function.

Events.Connect("SpawnPlayers", SpawnPlayers)
Code language: Lua (lua)

The PlayerServer Script

local function ActionPressed(player, action)
	if(action == "Toggle Fly Mode") then
		if(player.isFlying) then
			player:ActivateWalking()
		else
			player:ActivateFlying()
		end
	end
end

local function SpawnPlayers(position, rotation)
	for _, player in ipairs(Game.GetPlayers()) do
		player:SetWorldPosition(position + (Vector3.UP * 150))
		player:SetWorldRotation(rotation)
	end
end

Input.actionPressedEvent:Connect(ActionPressed)
Events.Connect("SpawnPlayers", SpawnPlayers)
Code language: Lua (lua)

Test the Game

Test the game to make sure the player is spawned in the correct position. If you have a different map layout, you may need to adjust the rotation in the Tiles data table for the spawn point.

9 Comments
Collapse Comments

Hello,
I have a problem spawing the character over the ground, even changing the value inside the function SpawnPlayers of the PlayerServer.
I’ve checked back my hierarchy and everything but cant happen to make it.

CommanderFoo (Administrator) June 6, 2022 at 6:41 am

Hello,

Make sure you have an entry in the Tiles table. There should be an entry with an id of `S` and the key should be `Spawn`.

Make sure no spawn point exists in the world either.

Is the player spawning at the world center position (0, 0, 0)?

Hello CommanderFoo, thank’s for your reply.
I verified everything but sadly I wasn’t able to fix it.
I made an image for you to see if you can detect something wrong in my project.
https://imgur.com/Z4ha7qN

CommanderFoo (Administrator) June 7, 2022 at 3:55 am

Hello,

Would you be able to send me your project on Discord?

https://discord.coregames.com

I can be found under the same name as here. Thanks 🙂

Sure, thank you. 🙂

CommanderFoo (Administrator) June 7, 2022 at 6:04 am

Thanks. So the error is on my part. I should mention that at the moment it is suited for single player preview, but later on in the course it gets updated. Will add a note to the lesson. Thanks very mucm 🙂

Hello
The spawning works but i get this error code
Error running Lua task: [45150F3CAED9AF47] PlayerServer:14: stack index 2, expected Rotation, received nil: value is not a valid userdata (bad argument into ‘void(Player, Rotation)’)
Not sure where i went wrong

CommanderFoo (Administrator) July 6, 2022 at 2:40 am

Make sure you are broadcasting the rotation in the MapBuilder script. It comes from the TILES table (should be a column called `rotation`).

`Events.Broadcast(“SpawnPlayers”, spawnPoint, TILES[MapBuilder.Type.Spawn].rotation)`

you have an error in the script “MapBuilder” function “Spawn” line 155, if “map[row][col]” is equal to “MapBuilder.Type.Spawn” then “tileRow.tile” will be equal to “nil” . Spawn points cannot be created dynamically.

Leave a Comment

Scroll to Top