This document is about: QUANTUM 2
SWITCH TO

Bomber

Level 4

概述

Quantum炸彈人範例附有完整的源程式碼,並且展示了在Quantum中如何組建炸彈人遊戲的遊戲遊玩。

下載

版本 發布日期 下載
2.1.6 2023年7月18日 Quantum 炸彈人 2.1.6 組建 268

在您開始之前

為了以線上多人玩家模式來運行範例,首先在Photon引擎儀表板中建立一個Quantum應用程式帳號,然後貼上它到PhotonServerSettings資產中的AppId欄位。
然後在場景選單中載入Menu場景並且按下Play

技術資訊

  • Unity:2021.3.13f1或更高版本
  • 平台:PC (Windows)

聚焦點

技術

  • 為了從上到下的網格基礎遊戲而量身打造的自訂Movement元件及系統
  • 半程序化的地圖生成以及強化物生成
  • 角色客製化
  • 基於時間的爆炸擴散
  • 零事件模擬方法
  • 相對簡潔的ECS模擬架構

遊戲遊玩

  • 大亂鬥炸彈人
  • 放置炸彈
  • 修改炸彈數量、爆炸範圍以及移動速度的形式的強化物。

控制

  • WASD以移動
  • 空白鍵以放置炸彈

單元格

Cell是一個DSL定義的架構,其以CellType旗標的形式來持有一個單一值。為了更方便地存取及修改這些值,該架構已使用Cell.User.cs指令碼中的屬性及方法來進行擴展。

各個單元格都知道目前位於其位置的物件的類型,但是它本身沒有任何參照到實體。當一個系統迭代一個實體,它檢查單元格中是否有任何有興趣的內容。舉例而言,如果單元格IsBurning,那麼它將在它負責的元件上觸發適當的回應。

因為CellType是一個旗標,所有屬性都可以架構為位元方式的操作,這使得它們非常高效。

網格

Grid是一個常規的CSharp類別,其含有GridSettings架構、Cell架構的陣列及一個指標。

幀.使用者

有了Grid定義為常規類別,以及在它的Frame.User中有執行個體,因此可以透過常規的Frame.XYZ API來讓其可用,同時將所有相關方法封裝在Frame.Grid.XYZ之中。

網格作為常規類別的缺點時它的資料位在常規DSL生成的記憶體之外。因此必要的是手動地處理初始化、序列化、複製、清除及拋棄資料。這是透過掛鉤到Frame.User內部的下列方法來完成的:

  • InitUser():最初建立幀時被調用一次。
  • FreeUser():幀被銷毀時被調用。
  • CopyUser():當先前的幀狀態被複製到下一個幀之中時被調用。
  • SerializeUser():當幀被序列化時被調用(比如針對延遲加入者作為一個夥伴快照)。
  • DumpFrameUser():當停止同步發生時被調用。

網格設定

可在GridSettings架構中找到如何生成及設定網格的資訊。使用其中持有的資訊,可以在運行階段設定1D陣列,並且以固定及可破壞的方塊的位置來填入它,以及推測SpawnPoints的位置。

單元格陣列,或稱為網格

網格單元格被內含在Cells的1D陣列之中。該原理與圖塊地圖路徑尋找器技術範例中的圖塊地圖資產相同。DSL支援1D陣列;然而,DSL需要在編譯時刻知道陣列的大小,因為網格是在運行階段基於玩家請求的大小而生成的,所以必須以不同的方式來處理。

陣列是在運行階段透過掛鉤到Frame.User中的InitUser()方法之中來建立的。

因為這是一個常規的CSharp脈絡,因此當銷毀類別時,垃圾收集器將收集與陣列關聯的記憶體。

Grid之中的1D陣列透過掛鉤到Frame.User內部的CopyUser()SerializeUser()方法,來與預測—復原及延遲加入序列化相容。

效能

雖然一個Cell只含有一個位元組旗標,但是透過複製幀來完成的的讀寫量可能會造成嚴重的效能瓶頸。為了預防這種情況,在Grid.cs中已經針對網格及其單元格來定義了多個取得-設定方法。這些方法使用者指標數學以將指標傳回單元格,因此允許直接讀取及寫入架構。

指標數學很快。然而,計算位移上的錯誤將導致讀取隨機記憶體,在好的情況下將導致未定義,不好的情況下將導致破壞整個模擬。請小心使用!

廣譜設定及清除

有兩種系統來寫入記憶體到網格:

  • SetBroadphaseSystem:在所有遊戲遊玩系統 之前 運行,並且基於其目前含有的實體來設定單元格類型。
  • ClearBroadphaseSystem:在所有遊戲遊玩系統 之後 運行,並且針對將被銷毀的實體來清除單元格中的資訊。

輸入

輸入架構由5個按鈕組成。

C#

input {
    Button MoveUp;
    Button MoveDown;
    Button MoveLeft;
    Button MoveRight;
    Button PlaceBomb;
}

這是輸入架構中可以採用的最精簡的形式。雖然Button類型在模擬中佔用1個位元組,但是它在纜線上被壓縮成1位元。

為了準備遊戲遊玩系統將耗用的玩家輸入,InputSystem使用移動按鈕來建構一個Direction,並且基於PlaceBomb按鈕值在AbilityPlaceBomb中設定WantsToPlaceBomb布林值。

