This document is about: QUANTUM 1
SWITCH TO

Quantum DSL (게임 상태)

Quantum은 자체 DSL로 모든 게임 상태 데이터를 선언해 주어야 합니다(도메인 명시 언어).
이 정의들은 ".qtn" 확장자인 텍스트 파일에 작성됩니다(정의는 필요한만큼 분리될 수 있으며, Quantum 컴파일러가 머지하여 하나의 게임 상태로 생성시켜줍니다).

DSL의 목적은 개발자로부터 Quantum의 예측/롤백 모델에 의해 부과된 복잡한 메모리 요건을 추상화하는 것입니다.

DSL-정의된 게임 상태는 커스텀 Quantum 컴파일러가 해석하여 일반 C#으로 변환해줍니다(메모리 제한을 두고)

이 변환에는 작업을 단순화하고 개발자를 고성능 포인터 기반 접근 방식에서 보호하기 위해 언매니지드 C# 포인터의 광범위한 사용과 코드 생성 기능이 포함됩니다.

엔티티

엔티티는 임시 게임 개념(동적으로 생성/파괴되어야 하는 인스턴스)으로 사용해야 하며, 일반적으로 컴포넌트의 집합을 포함하지만 속성으로써 선언된 타입(컴포넌트가 아닌 DSL 상에 있는 특수 타입)들도 포함할 수 있습니다.

엔티티에 대해 최소한으로 정의해 제공해야 할것은 이름과 최대 인스턴스 수(quantum은 모든 게임 상태 데이터를 사전 할당합니다)입니다.

C#

entity Character[19]
{

}

이것은 Character 라고하는 엔티티에 대한 C# 구조체를 생성하며 게임 상태내에 19개의 "인스턴스"를 사전할당합니다. "field" 스코프로 엔티티에 직접 속성을 추가하는 것이 가능합니다.

C#

entity MyEntity[19]
{
    fields
    {
        FP Health;
        FP Mana;
    }
}

생성된 "MyEntity" 구조체 인스턴스 포인터는 다음과 같이 필드에 직접 접근할 수 있습니다(생성된 상태 API에 대한 상세내용은 다음 장인 시스템을 참고하십시오).

C#

var mana = myEntity->Mana;
myEntity->Health = 10;

컴포넌트

컴포넌트는 특수한 재사용 데이터 컨테이너/그룹으로 엔티티에 붙일 수 있습니다. 다음은 컴포넌트의 기본 정의입니다:

C#

component Weapon
{
    FP Cooldown;
    FP Power;
}

"use" 키워드는 엔티티에 컴포넌트를 붙입니다.

C#

entity MyEntity[19]
{
    use Weapon;
    fields
    {
        FP Health;
        FP Mana;
    }
}

생성된 엔티티 구조체에서, 컴포턴트 이름/유형은 속성 이름으로도 사용됩니다:

C#

myEntity->Weapon.Power = 4;

Quantum에는 몇 개의 사전 구축된 컴포넌트들이 있습니다:

  • Transform2D - 위치(FPVector2)와 라디언으로 회전(FP)이 포함되어 있습니다;
  • DynamicBody - 현재 속도, 매스, 쉐이프, 머터리얼 데이터와 기타 물리 엔진 자료(엔티티의 물리를 사용할 때 필요한 위치와 회전은 Transform2D로 부터 가져옵니다);
  • Prefab - 런타임의 Unity Prefab에 엔티티를 연결하는 데 도움이 되는 단순화된 구성요소 (Unity에서 엔티티 렌더링용);
  • Animator - Quantum에서 결정론적 애니메이션 시스템의 시뮬레이션 부분 (Unity의 메카님과 통합됩니다);

다음은 이 섹션에서 주제로 다루어진 조합된 DSL입니다:

C#

component Weapon
{
    FP Cooldown;
    FP Power;
}

entity MyEntity[19]
{
    use Weapon;
    use Transform2D;
    use DynamicBody;
    fields
    {
        FP Health;
        FP Mana;
    }
}

구조체

Quantum DSL은 또한 메모리 정렬을 관리할 필요 없이 일반 구조체를 정의할 수 있으며, 정렬되지 않은 데이터의 정의로부터 보호합니다.

C#

struct Resources
{
    FP Health;
    FP Mana;
}

이렇게 하면 "Resources" 구조체를 DSL의 다른 모든 부분에서 유형으로 사용할 수 있습니다. 예를들어, 이전에 엔티티 정의에 사용된 루즈 필드를 교체하는 데 사용할 수 있습니다.

C#

entity MyEntity[19]
{
    use Weapon;
    use Transform2D;
    use DynamicBody;
    fields
    {
        Resources Resources;
    }
}

