This document is about: QUANTUM 2
SWITCH TO

Quantum 104 - Player Spawning

概述

建立玩家角色實體後,下一步是針對各個加入遊戲的玩家來生成一個玩家角色,並且鏈接玩家的輸入到實體。

玩家角色預製件

目前角色是一個場景物件。Quantum可以在運行階段生成實體,其類似於在單一玩家Unity遊戲中的運行階段時生成預製件的方式。建立一個PlayerCharacter預製件,方法是從從場景拖放PlayerCharacter遊戲物件到Resources/DB資料夾之中。這樣做之後,從場景刪除PlayerCharacter

The player prefab

請注意,如何在預製件下已經建立一個EntityPrototype及一個EntityView檔案。這些由Quantum使用,以生成實體並且鏈接它到Unity檢視。

重要事項: 所有Quantum預製件需要在Resources/DB資料夾或其子資料夾之中。在其外的預製件不會有一個已建立的EntityPrototype檔案。

玩家鏈結元件

Quantum有一個玩家的概念。各個客戶端可以有一個或多個玩家。然而,Quantum沒有一個內建的玩家物件/虛擬人偶的概念。各個連線到遊戲的玩家被給予一個獨一無二的ID。這個ID稱為一個PlayerRef。為了鏈接一個實體到一個特定玩家,我們將建立一個玩家鏈結元件,其含有擁有者的PlayerRef

quantum-code專案的3dPlatformer資料夾中建立一個PlayerLink.qtn檔案,並且新增下列程式碼到它之中:

C#

component PlayerLink
{
    player_ref Player;
}

玩家資料

為了動態地生成一個角色,我們需要讓遊戲遊玩程式碼知道該建立哪種實體。Quantum有一個玩家資料的概念。玩家資料允許各個玩家在連線時傳送資訊到模擬之中。比如這可以是玩家正在遊玩的角色的資訊,或是他們正在使用的皮膚,或其他事情。

可在RuntimePlayer.User檔案之中找到玩家資料。在quantum_code專案中開啟RuntimePlayer.User檔案,並且以下面的程式碼替換它的內容:

C#

using Photon.Deterministic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Quantum
{
    partial class RuntimePlayer
    {
        public AssetRefEntityPrototype CharacterPrototype;

        partial void SerializeUserData(BitStream stream)
        {
            stream.Serialize(ref CharacterPrototype);
        }
    }
}

按下組建(ctrl + shift + b)。

這個程式碼新增一個AssetRefEntityPrototype到玩家資料,其是預製件的Quantum相等產品。這稍後將用於生成玩家角色實體。

重要事項: 所有被新增到RuntimePlayer類別的資料,必須在SerializeUserData功能中被序列化。BitStream針對所有基礎類型來提供序列化。

當進入遊玩模式時,Quantum模擬將自動運行。這是由Unity中的Game場景中的QuantumRunnerDebug遊戲物件上的QuantumLocalRunnerDebug元件來驅動。這個元件用於針對開發目的,在本機偵錯運行一個Game的單一玩家版本。

QuantumLocalRunnerDebug允許模擬任何數量的本機玩家。在Players之中的QuantumLocalRunnerDebug之中,新增一個新的輸入項目到清單。輸入項目含有一個CharacterPrototype欄位,其是在之前被新增到玩家資料的欄位。拖放PlayerCharacterEntityPrototype檔案(可在PlayerCharacter預製件下找到)到欄位之中。

Inspector view of the QuantumDebugRunner

現在預製件已經被鏈接到玩家資料,剩下的事情是撰寫程式碼以在玩家加入時生成實體。

生成玩家物件

建立一個新的PlayerSpawnsystem.cs類別。新增下列程式碼:

C#

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

            // resolve the reference to the prototpye.
            var prototype = frame.FindAsset<EntityPrototype>(data.CharacterPrototype.Id);

            // 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 = 0 + player;
            }
        }
    }
}

當玩家加入時,這個程式碼建立角色實體,並且透過新增一個玩家鏈結元件給它,來鏈接它到玩家。

信號類似於在C#中的事件。Quantum系統使用它們以在彼此之間交流。Quantum附有很多現有的信號,比如ISignalOnPlayerDataSet,其在玩家已經加入遊戲階段並且分享他們的玩家資料之後被調用。

SystemSignalsOnly是一個系統的特殊類型,它自己不做任何事情。它允許只接聽信號的系統的實作。

新增玩家生成系統到MovementSystem之後的SystemSetup.cs中的系統清單。

更新移動系統

到目前為止,移動系統始終使用來自0玩家的輸入來移動,其使用下列程式碼:

C#

var input = *f.GetPlayerInput(0);

以下列程式碼來替換它,以取得來自已鏈接玩家的輸入:

C#

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

請注意,尚未調整篩選器,因此系統仍將篩選附有角色控制器但沒有玩家鏈結元件的實體。在這種情況下,它將使用default值作為輸入。這導致除了施加重力之外,沒有應用任何移動。

在Quantum中使用TryGet來取得一個元件是非常快速的O(n),因為Quantum使用一個疏鬆集ECS。

按下組建(ctrl + shift + b)並且切換到Unity,然後進入遊玩模式。除了已經在場景之中的角色之外,將生成一個玩家角色,該角色會對鍵盤輸入作出回應。

Back to top