This document is about: QUANTUM 3
SWITCH TO

Frequently Asked Questions

大多數問與答內容都衍生自開發人員在#quantum-sdk-v2#beginner-questions Discord聊天中提出的問題。還可以使用那裡的搜尋功能來查找更多幫助。

Quantum Unity框架

為什麼透過演示選單啟動遊戲時,遊戲場景沒有載入?

當伺服器拒絕應用程式帳號時,還沒有適當的錯誤訊息。請確保您已正確創建您的應用程式帳號:

  1. 您的Photon儀表板上創建Quantum應用程式帳號
  2. 點擊創建新應用程式
  3. 將Photon類型設定為 Photon Quantum
  4. 填寫名稱欄位
  5. 向下滾動並按 創建

為什麼Quantum遊戲開始前會有1秒的延遲?

檢查Deterministic Config資產中的Room Wait Time (seconds)。在那裡你可以調整它。房間等待時間 用於應對波動的Ping時間。
它將始終用於在開始時幫助同步所有客戶端遊戲階段。如果將其設定為1秒,它將始終等待整整1秒。

注意: 這不應該用於同步場景載入時間!如果需要對此進行協調,請在啟動Quantum遊戲階段之前載入場景,並直接透過Photon Realtime進行協調。在這種情況下,該值可以安全地設定為0。

為什麼在嘗試連線到ns.exitgames.com時連線超時?

要獲取更多詳細資訊,請在您的PhotonServerSettings中增加連線日誌級別Network Logging。大多數情況下,超時是由於您的UDP流量被阻擋。
有關如何分析您的情況的指南,請參閱Photon Realtime檔案中的 分析中斷連線 頁面:分析中斷連線

如何調試凍結的遊戲?

QUANTUM_STALL_WATCHER_ENABLED定義新增到Project Settings > Player > Scripting Define Symbols中,將使觀察者指令碼能夠啟動一個執行緒,該執行緒將觀察更新迴圈。如果檢測到停滯(即再次調用更新需要超過X秒),它將導致當機。當調試在模擬上凍結時,這很有用,因為生成的當機應該讓所有執行緒的調用堆棧都在運行。

在Unity Editor中運行遊戲時,是否有有效的方法來模擬網路延遲?

Quantum效能分析器有一個可用的延遲模擬。

在此下載:附加元件 | 分析器

或者使用外部網路節流工具,例如Clumsy(Windows),篩選掉遊戲伺服器埠:

  • UDP 5056
  • TCP 4531
Clumsy Filter: (udp.DstPort == 5056 or udp.SrcPort == 5056) or (tcp.DstPort == 4531 or tcp.SrcPort == 4531)

為什麼遊戲模擬在暫停或調試中斷點後運行得更快?

預設下,時間是在內部量測的,不會補償停止模擬。當SimulationConfig上的DeltaTimeType更改為EngineDeltaTime時,遊戲將在暫停後以正常速度恢復。警告:更改它將使每個客戶端使用僅用於調試的話可能不理想的設定。儘管一些具有非常嚴格的相機控制的遊戲(例如飛行模擬)會從將其設定為EngineDeltaTime中獲益。

C#

public enum SimulationUpdateTime {
    Default = 0,                        // internal clock
    EngineDeltaTime = 1,                // Time.deltaTime (Unity)
    EngineUnscaledDeltaTime = 2         // Time.unscaledDeltaTime
}

為什麼在我的幾何中生成導航網格島?