컴포넌트 vs. 구조체

중요한 것은 컴포넌트가 일반 구조체 대신에 왜 그리고 언제 사용하는가입니다(컴포넌트는 결국 구조체입니다).

컴포넌트는 특수 유형으로 변환되는 생성된 메타-데이터를 포함합니다.

  • 엔티티 메타 데이터에는 런타임의 정의에서 구성요소의 존재 여부를 신속하게 필터링하는 데이터가 포함되어 있습니다.
  • 컴포넌트 유형의 활성 인스턴스에 대해 게임 상태를 이동할 수 있습니다. (이것은 고성능 엔티티 필터의 가장 단순한 것이며 다음장 Systems API에서 다룹니다);

다음 세션에서 분명하게 드러나겠지만, 컴포넌트는 다른 구조체처럼 다른 구조체 없이 포인터(이 장의 뒷부분에 있는 시그널에서) 또는 값 유형으로 전달될 수 있습니다(다른 기능이나 이벤트에 대해서는 나중에 참조). 그러나 위에서 설명한 기능은 컴포넌트 전용입니다.

Unions, Enums와 Bitsets

C와 유사한 유니온과 열거형도 만들어질 수 있습니다. 아래 예제는 열거형을 사용하여 일부 상호 배타적인 데이터 유형/값을 조합에 겹쳐서 저장할 데이터를 지정하는 방법을 보여줍니다.

C#

enum DataType
{
    typeA, typeB 
}

struct DataA
{
    FPVector2 Something;
    FP Anything;
}

struct DataB
{
    FPVector3 SomethingElse;
    Int32 AnythingElse;
}

union Data
{
    DataA A;
    DataA B;
}
 
struct DataContainer
{
    DataType Type;
    Data Data;
}

Bitsets는 원하는 목적을 위해 고정된 크기의 메모리 블록을 선언하는 데 사용될 수 있습니다(예를들어 픽셀 퍼펙트 메카닉용 전쟁 안개, 그리드 같은 구조등).

C#

struct FOWData
{
    bitset[256] Map;
}

입력

Quantum에서는 클라이언트간에 교환되는 런타임 입력도 DSL에서 선언됩니다. 이 예에서는 간단한 이동 벡터를 게임의 입력으로 정의합니다.

C#

input
{
    FPVector2 Movement;
}

버튼과 같은 입력에 대해서, Quantum은 안전하게 상태 변화를 계산할 수 있으므로(다음장인 systems에서 다루게 될 것입니다) 특수한 타입 "button"이 사용되어야 합니다

C#

input
{
    FPVector2 Movement;
    button Fire;
}

사전 정의된 입력 구조체가 필요하며, 동일한 게임 플레이 세션에서 사용할 상호 배타적인 입력 유형과 데이터를 정의하기 위해 결합을 사용할 수 있습니다. 이 예에서는 동일한 입력 정의에 일반적인 이동/발사 또는 "물건 구입" 명령 데이터가 포함될 수 있는 방법을 보여 줍니다.

C#

enum InputType
{
    BuyCommand, MovementAndFire
}

struct BuyData
{
    Int32 ItemID;
    Int32 UnitID;
    Boolean ApplyImmediately;
}

struct MovementAndFireData 
{
    FPVector2 Movement;
    button Fire;
}

union InputData
{
    BuyData BuyCommand;
    MovementAndFireData MovementAndFire;
}

input
{
    InputType Type;
    InputData Data;
}

입력이 어떻게 (Systems)에 의해서 처리되고 Systems로 부터 Quantum으로 어떻게 주입되는지는 다음 장을 참고하십시오(Bootstrap Unity 프로젝트)

이벤트

이벤트는 시뮬레이션에서 발생하는 것을 렌더링 엔진으로 전달하는 세분화된 솔루션입니다(게임 상태의 일부를 수정/업데이트하는 곳에서는 사용해서는 안 됨). "event" 키워드를 사용하여 이름 및 데이터를 정의합니다:

C#

event TriggerSound
{
    FPVector2 Position;
    FP Volume;
}

상속은 이벤트에 사용할 수 있습니다(선택적으로 일부 이벤트는 abstract로 표시될 수 있으므로, 직접 트리거되는 것을 차단하여 구체적인 하위 클래스만 허용합니다).

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;
}

특정 이벤트가 서버에서 입력 데이터가 확인될 때만 (Unity로) 전송되도록 하려면(유도 허위 양성 롤백 방지) "synced" 키워드를 사용하십시오.

C#

synced event TriggerDeathSound : TriggerSound
{    
}

