DSL (ゲームステート)
イントロダクション
Quantumでは、コンポーネントやその他のランタイムのゲームステート型を独自のDSL(ドメイン固有言語)で宣言する必要があります。
これらの定義は、拡張子 .qtn
のテキストファイルに書き込まれます。 Quantumコンパイラはこれらを解析してASTに変換し、各型の部分的なC#構造体定義を生成します (定義は必要に応じて複数のファイルに分割することができ、コンパイラはそれらをマージします)。
DSLの目的は、QuantumのECSスパース集合メモリモデルによって課せられる複雑なメモリアラインメント要件を、開発者から抽象化することです。この要件は、シミュレーションへの決定論的予測/ロールバックアプローチをサポートするために必要です。
このコード生成アプローチでは、型のシリアル化(スナップショット、ゲームセーブ、キルカム再生に使用)やその他の機能(デバッグ目的のためのフレームデータの印刷/ダンピング)のために「ボイラープレート」コードを書く必要性がなくなります。
Quantum DSLインテグレーションでは、ワークフローにqtn
ファイルを追加する方法を説明しています。
コンポーネント
コンポーネントは、エンティティにアタッチできる特別な構造体で、フィルタリングに使用されます (アタッチされたコンポーネントに基づいてアクティブなエンティティのサブセットのみを反復処理する)。 以下はコンポーネントの基本的な定義例です:
C#
component Action
{
FP Cooldown;
FP Power;
}
これらは通常のC#構造体になります。それらをコンポーネントとしてラベルを付けると(上記のように)、適切なコード構造体(マーカーインターフェース、IDプロパティなど)が生成されます
カスタムコンポーネントとは別に、Quantumには以下の構築済みのものが付随します:
- Transform2D/Transform3D: 定点(FP)値を使用した位置と回転。
- PhysicsCollider、PhysicsBody、PhysicsCallbacks、PhysicsJoints (2D/3D): Quantumのステートレス物理エンジンで使用されます。
- PathFinderAgent, SteeringAgent, AvoidanceAgent, AvoidanceObstacle: navmeshベースの経路探索と移動。
構造体
構造体はDSLとC#の両方で定義できます。
DSL定義
Quantum DSLでは、正規構造体の定義も可能です(コンポーネント、メモリアラインメント、ヘルパー関数の処理と同じように)。
C#
struct ResourceItem
{
FP Value;
FP MaxValue;
FP RegenRate;
}
フィールドは、構造体が生成される際にアルファベット順に並べられます。特定の順序で表示させたい場合は、C# で構造体を定義します (以下のセクションを参照してください)。
これにより、DSL の他のすべての部分で "Resources" 構造体を型として使用できるようになります。例えば、コンポーネント定義の中でこれを使用することができます
C#
component Resources
{
ResourceItem Health;
ResourceItem Strength;
ResourceItem Mana;
}
生成された構造体は一部で、必要に応じてC#で拡張することができます。
CSharp定義
C#でも構造体を定義できますが、この場合は以下を手動で定義する必要があります:
- 構造体のメモリーレイアウト (LayoutKind.Explicit を使用)
- サイズを含む構造体に const int
SIZE
を追加する。 Serialize
関数を実装する。
C#
[StructLayout(LayoutKind.Explicit)]
public struct Foo {
public const int SIZE = 12; // the size in bytes of all members in bytes.
[FieldOffset(0)]
public int A;
[FieldOffset(4)]
public int B;
[FieldOffset(8)]
public int C;
public static unsafe void Serialize(void* ptr, FrameSerializer serializer)
{
var foo = (Foo*)ptr;
serializer.Stream.Serialize(&foo->A);
serializer.Stream.Serialize(&foo->B);
serializer.Stream.Serialize(&foo->C);
}
}
DSLでC#で定義された構造体を使用する場合(コンポーネント内部など)、構造体の定義を手動でインポートする必要があります。
import struct Foo(12);
注: インポート はサイズの定数に対応していませんので、毎回正確な数値を指定する必要があります。
コンポーネントと構造体の比較
どのような理由で、またどのような場合に、通常の構造体ではなくコンポーネントを使うべきなのかが重要です(結局、コンポーネントは構造体でもあります)。
コンポーネントには生成されたメタデータが含まれており、それを以下の機能を持つ特殊な型に変換します:
- エンティティに直接取り付けることができます。
- ゲームステートを横断する際にエンティティをフィルタリングするために使用されます(次の章ではフィルタAPIについて説明します)。
コンポーネントは他の構造体と同様に、ポインタまたは値型として、パラメータとしてアクセス、使用、または渡すことができます。
動的コレクション
Quantumのカスタムアロケータは、ロールバック可能なゲームステートの一部として、転送可能なコレクションを公開しています。コレクションは転送可能な型(すなわちプリミティブ型とDSL定義型)のみをサポートしています。
コレクションを管理するために、Frame APIはそれぞれ3つのメソッドを提供します:
Frame.AllocateXXX
: ヒープ上のコレクションのためのスペースを割り当てる。Frame.FreeXXX
: コレクションのメモリを解放/割り当て解除する。Frame.ResolveXXX
: ポインタを解決してコレクションにアクセスする。
注意: コレクションを解放した後は、必ずdefault
に設定して、コレクションを無効にしなければなりません。これは、ゲームの状態をシリアライズするために必要です。ヌル化を省略すると、不確定な動作や非同期が発生します。
重要な注意事項
- 複数のコンポーネントが同じコレクションインスタンスを防御できます。
- 動的コレクションは、コンポーネントや構造体の内部に参照として格納されます。そのため、初期化時には割り当てが必要 であり、さらに重要なのは、不要になったときには解放されるべきです。コレクションがコンポーネントの一部である場合、2つの方法があります。
- リアクティブなコールバックである
ISignalOnAdd<T>
とISignalOnRemove<T>
を実装し、コレクションの割り当てと解放を行います。(これらの特定のシグナルに関する詳細は、マニュアルのECSセクションにあるコンポーネントのページを参照してください)。または、 [AlocateOnComponentAdded]
と[AllocateOnComponentAdded]
属性を使って,コンポーネントが追加されたときと削除されたときに,それぞれQuantumに割り当てと解放を任せます.
- リアクティブなコールバックである
- Quantumは、少なくとも値がない限り、プロトタイプからコレクションを事前に割り当てることはありません。コレクションが空の場合は、メモリを手動で割り当てる必要があります。
- コレクションを2回以上解放しようとすると、エラーが発生し、ヒープが内部的に無効な状態になります。
リスト
動的リストは、DSLでlist<T> MyList
を用いて定義することができます。
component Targets {
list<EntityRef> Enemies;
}
これらのリストを扱うための基本的なAPIメソッドは以下の通りです。
Frame.AllocateList<T>()
Frame.FreeList(QListPtr<T> ptr)
Frame.ResolveList(QListPtr<T> ptr)
解決されると、リストは、Add、Remove、Contains、IndexOf、RemoveAt、[] などのリストに期待されるすべてのAPIメソッドを使用して、反復処理や操作を行うことができます。
上記のコードスニペットで定義された Targets 型のコンポーネントでリストを使用するには、以下のようなシステムを作成することができます。
C#
namespace Quantum
{
public unsafe class HandleTargets : SystemMainThread, ISignalOnComponentAdded<Targets>, ISignalOnComponentRemoved<Targets>
{
public override void Update(Frame f)
{
foreach (var (entity, component) in f.GetComponentIterator<Targets>()) {
// To use a list, you must first resolve its pointer via the frame
var list = f.ResolveList(component.Enemies);
// Do stuff
}
}
public void OnAdded(Frame f, EntityRef entity, Targets* component)
{
// allocating a new List (returns the blittable reference type - QListPtr)
component->Enemies = f.AllocateList<EntityRef>();
}
public void OnRemoved(Frame f, EntityRef entity, Targets* component)
{
// A component HAS TO de-allocate all collection it owns from the frame data, otherwise it will lead to a memory leak.
// receives the list QListPtr reference.
f.FreeList(component->Enemies);
// All dynamic collections a component points to HAVE TO be nullified in a component's OnRemoved
// EVEN IF is only referencing an external one!
// This is to prevent serialization issues that otherwise lead to a desynchronisation.
component->Enemies = default;
}
}
}
ディクショナリ
dictionary<key, value> MyDictionary
のように、DSLでディクショナリを宣言することができます。
C#
component Hazard{
dictionary<EntityRef, Int32> DamageDealt;
}
これらのディクショナリを扱うための基本的なAPIメソッドは以下の通りです:
Frame.AllocateDictionary<K,V>()
Frame.FreeDictionary(QDictionaryPtr<K,V> ptr)
Frame.ResolveDictionary(QDictionaryPtr<K,V> ptr)
他のダイナミックコレクションと同様に、使用する前に割り当てを行い、使用後にフレームデータから割り当て解除する必要がありますです。 上記のリストに記載されている例を参照してください。
HashSet
ハッシュセットはDSLの中でhash_set<T> MyHashSet
のように宣言することができます。
C#
component Nodes{
hash_set<FP> ProcessedNodes;
}
これらのディクショナリを扱うための基本的なAPIメソッドは以下の通りです。
Frame.AllocateHashSet(QHashSetPtr<T> ptr, int capacity = 8)
Frame.FreeHashSet(QHashSetPtr<T> ptr)
Frame.ResolveHashSet(QHashSetPtr<T> ptr)
他の動的コレクションと同様に、ハッシュセットを使用する前に割り当てを行い、フレームデータからの割り当てを解除し、ハッシュセットが使用されなくなったら無効にすることが必須です。前述のリストの項で紹介した例をご覧ください。
ユニオン、Enum、ビットセット
Cのような共用体や列挙型も生成できます。 以下の例は、相互に排他的なデータ型/値を重ね合わせて共用体にすることでデータメモリを節減する方法を示しています。
C#
struct DataA
{
FPVector2 Something;
FP Anything;
}
struct DataB
{
FPVector3 SomethingElse;
Int32 AnythingElse;
}
union Data
{
DataA A;
DataB B;
}
生成された型 Data には、(どの共用体型が追加されているかを伝えるための)微分器プロパティも含まれます。いずれかの共用体サブタイプに "タッチ "すると、このプロパティが適切な値に設定されます。
ビットセットは、任意の目的のために固定サイズのメモリブロックを宣言するために使用することができます(例えば、フォグオブウォー、ピクセルパーフェクトなゲームメカニクスのためのグリッドのような構造体など)。
C#
struct FOWData
{
bitset[256] Map;
}
入力
Quantumでは、クライアント間でやり取りされるランタイム入力もDSLで宣言されています。 この例では、ゲームの入力として単純な動きベクトルとFireボタンを定義しています。
C#
input
{
FPVector2 Movement;
}
ボタンのような入力の場合、Quantumは状態変化を安全に計算することができます(これは次の章-システムで取り上げます)。これには、特別な型「button」を使用する必要があります:
C#
input
{
FPVector2 Movement;
button Fire;
}
ボタンは多くの場合、ネットワークプロトコルのシングルビットのみを使用するため、予測/ロールバックゲームに理想的な型です。
入力構造体はティックごとにポーリングされ、サーバーに送信されます(オンラインでプレイする場合)。サーバーは、フルティックセット(全プレイヤーの入力)の入力確認を一括して送信する役割を担っています。このため、この構造体は可能な限り最小限のサイズに抑える必要があります。
予測コマンド(該当する章を参照してください)は、Quantumの別の入力パスです。任意のデータとサイズを設定できるため、予測コマンドは入力の特別な型(たとえば「このアイテムを買う」、「どこかにテレポートする」など)に適するよう設定可能です。
入力が(システムによって)どのように消費され、UnityからQuantumに注入されるかについては、次の章を参照してください(Bootstrap Unity Project)。
シグナル
シグナルは、デカップリングされたシステム間通信API(パブリッシャー/サブスクライバーAPIの1つの形態)として使用される関数シグネチャです。 これは単純なシグナルを定義します(特別な型である entity_ref に注意してください - これらについてはこの章の最後に記載します)。
C#
signal ApplyDamage(FP damage, entity_ref entity);
これにより、以下のようなインターフェースが生成されます(どのシステムでも実装可能)。
C#
public interface ISignalOnDamage
{
public void OnDamage(Frame f, FP damage, EntityRef entity);
}
シグナルはQuantumのDSLでポインタを直接宣言できる唯一の概念です。このため、参照でデータを渡すことによって、それらの具体的な実装で元のデータを直接修正することができます。
C#
signal OnDamageBefore(FP Damage, Resources* resources);
これによって、コンポーネントポインタ―を渡すことが許可される点に留意してください(エンティティ参照型の代わりに)。
イベント
イベントは、シミュレーション内での発生事項をレンダリングエンジンに伝えるための精度の高いソリューションです(ゲームステートの一部を変更したり更新したりするために使用してはいけません)。キーワード「event」を使用して、名前とデータを定義します。
C#
event TriggerSound
{
FPVector2 Position;
FP Volume;
}
継承はイベントに利用可能です(オプションでいくつかのイベントを抽象化して、直接トリガーされないようにブロックし、具体的なサブクラスのみを許可することもできます)。
C#
abstract event TriggerSound
{
FPVector2 Position;
Int32 SoundID; // this would be better handled with unity-side asset linking extensions, but this is out of the scope of this chapter
}
event TriggerShot : TriggerSound
{
FP Power;
}
入力データがサーバーから確認された場合に特定のイベントの送信のみをおこなう点を保証するには(ロールバックに誘発された、誤った正数を回避)、「同期した」キーワードを使用してください。
C#
synced event TriggerDeathSound : TriggerSound
{
}
望ましくない重複ディスパッチを避けるため、Quantumはすべてのイベントタイプのハッシュコード関数を自動生成します(イベントデータをソースとして使用します)。特定の条件では、ディベロッパーは(イベントインスタンスを一意にする)キー候補データが何であるかを厳密に制御したい場合があるかもしれません。 たとえば、わずかなロールバックによる位置の変化によって同じイベントの2つのインスタンスが、2つの実際に異なるイベントと誤って解釈されてしまう場合です。 これを避けるために、「nothashed」キーワードを使用して、ハッシュ関数でイベントデータの一部を強制的に無視させることができます。
C#
abstract event TriggerSound
{
nothashed FPVector2 Position;
Int32 SoundID;
}
イベントに player_ref
パラメータが含まれている場合、local
および remote
キーワードを適用して、これらのイベントをフィルタリングすることができます。
C#
event KilledOtherPlayer {
local player_ref Killer;
remote player_ref Killed;
}
このイベントがUnity側でディスパッチされると、まず、player_ref
変数が、その前のキーワードと一致するかどうかをチェックします。上記の例では、_キラー_がローカルプレイヤーであり、かつ、_キルされた_プレイヤーがリモートプレイヤーである場合にのみ、Unityでイベントが発生することを意味します。これらの条件のいずれかが意味されていない場合、イベントはUnityで抑制されます。
Quantum 2.1 からは、client
および server
キーワードを使用してイベントを修飾することで、イベントをさらにフィルタリングすることができます。イベントが client
とマークされている場合は、クライアントのビルドでのみトリガーされます。一方、server
キーワードは、サーバープラグインでのみイベントがトリガーされます。注意:__これは、カスタムプラグインを使用してサーバーサイドシミュレーションを実行する場合にのみ該当します。
イベントがQuantum(Systems)からトリガーされ、Unityのコールバック(Bootstrap Unity Project)にディスパッチ/確認/キャンセルされる仕組みについては、次の章も参照してください。
グローバル
DSLでは、グローバルにアクセス可能な変数を定義することができます。グローバルは、どの.qtnファイルでも、global
スコープを使って宣言することができます。
C#
global {
// Any type that is valid in the DSL can also be used.
FP MyGlobalValue;
}
DSL定義のすべてのものと同様に、グローバル変数は状態の一部であり、予測-ロールバック・システムと完全に互換性があります。
グローバル・スコープで宣言された変数は、Frame APIを通じて利用可能になります。これらは、フレームにアクセスできる場所であれば、どこからでもアクセス(読み取り/書き込み)することができます。ECSセクションの_Systems_ドキュメントを参照してください。
グローバル変数の代わりに、シングルトン・コンポーネントを使用することもできます。詳細については、マニュアルのECSセクションにある_Components_のページを参照してください。
特別な型
Quantumにはいくつかの特別な型があり、複雑な概念(エンティティ参照、プレイヤーインデックスなど)を抽象化したり、管理されていないコードによって一般的な間違いから保護したり、あるいはその両方に使用されます。以下の特殊な型は、他のデータ型(コンポーネント内、イベント内、シグナルなど)の内部で使用することができます。
player_ref
: ランタイムのプレイヤーインデックスを表します(Int32にキャストされたり、Int32からキャストされたりします)。 コンポーネントで定義されている場合、どのプレイヤーが関連するエンティティを制御しているかの保存に使用できます(Quantumのプレイヤーインデックスベースの入力と組み合わせて)。entity_ref
: Quantumの各フレーム/ティックデータは別のメモリ領域/ブロックに存在するため(ロールバックをサポートするためにQuantumはいくつかのコピーを保持しています)、フレーム間でのポインタのキャッシュはできません(Unityスクリプトでもゲームステートでも)。エンティティ参照は、エンティティのインデックスとバージョンのプロパティを抽象化します(開発者が誤って古い参照で、破棄または再利用されたエンティティスロット上の非推奨データに誤ってアクセスすることを防ぎます)。asset_ref<AssetType>
: Quantumアセットデータベースからのデータアセットインスタンスへのロールバック可能な参照です (データアセットの章を参照してください)。list<T>
,dictionary<K,T>
: 動的コレクション参照 (Quantum のフレームヒープに格納されています)。転送可能な型(プリミティブおよびDSL定義型)のみをサポートしています。array<Type>[size]
: データコレクションを表すための固定サイズの「配列」。 通常のC#配列であれば、ヒープ割り当てされたオブジェクト参照(プロパティなどを保持)になりQuantumのメモリ要件に抵触します。このため、特殊な配列型ではポインタベースのシンプルなAPIを生成し、ゲームステートの内部にロールバック可能なデータコレクションを保持しています。
アセットについての留意事項
アセットはQuantumの特別な機能です。ディベロッパーがデータ駆動型のコンテナ(通常のクラス、継承、多態性メソッドなど)を定義し、インデックス化されたデータベース内で不変のインスタンスとして終了することを可能にします。「asset」キーワードは、(既存の)クラスを、ゲームステートの内部で参照を割り当て可能なデータアセットとして割り当てるために使用されます(機能や制限についてはデータアセットの章を参照してください)。
C#
asset CharacterData; // the CharacterData class is partially defined in a normal C# file by the developer
以下の構造は、上記の型のいくつかの有効な例を示しています(以前に定義された型を参照することもあります):
C#
struct SpecialData
{
player_ref Player;
entity_ref Character;
entity_ref AnotherEntity;
asset_ref<CharacterData> CharacterData;
array<FP>[10] TenNumbers;
}
利用可能な型
DSLで作業をする場合、様々な型を使用できます。 パーサーによって事前にインポートされているものもあれば、手動インポートが必要なものもあります。
デフォルト
QuantumのDSLパーサーにはゲームの状態定義で使用可能な、あらかじめインポートされたクロスプラットフォームの予測型のリストがあります。
- Boolean / bool - 内部的には同じように動作するQBooleanでラップされます(取得/設定、比較など)。
- Byte
- SByte
- UInt16 / Int16
- UInt32 / Int32
- UInt64 / Int64
- FP
- FPVector2
- FPVector3
- FPMatrix
- FPQuaternion
- PlayerRef / DSLではplayer_ref
- EntityRef - DSLではentity_ref - EntityRef / DSLではentity_ref
- LayerMask - LayerMask
- NullableFP - DSLではFP?を使用 - NullableFP / DSLではFP?
- NullableFPVector2 - DSLではFPVector2?を使用 - NullableFPVector2 / DSLではFPVector2?
- NullableFPVector3 - DSLではFPVector3?を使用 - NullableFPVector3 / DSLではFPVector3?
- QString
はUTF-16用 (.NETでは別名Unicode) - QStringUtf8
は常にUTF-8 - Hit
- Hit3D
- Shape2D
- Shape3D
- Joint, DistanceJoint, SpringJoint and HingeJoint
N
は文字列の合計サイズをバイト数で表したもので、記帳に使用される2バイトを差し引いた値です。たとえば、QString<64>
は最大バイト長が62バイトの文字列に対して64バイト、つまりUTF-16で最大31文字までを使用することになります。
手動インポート
前のセクションで記載されていない型が必要な場合は、QTNファイルの先頭にある以下の構文を使用して手動でインポートする必要があります。
名前空間 / Quantum以外の型
他の名前空間で定義されている型をインポートするには、次の構文を使用します。
C#
import MyInterface;
or
import MyNameSpace.Utils;
For an enum the syntax is as follows:
C#
import enum MyEnum(underlying_type);
// This syntax is identical for Quantum specific enums
import enum Shape3DType(byte);
組み込み型のQuantumとカスタム型
Quantum の組み込み型やカスタム型をインポートする場合、構造体のサイズは C# 宣言であらかじめ定義されています。そのため、いくつかの安全対策を追加することが重要です。
C#
namespace Quantum {
[StructLayout(LayoutKind.Explicit)]
public struct Foo {
public const int SIZE = sizeof(Int32) * 2;
[FieldOffset(0)]
public Int32 A;
[FieldOffset(sizeof(Int32))]
public Int32 B;
}
}
C#
#define FOO_SIZE 8 // Define a constant value with the known size of the struct
import struct Foo(8);
構造体の予測サイズが実際のサイズと一致することを確認するために、以下のように Assert
をシステムの 1 つに追加することをお勧めします。
C#
public unsafe class MyStructSizeCheckingSystem : SystemMainThread{
public override void OnInit(Frame f)
{
Assert.Check(Constants.FOO_SIZE == Foo.SIZE);
}
}
アップグレード中に組み込み構造体のサイズが変更された場合、このAssert
がスローされ、DSLの値を更新できるようになります。
属性
Quantum はInspectorにパラメータを表示するために、いくつかの属性をサポートしています。
属性 | パラメータ | 説明 |
---|---|---|
DrawIf |
string fieldName
long value
CompareOperator compare
HideType hide
|
条件がtrueと評価された場合のみプロパティを表示します。
fieldName = 評価するプロパティの名前 value = 比較に使用する値。 compare = 実行する比較演算 Equal 、 NotEqual 、 Less 、 LessOrEqual 、 GreaterOrEqual または Greater 。
hide = 式が False と評価された場合のフィールドの挙動:Hide または ReadOnly 。
比較と非表示の詳細は、以下を参照してください。 |
Header | string header |
プロパティの上にヘッダーを追加します。
header = 表示するヘッダーテキスト。 |
HideInInspector | - | フィールドをシリアル化し、Unityインスペクターで以下のプロパティを非表示にします。 |
Layer | - |
int型のみに適用できます。
フィールド上で EditorGUI.LayerField を呼び出します。
|
Optional | string enabledPropertyPath |
プロパティのオン/オフを切り替えます。
enabledPropertyPath = トグルを評価するために使用される*bool*へのパス。 |
Space | - | プロパティの上にスペースを追加する |
Tooltip | string tooltip |
プロパティの上にカーソルを置くと、ツールチップを表示します。
tooltip = 表示するチップ。 |
ArrayLength (since 2.1) FixedArray (in 2.0) CSharpのみ |
int length
-------- int minLength
int maxLength
|
lengthを使用すると、配列のサイズを定義できます。
------ minLengthとmaxLengthを使用すると、Inspectorでサイズの範囲を定義することができます。 最終的なサイズはInspectorで設定できます。 (minLengthとmaxLengthは包括的です) |
ExcludeFromPrototype | - |
コンポーネントとコンポーネントフィールドの両方に適用できます。
------ - フィールド:コンポーネント用に生成されたプロトタイプからフィールドを除外します。 - コンポーネント:このコンポーネントのプロトタイプは生成されません。 |
PreserveInPrototype | - |
タイプに追加すると、プロトタイプで使用可能としてマークされ、プロトタイプクラスが発行されなくなります。 フィールドに追加すると、特定のフィールドにのみ影響します。シンプルな [Serializable] 構造体の場合、Unity 側で_Prototype タイプを使用する必要がないので便利です。
|
AllocateOnComponentAdded | - |
動的なコレクションに適用可能。
これにより、コレクションを保持するコンポーネントがエンティティに追加されたときに、コレクション用のメモリがまだ割り当てられていなければ、そのメモリが割り当てられます。 |
FreeOnComponentRemoved | - |
ダイナミックコレクションやPtr にも適用可能です。
これにより、コンポーネントが取り外されたときに、関連するメモリが解放され、フィールドに保持されていた Ptr が無効になります。
------ 重要:この属性を相互参照されたコレクションと組み合わせて使用しないでください。この属性は、特定のフィールドに保持されているPtrを無効にするだけで、他のフィールドは無効なメモリを指します。 |
Attributesは、特に指定がない限り、いくつかの構文の違いはあるものの、C#ファイルとqtnファイルの両方で使用することができます。
CSharpでの使用
C#ファイルでは他の属性と同様に、属性を使用したり連結したりできます。
C#
// Multiple single attributes
[Header("Example Array")][Tooltip("min = 1\nmax = 20")] public FP[] TestArray = new FP[20];
// Multiple concatenated attributes
[Header("Example Array")][Tooltip("min = 1\nmax = 20")] public FP[] TestArray = new FP[20];
qtnでの使用
qtnファイルでは、単一属性の使用法はC#と同じです。
C#
[Header("Example Array")] array<FP>[20] TestArray;
複数の属性を組み合わせる場合、それらの属性は連結する必要があります。
C#
[Header("Example Array"), Tooltip("min = 1\nmax = 20")] array<FP>[20] TestArray;
コンパイラーオプション
現在、QuantumのDSLファイル内で使用できるコンパイラーオプションは以下の通りです (今後さらに追加される予定です):
C#
// pre defining max number of players (default is 6, absolute max is 64)
#pragma max_players 16
// numeric constants (useable inside the DSL by MY_NUMBER and useable in code by Constants.MY_NUMBER)
#define MY_NUMBER 10
// overriding the base class name for the generated constants (default is "Constants")
#pragma constants_class_name MyFancyConstants
カスタムFP定数
また、QuantumのDSLファイルの中でカスタムのFP
定数を定義することもできます:
C#
// in a DSL file
#define CustomConstant 3.14
Quantum codegen は FP
構造体に対応する定数を生成します:
C#
// 3.14
FP constant = FP.CustomConstant;
また、対応する生の値も生成されます:
C#
// 3.14 Raw
var rawConstant = FP.Raw.CustomConstant;
Back to top