This document is about: QUANTUM 3
SWITCH TO

5 - Player Spawning

Overview

With the player character entity created the next steps are spawning a player character for each player that joins the game and linking up the input of the player to the entity.

Player Prefab

Currently, the ship is a scene object. Quantum can spawn entities at runtime similar to how prefabs can be spawned at runtime in a single player Unity game. Turn the ship into a prefab by dragging the AsteroidsShip GameObject from the scene into the Resources folder. After doing so, delete the AsteroidsShip from the scene.

the asteroidsship prefab

Note how a EntityPrototype and a EntityView file have been created nested into the prefab. These are used by Quantum to spawn the entity and link it up to the Unity view.

Quantum has the concept of players. Each client can own one or multiple players. However, Quantum does not have a built-in concept of a player object/avatar. Each player that is connected to the game is given a unique ID. This ID is called a PlayerRef. To link an entity to a specific player we will create a PlayerLink component that contains the PlayerRef of the owner.

Create a PlayerLink.qtn file in the Assets/QuantumUser/Simulation/Platformer folder and add the following code to it:

C#

component PlayerLink
{
    player_ref PlayerRef;
}

Player Data

To dynamically spawn a character we need to let the gameplay code know which entity it should create. Quantum has the concept of player data. Player data allows each player to pass information into the simulation upon connection. It can be information such as which character a player has selected or which skin they are using.

By default the player data contains an Avatar entity and a Player Nickname, however the data can also be extended in the Photon/QuantumUser/Simulation/RuntimePlayer.User.cs file.

To spawn the player entity we will use the predefined avatar field. An AssetRefEntityPrototype is the Quantum equivalent to a prefab.

When entering play mode the Quantum simulation automatically runs. This is driven by the QuantumRunnerLocalDebug component on the QuantumDebugRunner GameObject in the QuantumGameScene scene in Unity. This component is used to run a single player version of the Game locally for development purposes.

The QuantumLocalRunnerDebug allows to simulate any numbers of local players. In the QuantumLocalRunnerDebug under Local Players drag and drop the AsteroidsShipEntityPrototype file that can be found under the AsteroidsShip prefab into the Player Avatar field of the player.

inspector view of the quantumdebugrunner

Now that the prefab is linked up to the player data all that is left is to write code to spawn the entity when a player joins.

Spawning Player Objects

Create a new ShipSpawnSystem.cs class. Add the following code:

C#

namespace Quantum.Asteroids
{
    public unsafe class ShipSpawnSystem : SystemSignalsOnly, ISignalOnPlayerAdded
    {
        public void OnPlayerAdded(Frame frame, PlayerRef player, bool firstTime)
        {
            {
                RuntimePlayer data = frame.GetPlayerData(player);

                // resolve the reference to the avatar prototype.
                var entityPrototypAsset = frame.FindAsset<EntityPrototype>(data.PlayerAvatar);

                // Create a new entity for the player based on the prototype.
                var shipEntity = frame.Create(entityPrototypAsset);

                // Create a PlayerLink component. Initialize it with the player. Add the component to the player entity.
                frame.Add(shipEntity, new PlayerLink { PlayerRef = player });
            }
        }
    }
}

This code creates the ship entity when a player joins and links it up to the player by adding a PlayerLink component to it.

Signals are similar to events in C#. They are used by Quantum systems to communicate with each other. Quantum comes with a lot of existing signals such as the ISignalOnPlayerAdded which gets called after a player has joined the session and shared their player data.

SystemSignalsOnly is a special type of system that doesn't have an Update routine, which makes it leaner and can be used solely for reacting to signals.

Add the ShipSpawnSystem to the list of systems in the AsteroidsShipConfig asset after the AsteroidsShipSystem.

Update the Movement

Until now the ship movement in the AsteroidsShipSystem always moved using inputs from player 0:

C#

var input = f.GetPlayerInput(0);

Replace it with the following code to get the input from the linked player:

C#

Input* input = default;
if(f.Unsafe.TryGetPointer(filter.Entity, out PlayerLink* playerLink))
{
    input = f.GetPlayerInput(playerLink->PlayerRef);
}

Note that the filter has not been adjusted so the system will still filter for entities with a PhysicsBody2D but no PlayerLink component. In this case it will use the default value for the input. This results in no movement besides gravity being applied. This pattern makes it easy to add AI controlled ships by passing in input from a different source.

Getting a component using TryGet in Quantum is very fast O(1) because Quantum uses a sparse set ECS.

f.Unsafe provides access to Quantum's unsafe API which is generally faster and more convenient to use than the no-pointer counterparts. However, if you want to avoid using pointers the safe API such as f.Get can be used.

Switch to Unity and enter play mode. The ship will be spawned and it reacts to keyboard inputs.

Back to top