Unity將在某個時候解決這個問題(見論壇貼文:forum.unity.com/threads/nav-generating-inside-non-walkable-objects

NavmeshModifierVolume的解決方法將緩解這一問題(需要NavMeshComponents)。

在匯入步驟中進行三角形剔除是我們未來可以提供的另一種選擇。

Navmesh Island

為什麼當場景載入時間過長時,我的遊戲會超時中斷連線?

載入Unity場景時,即使使用LoadSceneAsync完成,主執行緒也會根據該場景的大小和複雜性而凍結一段時間。這可能會導致由於超時而導致的中斷連線錯誤,因為在遊戲凍結時沒有發生通信。

為了防止這種情況發生,您可以使用ConnectionHandler類別中提供的一些API。以下是如何設定和使用它的分步說明:

  • 檢查是否有任何帶有ConnectionHandler元件的遊戲物件。如果沒有,請新增一個;

  • 在元件上,您將能夠看到一個名為KeepAliveInBackground的欄位,您可以使用它來增加連線保持的時間。該值以毫秒為單位通知;

  • 現在,您應該告知什麼是QuantumLoadBalancingClient,在UIMain上有一個靜態取得器,以便您使用它(Quantum預設附帶了它)。完成此操作後,您可以啟動StartFallbackSendAckThread。以下是一個關於如何實現這一目標的範例程式碼片段:

C#

    // Before starting loading the scene
    if (_connectionHandler != null)
    {
      _connectionHandler.Client = UIMain.Client;
      _connectionHandler.StartFallbackSendAckThread();
    }

在Quantum中開發模擬

為什麼模擬從幀60開始?

這是在模擬開始時分配的可復原幀的數量。該數字將與DeterministicConfig->復原視窗中設定的值匹配。

為什麼用相同的幀號多次調用Update()?

線上運行Quantum時,在復原的情況下,系統上的Update()會針對同一幀號被多次調用。當檢測到遠端玩家輸入的預測不正確,並且模擬必須使用正確的輸入資料重新運行一個幀以恢復到確定性狀態時,就會發生這種情況。
不過,可以檢查/記錄「frame.IsVerified」,以檢查幀是否經過驗證。在離線模式下運行Quantum時,由於在這種情況下不會發生復原,因此沒有重複。

FP.MaxValue和FP.UseableMax之間有什麼區別?

固定點數學只使用其64位元值的16+16位元。這使得部分數學運算更快,因為我們不必檢查溢位。也就是說:FP.MinValueFP.MaxValue使用了所有64位元,且 應該用於計算。請改用FP.UseableMaxFP.UseableMin(例如,用最小FP值初始化距離變數)。

注意: FP可以表示-32,768 到 32,768(-2¹⁵ 到 2¹⁵)之間的值。

為什麼指向新結構的指標指向過時的資料?

在迴圈中,指向該結構的指標將獲得相同的堆疊指標,如果 使用newdefault,則將包含過時的資料。

C#

struct Bar {
    public bool Foo;
}

static unsafe void Main(string[] args) {
    for (int i = 0; i < 2; i++) {

        Bar bar;
        //Bar bar = default(Bar); // <---- Fixes the stale data

        Bar* barPt = &bar;
        if (barPt->Foo)
            Console.WriteLine("Stuff and Things");

        barPt->Foo = true;
    }

    Console.ReadKey();
}

為什麼我的模擬不同步?

DeterministicConfig.ChecksumInterval大於0時,計算已驗證幀的校驗和,將其發送到伺服器,並與其他客戶端發送的校驗和進行比較。

最常見的原因是:

寫入Quantum資料資產

C#

var characterSpecAsset = frame.FindAsset<CharacterSpec>("WhiteFacedBarghast");
characterSpecAsset.RemainigLifetime = 21;

請勿 向Quantum資產寫入任何內容。它們包含唯讀資料。

從Unity執行緒寫入Quantum

Unity中的所有指令碼都能夠唯讀存取透過Quantum Frame公開的所有內容。僅透過輸入和/或命令影響模擬。

快取資料

當模擬復原時,下方給出的程式碼片段最終將取消同步。

C#

public class CleaningSystem : SystemBase {
    public Boolean HasShoweredToday;    // <----- Error
    public override void Update(Frame f) {
        if (!HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            HasShoweredToday = true;
        }
    }
}

請改為將非瞬態資料儲存在框架或實體元件上。

C#

// Frame
unsafe partial class Frame {
    public Boolean HasShoweredToday;
    partial void CopyFromUser(Frame frame) {
        // Implement copy of the custom parameters.
    }
}

public class CleaningSystem : SystemBase {
    public override void Update(Frame f) {
        if (!f.HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            f.HasShoweredToday = true;
        }
    }
}

有關更多資訊,請閱讀Quantum v2手冊的 條目。

浮點數學

避免在模擬中使用浮點數,只使用FP數學。

小心處理FP.FromFloat_UNSAFE()。使用它「離線」在一台機器上平衡資產生成是可以的;但是,請注意,這可能會在不同的平台上傳回不同的結果。如果您需要在運行時匯入浮點數,並且不能使用整數或FP(例如下載平衡資料),請將 字串轉換為FP

AssetObject.Loaded()期間創建資料

在載入過程中對每個資產調用一次AssetObject.Loaded()。此時在資產成員中存儲、計算和存儲新資料是完全可以的——注意: 如果您在伺服器上運行模擬,所有遊戲都將共享這一資產。

如果您的資產是從資源載入的,並且您正在重新啟動Unity編輯器或在運行時重設Unity DB,請注意Unity不會卸載Unity資產。

C#

public partial class FooAsset {
  public Foo Settings;
  public int RawData;

  [NonSerialized]
  public List<int> Bar = new List<int>();

  public override AssetObject AssetObject => Settings;

    public override void Loaded(IResourceManager resourceManager, Native.Allocator allocator)
    {
      base.Loaded(resourceManager, allocator);
    // This will break on the second run (see above) because Bar needs to be reset by either Bar.Clear() or Bar = new List<int>()
    Bar.Add(RawData);
  }
}

我可以將Photon Room重新用於具有相同客戶端的新Quantum遊戲階段嗎?

不可以,您不應該將該房間重新用於另一個Quantum遊戲階段。

但是 您可以讓玩家(或其中一部分)留在房間裏,然後軟重啟模擬。這就是Stumble Guys在三個階段之間的流程。

  • 即使您的遊戲回合已經結束,也要保持Quantum遊戲階段運行。
  • 將程式碼新增到您的遊戲遊玩系統(例如遊戲狀態機)中,其處理遊戲回合的開始。
  • 確定性地停用Quantum系統和/或載入新的Quantum地圖。
  • 重設或銷毀所有Quantum實體,重設您的遊戲視圖。
  • 輸掉一回合的玩家可以保持連線來觀看遊戲,但不能再影響遊戲。

或者 所有玩家都可以切換房間。由於這涉及伺服器過渡,並且在分佈式系統中,很多事情都可能失敗,因此建議嘗試軟重啟模擬,並僅將房間過渡作為最後手段。

  • 使用(例如Photon房間内容或Quantum命令)在客戶端之間共享新房間的ID。或者以程式設計/確定性的方式創建新房間(注意不要被輕易猜到)。
  • 每個客戶端都會停止Quantum遊戲階段並運行LeaveRoom(),但不會中斷連線。
  • 所有客戶端都使用JoinOrCreate()連線到新房間。
  • 緩解連線問題,如客戶端在此過程中重新啟動應用程式、其他連線錯誤、等待玩家加入等等

Photon Realtime對戰配對指南

我可以在Quantum資產中使用DSL生成的聯合結構嗎?

不可直接用。Unity不支援重疊欄位。

取而代之地,可以使用<UnionName>_Prototype,因為它是Unity可序列化的,並且有一個啟動器。

要轉換為union結構體,如下所示:

C#

UnionName result = default;
prototype.Materialize(frame, ref result, default);
Back to top