This document is about: QUANTUM 3
SWITCH TO

This page is a work in progress and could be pending updates.

Quantum 104 - Player Spawning


Available in the Gaming Circle and Industries Circle
Circle

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 Character Prefab

Currently, the character 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. Create a Prefabs folder under QuantumUser/Resources/. Then create a PlayerCharacter prefab by dragging the PlayerCharacter GameObject from the scene into the Prefabs folder. After doing so, delete the PlayerCharacter from the scene.

the player 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 Player;
}

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.

IMPORTANT: All data added to the RuntimePlayer class must be serialized in the SerializeUserData function. The BitStream provides serialization for all basic types.

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 PlayerCharacterEntityPrototype file that can be found under the PlayerCharacter 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 PlayerSpawnSystem.cs class. Add the following code:

C#

namespace Quantum
{
    unsafe class PlayerSpawnSystem : SystemSignalsOnly, ISignalOnPlayerDataSet
    {
        public void OnPlayerDataSet(Frame frame, PlayerRef player)
        {
            var data = frame.GetPlayerData(player);

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

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

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

            // Offset the instantiated object in the world, based in its ID.
            if (frame.Unsafe.TryGetPointer<Transform3D>(entity, out var transform))
            {
                transform->Position.X = player._index;
            }
        }
    }
}

This code creates the character 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 ISignalOnPlayerDataSet 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 PlayerSpawnSystem to the list of systems in the SystemSetup.User.cs after the MovementSystem.

Update MovementSystem

Until now the MovementSystem 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->Player);
}

Note that the filter has not been adjusted so the system will still filter for entities with a CharacterController 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.

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

Switch to Unity and enter play mode. A player character will be spawned and the player reacts to keyboard inputs.

Back to top