9 - Shooting
船設置
現時,船的加速度和轉彎速度等值已硬編碼到AsteroidsShipSystem
中。一個更好的模式是將這些變數加上將用於控制船射擊的其他變數放入設置檔中。
首先,創建一個新的c#指令碼,並將其命名為AsteroidsShipConfig
。然後新增以下程式碼:
C#
using Photon.Deterministic;
using UnityEngine;
namespace Quantum
{
public class AsteroidsShipConfig : AssetObject
{
[Tooltip("The speed that the ship turns with added as torque")]
public FP ShipTurnSpeed = 8;
[Tooltip("The speed that the ship accelerates using add force")]
public FP ShipAceleration = 6;
[Tooltip("Time interval between ship shots")]
public FP FireInterval = FP._0_10;
[Tooltip("Displacement of the projectile spawn position related to the ship position")]
public FP ShotOffset = 1;
[Tooltip("Prototype reference to spawn ship projectiles")]
public AssetRef<EntityPrototype> ProjectilePrototype;
}
}
然後在AsteroidsShip.qtn
中新增對船設置的引用:
C#
component AsteroidsShip
{
AssetRef<AsteroidsShipConfig> ShipConfig;
}
在資源資料夾中創建一個新的船設置執行個體。將其命名為DefaultShipConfig
。然後將其放入AsteroidsShip
遊戲物件的ShipConfig
欄位,將其連結到船原型。
接下來,調整AsteroidsShipSystem
的UpdateShipMovement
函數中的程式碼,以使用設置中的值:
C#
var config = f.FindAsset(filter.AsteroidsShip->ShipConfig);
FP shipAcceleration = config.ShipAceleration;
FP turnSpeed = config.ShipTurnSpeed;
射擊
首先,在AsteroidsShip.qtn
中新增一個射擊間隔欄位,以追蹤射擊時射擊間隔之間的時間。
C#
component AsteroidsShip
{
AssetRef<AsteroidsShipConfig> ShipConfig;
FP FireInterval;
}
接下來,打開AsteroidsShipSystem
並新增一個函數來處理射擊:
C#
private void UpdateShipFire(Frame f, ref Filter filter, Input* input)
{
var config = f.FindAsset(filter.AsteroidsShip->ShipConfig);
if (input->Fire && filter.AsteroidsShip->FireInterval <= 0)
{
filter.AsteroidsShip->FireInterval = config.FireInterval;
// TODO create projectile
}
else
{
filter.AsteroidsShip->FireInterval -= f.DeltaTime;
}
}
此功能允許船在按下射擊按鈕時定期射擊。TODO
行將被替換為創建子彈實體的程式碼。
在調用UpdateShipMovement
後,從系統的Update
方法調用此函數:
C#
UpdateShipFire(f, ref filter, input);
創建一個新的c#指令碼,並將其命名為AsteroidsProjectileSystem
。新增以下程式碼到它:
C#
using Photon.Deterministic;
using UnityEngine.Scripting;
namespace Quantum.Asteroids
{
[Preserve]
public unsafe class AsteroidsProjectileSystem : SystemSignalsOnly
{
public void AsteroidsShipShoot(Frame f, EntityRef owner, FPVector2 spawnPosition, AssetRef<EntityPrototype> projectilePrototype)
{
EntityRef projectileEntity = f.Create(projectilePrototype);
Transform2D* projectileTransform = f.Unsafe.GetPointer<Transform2D>(projectileEntity);
Transform2D* ownerTransform = f.Unsafe.GetPointer<Transform2D>(owner);
projectileTransform->Rotation = ownerTransform->Rotation;
projectileTransform->Position = spawnPosition;
}
}
}
將此系統新增到Unity中的AsteroidsSystemConfig
中。
接下來,創建一個AsteroidsProjectileConfig
c#指令碼,作為拋射物的設置檔:
C#
using Photon.Deterministic;
using UnityEngine;
namespace Quantum
{
public class AsteroidsProjectileConfig : AssetObject
{
[Tooltip("Speed applied to the projectile when spawned")]
public FP ProjectileInitialSpeed = 15;
[Tooltip("Time until destroy the projectile")]
public FP ProjectileTTL = 1;
}
}
最後,創建一個AsteroidsProjectile.qtn
檔案作為拋射物的資料元件:
C#
component AsteroidsProjectile
{
FP TTL;
EntityRef Owner;
AssetRef<AsteroidsProjectileConfig> ProjectileConfig;
}
透過在AsteroidsProjectileSystem
的結尾新增以下行來初始化拋射物,從而調整AsteroidsShipShoot
函數:
C#
AsteroidsProjectile* projectile = f.Unsafe.GetPointer<AsteroidsProjectile>(projectileEntity);
var config = f.FindAsset(projectile->ProjectileConfig);
projectile->TTL = config.ProjectileTTL;
projectile->Owner = owner;
PhysicsBody2D* body = f.Unsafe.GetPointer<PhysicsBody2D>(projectileEntity);
body->Velocity = ownerTransform->Up * config.ProjectileInitialSpeed;
訊號
每當船射擊時,都應該調用AsteroidsShipShoot
。然而,該函數位於不同的系統中。調用它的一種解決方案是使函數靜態並直接調用它,但這會導致系統的緊密耦合,這對於較大的程式碼庫來說尤其不理想。
一個解決方案是使用訊號。訊號是系統相互通信的一種基於事件的管道。訊號可以在任何.qtn檔案中定義。在這種情況下,打開AsteroidsProjectile.qtn
並向其新增一個訊號,如下所示:
C#
component AsteroidsProjectile
{
FP TTL;
EntityRef Owner;
AssetRef<AsteroidsProjectileConfig> ProjectileConfig;
}
signal AsteroidsShipShoot(EntityRef owner, FPVector2 spawnPosition, AssetRef<EntityPrototype> projectilePrototype);
現在,讓AsteroidsProjectileSystem
實作ISignalAsteroidsShipShoot
介面。
C#
public unsafe class AsteroidsProjectileSystem : SystemSignalsOnly, ISignalAsteroidsShipShoot
每當叫用訊號時,此介面都會調用AsteroidsShipShoot
函數。
要調用訊號,請透過將// TODO create projectile
行替換為以下內容來調整AsteroidsShipSystem
:
C#
var relativeOffset = FPVector2.Up * config.ShotOffset;
var spawnPosition = filter.Transform->TransformPoint(relativeOffset);
f.Signals.AsteroidsShipShoot(filter.Entity, spawnPosition, config.ProjectilePrototype);
創建拋射物實體
在場景中創建一個新的Quantum > 2D > Circle Entity
遊戲物件。將其命名為AsteroidsProjectile
。從中移除MeshRenderer
和MeshFilter
。將圓形碰撞器的半徑調整為0.16
,並勾選PhysicsBody2D
核取方塊。
在QuantumEntityPrototype
的Entity Components
清單中新增一個AsteroidsProjectile
元件。TTL
和Owner
欄位在運行時透過設置分配,因此它們可以保持為空。對於設置欄位,創建一個新的AsteroidsProjectileConfig
資產,並將其命名為DefaultProjectileConfig
。將Projectile Initial Speed
設定為20
,將Projectile TTL
設定為2
,然後在元件的設置欄位中為拋射物分配設置。
創建一個3D > Cube
遊戲物件作為AsteroidsProjectile
的下層,並將其縮小到(0.17, 0.17, 0.17)
。這將作為拋射物的視覺效果。從物件上移除箱式碰撞器。
將AsteroidsProjectile
拖到Resources
資料夾中,使其成為預製件,然後將其從場景中移除。接下來,將預製件的prototype
連結到DefaultShipConfig
的Projectile Prototype
欄位。
生存時間
現時,拋射物還沒有被摧毀。拋射物可以透過兩種方式摧毀。要麼是它們的生存時間用完,要麼是與小行星或太空梭相撞。
為了在TTL到期後摧毀拋射物,系統需要在每一幀都對其進行檢查。讓AsteroidsProjectileSystem
繼承自SystemMainThreadFilter<AsteroidsProjectileSystem.Filter>
而不是SystemSignalsOnly
,並新增以下內容到它:
C#
public struct Filter
{
public EntityRef Entity;
public AsteroidsProjectile* Projectile;
}
public override void Update(Frame f, ref Filter filter)
{
filter.Projectile->TTL -= f.DeltaTime;
if (filter.Projectile->TTL <= 0)
{
f.Destroy(filter.Entity);
}
}
進入遊玩模式。太空梭現在可以按space
鍵射擊了。拋射物在2
秒後自動取消生成,但它們現時與小行星和玩家碰撞時,它們就像是普通的物理體一樣。在教學的下一部分中,碰撞檢測將新增到子彈中。