quantum | v2 switch to v1  

물질화

소개

컴포넌트 프로토타입 또는 엔티티 프로토타입에서 컴포넌트 또는 컴포넌트 인스턴스를 생성하는 프로세스가 물질화(Materialization) 입니다.

맵 에셋에 베이크 된 씬 프로토타입의 물질화는 Frame.Create API를 사용하여 코드 작성 인스턴스를 물질화하는 것과 동일한 규칙과 실행 흐름을 따릅니다.

메인 화면으로
 

프로토타입 Vs 인스턴스

컴포넌트 인스턴스와 엔티티 인스턴스는 게임 상태의 일부입니다. 즉, 런타임에 조작할 수 있습니다. DSL에 선언된 컴포넌트는 해당하는 컴포넌트 프로토타입을 생성하는 데 사용됩니다. 코드 생성 프로토타입은 MyComponent_Prototype이라는 명명 규칙을 따릅니다.

컴포넌트 프로토타입엔티티 프로토타입은 둘 다 에셋 이며, 이는 게임 상태의 일부가 아니며 런타임에 변하지 않으며 모든 클라이언트에서 항상 동일해야 함을 의미합니다. 각 컴포넌트 프로토타입에는 ComponentPrototypeRef가 있으며, Frame.FindPrototype<MyComponentName_Prototype>(MyComponentPrototypeRef)을 사용하여 해당하는 에셋을 찾는 데 사용할 수 있습니다.

메인 화면으로
 

컴포넌트 프로토타입

컴포넌트 프로토타입은 구현에 직접 사용되지 않을 수 있는 데이터를 포함하도록 확장할 수 있습니다. 이를 통해 특정 컴포넌트의 인스턴스 간에 데이터를 공유하거나 프레임에서 읽기 전용 데이터를 제외하여 게임 상태를 슬림 하게 유지할 수 있습니다.

코드 생성 컴포넌트 프로토타입은 쉽게 확장할 수 있는 부분 클래스입니다.

  1. MyComponentName_Prototype.cs라는 C# 파일을 만듭니다.
  2. 스크립트 바디를 Quantum.Prototypes 네임스페이스로 넣습니다.
  3. ( 선택 ) 인스펙터 속성에 접근하기 위해 설명서 \ ECS속성 섹션에 설명되어 있는 using Quantum.Inspector;을 추가합니다.

그러면 컴포넌트 프로토타입 에셋에 추가 데이터를 추가하고 partial MaterializeUser() 메소드를 구현하여 사용자 정의 물질화 로직을 추가할 수 있습니다.

메인 화면으로
 

예제

다음 예는 아케이드 레이싱 템플릿에서 볼 수 있는 차량 컴포넌트의 물질화를 보여줍니다.

차량 컴포넌트는 주로 런타임에 계산된 동적 값을 보유합니다. 이러한 매개 변수는 초기화할 수 없으므로 DSL의 컴포넌트 정의는 해당 매개 변수에 대해 ExcludeFromPrototype 속성을 사용하여 에셋 설계자가 유니티 편집기에서 조작할 수 있는 Vehicle_Prototype에서 제외합니다. Nitro 매개 변수는 디자이너가 특정 Vehicle의 초기화 정도를 결정할 수 있도록 편집할 수 있는 부분일 뿐입니다.

component Vehicle 
{
    [ExcludeFromPrototype] 
    ComponentPrototypeRef Prototype;

    [ExcludeFromPrototype]
    Byte Flags;
    [ExcludeFromPrototype]
    FP Speed;
    [ExcludeFromPrototype]
    FP ForwardSpeed;
    [ExcludeFromPrototype]
    FPVector3 EngineForce;
    [ExcludeFromPrototype]
    FP WheelTraction;

    [ExcludeFromPrototype]
    FPVector3 AvgNormal;

    [ExcludeFromPrototype]
    array<Wheel>[4] Wheels;

    FP Nitro;
}

Vehicle_Prototype 에셋이 확장되어 설계자에게 사용자 정의 가능한 읽기 전용 매개 변수를 제공합니다. 따라서 Vehicle_Prototype 에셋은 특정 차량 엔티티 프로토타입 "type"의 모든 인스턴스에 대한 공유 값을 보유할 수 있습니다. Vehicle 컴포넌트의 Prototype 매개 변수는 AssetRef에 해당하는 컴포넌트별 ComponentPrototypeRef 타입입니다. 이를 채우기 위해 partial MaterializeUser() 메소드를 사용하여 Vehicle_Prototype의 참조를 할당합니다.