炸彈人

Bomber元件是一個旗標元件以識別及篩選實體。

因此,關聯的BomberSystem相對簡單,因為它的唯一工作是檢查一個炸彈人類型實體在給定的Update()期間是否正站在一個燃燒的單元格上。

C#

public override void Update(Frame f, ref BomberFilter filter)
{
    var gridPosition = filter.Transform->Position.RoundToInt(Axis.Both);
    var isInvincible = false;
#if DEBUG
    // Used for debugging purposes
    isInvincible = f.RuntimeConfig.IsInvincible;
#endif
    if (isInvincible == false && f.GetCellRef(gridPosition).IsBurning)
    {
        // Death animation is triggered from OnEntityDestroyed
        f.Destroy(filter.Entity);
    }
}

炸彈

炸彈是暫時實體,其由一個玩家動作來建立,並且在它們的Timer到期時導致一個爆炸。

有兩個系統與Bomb元件互動:

  • AbilityPlaceBombSystem:檢查玩家是否已經按下PlaceBomb按鈕,以及是否符合條件來放置一個。
  • BombSystem:開始在炸彈上的Timer,處理連鎖反應,並且在銷毀炸彈時觸發一個爆炸(因為計時器到期或另一個炸彈的爆炸碰到它)。

爆炸

爆炸持續一段確定的時間。因此它們需要作為一個實體而存在,而非只是一個射線投射。ExplosionSystem處理爆炸生命週期,以及從單元格到單元格的爆炸擴散。

一個爆炸實體由兩個元件組成:

  • Explosion:一個元件用作為一個旗標元件,以及持有關於爆炸設置的資訊。
  • Timer:計時器用於計算爆炸擴散到下一個相鄰單元格之前的剩餘時間。

強化物

透過兩個主要的元件來提供強化物功能性

  • PowerUp:以類型及修改器數量,來定義一個強化物;以及,
  • PowerUpManager:持有可生成強化物的清單以及生成機率。

當從網格中清除一個可銷毀方塊時,有機會生成強化物。在方塊銷毀時,PowerUpManager將被告知新的被清除的位置。PowerUpManagerSystem迭代新的可用的地點,並且如果該單元格是空的且目前沒有著火的話,將嘗試在其上生成一個隨機的強化物。

移動

單元格內部的移動是不受限制的,並且讀取網格以確定允許角色前往的地方。

方向

Direction是一個位元組旗標,其含有目前被按下的移動按鈕。

移動元件

Movement元件追蹤幀之間的相關移動值。

  • FP CurrentSpeed:目前的角色移動的速度。
  • FP MaxSpeed:角色被允許移動的最大速度。
  • Boolean IsMoving:追蹤最後的移動輸入是否導致一個移動。
  • Boolean LastMoveWasHorizontal:追蹤最後的移動的方向。
  • Direction LastNewInput:在垂直及水平方向追蹤玩家提供的最後的輸入。
  • Direction CurrentInput:目前被按下的移動方向。
  • FPVector2 MoveDirection:最後的移動方向
  • FP StartRotation:在方向改變之前的最後的查看旋轉。
  • FP TargetRotation:在方向改變之後的角色應該有的查看旋轉。
  • int RotationStartTick:開始向新的方向旋轉的刷新。
  • FP RotationDuration:一個旋轉應該有的最大總持續時間。
  • FP RotationTimeMultiplier:為了在分配的時間內達到一個完整的旋轉所需的旋轉速度乘數。

移動系統

移動系統滿足了三個核心功能性:

  1. 基於目前被按下的移動鍵來計算可能的移動(GetMovementResult())並且傳回一個含有值的MoveResult架構。
  2. 更新目前的移動速度(UpdateCurrentSpeed())
  3. 移動角色(UpdateMovement())
  4. 旋轉角色(UpdateRotation())

MovementSystem執行兩個功能以提供一個在網格上的平順連續的對角線的移動:

  • CanMoveInDirection()負責角落滑行。當按下一個對角線的方向時,這讓一個角色可以平順地在角落周圍移動。
  • GetMoveVector()含有一個方向切換以替換首先檢查的方向(垂直或水平)。這允許角色來連續地在網格上沿著對角線移動。

移動結果

MoveResult是一個公用程式架構。它針對角色將在其上移動的網格單元格,來持有目前的Movement.CurrentInput的處理結果。因為這些值不作為幀狀態的一部分來使用,它被定義為一個常規的CSharp架構,而不是在DSL之中。

  • FPVector2 Direction:所需的移動方向。
  • FP MaxDistance:角色在所需方向上能夠移動的最大可能距離。
  • FPVector2 LookDirection:作為這個移動的結果,角色將查看的方向。
  • FP RotationTimeMultiplier:角色應該基於它目前的查看方向及所需的查看方向之間的角度,來切換它們的查看方向的速度,以在預先定義的時間量之內完成旋轉。

第三方資產

炸彈人範例包含由其各自的創作者提供的多個資產。您可以在他們各自的網站上為自己的專案獲得完整的套件:

重要事項 :為了在商業專案中使用它們,需要從各自的創作者來購買授權。

Back to top