원치 않는 중복 디스패치를 방지하기 위해 Quantum은 (이벤트 데이터를 소스로 사용하여) 모든 이벤트 유형에 대한 해시코드 함수를 자동으로 생성합니다. 특정 상황에서 개발자는 (이벤트 인스턴스를 고유하게 만드는) 주요-후보 데이터가 무엇인지 엄격하게 제어 할 수 있습니다. 한 가지 예를 들면 위치에서 약간의 롤백으로 인한 변경으로 인해 동일한 이벤트의 두 인스턴스가 실제로 다른 두 개의 이벤트로 잘못 해석되는 경우입니다. 이를 방지하기 위해 해시 함수는 "nothashed" 키워드를 사용하여 일부 이벤트 데이터를 무시하도록 할 수 있습니다:

C#

abstract event TriggerSound
{
    nothashed FPVector2 Position;
    Int32 SoundID;
}

다음 장을 참조하여 Quantum (Systems)에서 이벤트가 트리거되고 Unity 콜백(Bootstrap Unity 프로젝트)에 어떻게 전송되는지 알아보십시오.

시그널

시그널은 분리된 시스템간 통신 API(게시자/가입자 API 또는 관찰자 패턴과 같은 비트)로 사용되는 함수 서명입니다. 다음과 같이 간단하게 시그널을 정의합니다.

C#

signal OnDamage(FP Damage);

이것은 다음의 인터페이스를 생성할 것 입니다(모든 시스템에서 구현될 수 있습니다):

C#

public interface ISignalOnDamage
{
    public void OnDamage(Frame f, FP Damage);
}

시그널은 Quantum DSL에서 포인터의 직접 선언을 허용하는 유일한 개념이기 때문에 참조로 데이터를 전달하면 구체적인 구현에서 원본 데이터를 직접 수정할 수 있습니다.

C#

signal OnDamageBefore(FP* Damage);

특수 유형

Quantum은 복잡한 개념(조합 포인터 오프셋, 플레이어 인덱스 등)을 추상화하거나 언매니지드 코드로 일반적인 실수로부터 보호하는 데 사용되는 몇 가지 특수 유형을 가집니다. 다음 특수 유형을 다른 데이터 유형(컴포넌트, 엔터티 필드, 이벤트 등)내에서 사용할 수 있습니다.

  • player_ref - 런타임 플레이어 인덱스를 나타냅니다(Int32간에 캐스트됩니다). 엔티티에 붙혀진 경우 플레이어가 제어하는 엔티티 인스턴스를 표시할 수 있습니다(Quantum의 플레이어 인덱스 기반 입력과 결합);
  • entity_ref - (일반) Quantum 내의 각 프레임/틱 데이터가 분리된 메모리 영역/블록(Quantum은 빠른 롤백을 위해 이러한 버퍼를 저장합니다), 포인터들은 프레임간(Unity 상에 게임 상태로도 되지 않음)에 캐시될 수 없습니다. 엔티티 ref는 엔티티유형, 인덱스와 버전을 추상화합니다(또한 개발자가 이전 ref를 사용하여 손상되거나 재사용된 엔티티 슬롯에 실수로 사용되지 않는 데이터에 액세스하지 못하도록 보호);
  • asset_ref - Quantum 에셋 데이터베이스에서 데이터 에셋 인스턴스에 대한 롤백 가능 참조 (데이터 에셋 장을 참고하십시오)
  • array[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> Character;
    entity_ref AnyEntity;
    asset_ref<CharacterData> CharacterData;
    array<FP>[10] TenNumbers;
    array<entity_ref<Character>>[4] FourCharacterRefs;
}

사용가능한 타입과 Imports

Quantum의 DSL 파서는 사전 임포트된 크로스-플랫폼 결정론적 유형이 있으며 게임 상태 정의를 하는데 사용할 수 있습니다:

  • Boolean
  • Char
  • Byte
  • SByte
  • UInt16
  • Int16
  • UInt32
  • Int32
  • UInt64
  • Int64
  • FP
  • FPVector2
  • FPVector3
  • FPMatrix
  • FPQuaternion
  • PlayerRef - DSL 내에서의 player_ref
  • EntityRef - DSL 내에서의entity_ref
  • LayerMask
  • NullableFP - DSL 내에서 FP 사용
  • NullableFPVector2 - DSL 내에서 FPVector2 사용?
  • NullableFPVector3 - DSL 내에서 FPVector3 사용?

컴파일러 옵션

다음의 컴파일러 옵션은 현재 Quantum의 DSL 파일 내부에서 사용할 수 있는 것 입니다(향후 더 많은 옵션이 추가될 예정입니다):

C#

// numeric constants
#define MY_NUMBER 10

// pre defining max number of players (default is 8)
#pragma max_players 16
Back to top