using Photon.Deterministic;
using Quantum.Inspector;
using System;

namespace Quantum.Prototypes
{
public unsafe partial class Vehicle_Prototype
{
    // PUBLIC METHODS

    [Header("Engine")]
    public FP EngineForwardForce = 130;
    public FP EngineBackwardForce = 120;
    public FPVector3 EngineForcePosition;
    public FP ApproximateMaxSpeed = 20;

    [Header("Hand Brake")]
    public FP HandBrakeStrength = 10;
    public FP HandBrakeTractionMultiplier = 1;

    [Header("Resistances")]
    public FP AirResistance = FP._0_02;
    public FP RollingResistance = FP._0_10 * 6;
    public FP DownForceFactor = 0;
    public FP TractionGripMultiplier = 10;
    public FP AirTractionDecreaseSpeed = FP._0_50;

    [Header("Axles")]
    public AxleSetup FrontAxle = new AxleSetup();
    public AxleSetup RearAxle = new AxleSetup();

    [Header("Nitro")]
    public FP MaxNitro = 100;
    public FP NitroForceMultiplier = 2;

    // PARTIAL METHODS
    partial void MaterializeUser(Frame frame, ref Vehicle result, in PrototypeMaterializationContext context)
    {
        result.Prototype = context.ComponentPrototypeRef;
    }
    
    [Serializable]
    public class AxleSetup
    {
        public FPVector3 PositionOffset;
        public FP Width = 1;
        public FP SpringForce = 120;
        public FP DampingForce = 175;
        public FP SuspensionLength = FP._0_10 * 6;
        public FP SuspensionOffset = -FP._0_25;
    }
}
}

Vehicle_Prototype 내의 파라미터는 Vehicle 컴포넌트가 연결된 엔티티의 동작에 영향을 미치는 컴포넌트 인스턴스의 동적 값을 계산하는 데 필요한 값을 보유합니다. 예를 들어 플레이어가 추가 Nitro를 선택하면 Vehicle 컴포넌트 에 있는 값이 Vehicle_Prototype에 있는 MaxNitro 값에 고정됩니다. 이것은 비동기화의 페널티에 제한을 가하고 게임 상태를 슬림 하게 유지합니다.

namespace Quantum
{
    public unsafe partial struct Vehicle
    {
        public void AddNitro(Frame frame, EntityRef entity, FP amount)
        {
            var prototype = frame.FindPrototype<Vehicle_Prototype>(Prototype);
            Nitro = FPMath.Clamp(Nitro + amount, 0, prototype.MaxNitro);
        }
    }
}

메인 화면으로
 

물질화 순서

씬 프로토타입을 포함한 모든 엔티티 프로토타입의 물질화는 다음 순서로 실행됩니다:

  1. 빈 엔티티가 생성됩니다.
  2. 엔티티 프로토타입에 포함된 각 컴포넌트 프로토타입에 대해 다음을 수행합니다.
    1. 컴포넌트 인스턴스가 스택에 생성됩니다.
    2. 컴포넌트 프로토타입은 컴포넌트 인스턴스로 물질화됩니다.
    3. ( 선택 ) MaterializeUser() 가 호출됩니다.
    4. ISignalOnComponentAdded<MyComponent> 시그널을 트리거 하는 엔티티에 컴포넌트가 추가됩니다.
  3. 물질화된 각 엔티티에 대해 ISignalOnEntityPrototypeMaterialized가 실행됩니다.
    • 맵 / 씬 로드 : 시그널을 모든 씬 프로토타입을 물질화한 후 모든 엔티티와 엔티티 프로토타입 쌍에 대해 호출됩니다.
    • Frame.Create()으로 생성: 시그널을 프로토타입이 구체화된 후 즉시 호출됩니다.

컴포넌트 프로토타입의 구체화 단계는 기본 컴포넌트를 미리 정해진 순서대로 구체화합니다.

Transform2D
Transform3D
Transform2DVertical
PhysicsCollider2D
PhysicsBody2D
PhysicsCollider3D
PhysicsBody3D
PhysicsJoints2D
PhysicsJoints3D
PhysicsCallbacks2D
PhysicsCallbacks3D
CharacterController2D
CharacterController3D
NavMeshPathfinder
NavMeshSteeringAgent
NavMeshAvoidanceAgent
NavMeshAvoidanceObstacle
View
MapEntityLink

모든 기본 컴포넌트가 구체화되면 사용자 정의 컴포넌트가 알파벳순으로 물질화됩니다.

MyComponentAA
MyComponentBB
MyComponentCC
...

기술문서 TOP으로 돌아가기