This document is about: QUANTUM 2
SWITCH TO

액터

개요

ActorSystem은 사용자 지정 엔티티 행동을 생성하는 편리한 방법을 제공합니다.

설정

새로운 액터 생성은 다음 단계로 설정합니다:

  1. 새로운 컨트롤러 클래스를 생성합니다.
  2. ActorController 또는 UpdatableActorController로부터 상속을 받습니다.
  3. 특정 롤백 가능한 액터 데이터를 위한 새로운 Quantum 컴포넌트를 생성합니다. ( 선택 사항 );
  4. 컨트롤러에 롤백 할 수 없는 속성을 추가합니다.
  5. OnInitialize(), OnDeinitialize() 그리고 OnTick() 메소드를 오버라이드 합니다.
  6. 엔티티 로직을 작성합니다.
  7. Quantum 솔루션을 다시 빌드하고
  8. EntityComponentController 컴포넌트에 이 클래스의 새 인스턴스를 만들어 유니티에서 이 컨트롤러 타입을 사용합니다.

그림에 표시된 것처럼 ActorController 클래스는 엔티티의 초기화 및 초기화 해제를 처리하는 반면 UpdatableActorController는 업데이트 가능한 모든 것을 관리합니다.

updatable actor controller
Updatable Actor Controller.

구현 예제

이 섹션에서는 FPS 템플릿에 포함된 컨트롤러의 몇 가지 예를 제시합니다.

소멸 가능한 객체 컨트롤러

C#

internal override sealed void Update(Frame frame, EntityRef entity)
{
    // 1. Check Health state every frame
    
    Health* health = frame.GetComponent<Health>(entity);
    if (health->IsAlive == false)
    {
        Transform3D* transform3D = frame.GetComponent<Transform3D>(entity);

        Destroy(frame, entity, transform3D->Position, transform3D->Rotation);
    }
}

private void Destroy(Frame frame, EntityRef entity, FPVector3 position, FPQuaternion rotation)
{
    EntityRef ownerEntity = frame.TryGetOwnerEntity(entity);

    // 2. Spawn effects
    
    foreach (DestroyEffect effect in DestroyEffects)
    {
        frame.CreateEntity(effect.EntityPrototype, ownerEntity, position + effect.PositionOffset, rotation);
    }
    
    // 3. Invoke event and destroy entity

    frame.Events.DestructibleObjectDestroyed(entity);
    frame.DestroyEntity(entity);
}

체력 영역

C#

public unsafe class HealthAreaController : UpdatableActorController, IInteractableTriggerController
{
    //========== PUBLIC MEMBERS ===================================================================================

    public EHealthAction Action        = EHealthAction.Remove;
    public EHealthType   Type          = EHealthType.Health;
    public FP            HealthPerTick = FP._10;
    public FP            TickInterval  = FP._1;
    public bool          InitEnabled   = true;
    public LayerMask     TargetLayerMask;

    //========== ActorController INTERFACE ========================================================================

    internal override void Initialize(Frame frame, EntityRef entity)
    {
        // 1. Add HealthArea component (support for custom rollbackable data), set initial state
        
        HealthArea* healthArea = frame.TryGetOrAddComponent<HealthArea>(entity);
        healthArea->SetEnabled(frame, entity, InitEnabled);
    }

    //========== UpdatableActorController INTERFACE ===============================================================

    internal override void Update(Frame frame, EntityRef entity)
    {
        HealthArea* healthArea = frame.GetComponent<HealthArea>(entity);
        if (healthArea->IsEnabled == false)
            return;
            
        // 2. Health action ticks in intervals

        healthArea->TickDelay -= frame.DeltaTime;

        if (healthArea->TickDelay <= FP._0)
        {
            Tick(frame, entity);

            healthArea->TickDelay += TickInterval;
        }
    }

    //========== IInteractableTriggerController INTERFACE ===================================================================

    bool IInteractableTriggerController.OnEnter(Frame frame, EntityRef entity, EntityRef instigator, TriggerInfo3D info)
    {
        // 3. Only entities with Health component and valid layer are tracked
        
        if (frame.HasComponent<Health>(instigator) == false)
            return false;
        if (info.Other.HasValidLayer(frame, TargetLayerMask) == false)
            return false;

        return true;
    }

    void IInteractableTriggerController.OnExit(Frame frame, EntityRef entity, EntityRef instigator, ExitInfo3D info)
    {
    }

    //========== PRIVATE METHODS ==================================================================================

    private void Tick(Frame frame, EntityRef entity)
    {
        InteractableTrigger* interactableTrigger = frame.GetComponent<InteractableTrigger>(entity);
        QList<EntityRef>     instigators         = frame.ResolveList(interactableTrigger->Instigators);

        if (instigators.Count == 0)
            return;

        // 4. Prepare hit and health data
        
        HitData hitData = new HitData()
        {
            Source       = entity,
            Instigator   = entity,
            BodyPartType = EBodyPartType.None,
            HitSource    = EHitSource.Environment,
        };

        HealthData healthData = new HealthData
        {
            Action        = Action,
            DesiredAmount = HealthPerTick,
            Source        = entity,
            Instigator    = entity,
            Type          = Type,
        };

        // 5. Iterate over all instigators (allowed entities) and hit them if they are alive
        
        for (int i = instigators.Count; i --> 0;)
        {
            EntityRef instigator = instigators[i];
            if (frame.DestroyPending(instigator) == true)
                continue;

            Health* health = frame.GetComponent<Health>(instigator);
            if (health->IsAlive == false)
                continue;

            hitData.Target   = instigator;
            hitData.Position = frame.GetComponent<Transform3D>(instigator)->Position;

            healthData.Target = instigator;

            frame.Signals.Hit(hitData, healthData);
        }
    }
}

피해 영역

이전 섹션에 표시된 체력 영역에서 체력을 빼서 실드를 피해 영역에 추가하는 것으로 변경할 수 있습니다.

damage area
피해 영역.

새로운 액터 생성하기

다음은 FPS 템플릿에 새로운 액터 를 만드는 단계별 절차입니다. 그 단계는 플레이어 액터와 AI 액터 모두 동일합니다.

  1. Quantum 솔루션에서, 다음 클래스로부터 상속을 받는 새로운 MyActorController 클래스를 생성합니다.
    • ActorController: 정적 액터용 또는
    • UpdatableActorController: 매 프레임마다 갱신되는 동적 액터용.
  2. 컨트롤러에 직렬화 가능한 필드를 추가합니다 (환경 구성)
  3. 필요시 MyActor 컴포넌트 정의를 포함하고 롤백 가능한 데이터와 같은 데이터 구조체를 위해 MyActor.qtn을 생성합니다.
  4. 컨트롤러에서 Initialize(), Deinitialize() 그리고 Update() 메소드를 구현합니다.
  5. 사용할 수 있는 컨트롤러 인터페이스를 구현합니다 (예: IInputController).
  6. quantum.code 솔루션을 재 컴파일합니다.
  7. 유니티에서 빈 게임 오브젝트를 생성하고 하위 객체로 비주얼 컴포넌트 / 모델을 추가합니다.
  8. 게임 오브젝트에 3개의 핵심 스크립트인 Entity, EntityPrototypeEntityComponentController를 추가합니다.
  9. Entity 컴포넌트에서, Transform Synchronization을 다음과 같이 설정합니다
    • None: 스폰 이후 위치 / 회전이 절대로 변경되지 않는 경우
    • Simple: 위치 / 회전을 보간할 필요가 없을 때
    • Interpolate / Error Correction: 부드러운 위치 / 회전 동기화를 위해
  10. EntityPrototype 컴포넌트에서 Transform 을 3D로 설정합니다.
  11. EntityComponentController 컴포넌트에서 ActorController 드롭다운 메뉴에서 MyActorController를 선택하고 정의된 속성으로 채웁니다.
  12. 엔티티 행동 정의를 위해 기타 엔티티 컴포넌트들을 추가합니다.(EntityComponentMyActor, EntityComponentHealth, EntityComponentPhysicsCollider3D 등).
  13. 엔티티의 프리팹을 생성합니다 (선택).
  14. Quantum > Generate Asset Resources 메뉴를 통해 에셋 리소스 생성을 실행합니다.
  15. 이제 엔티티를 사용할 준비가 되었고 - 씬으로 드래그 앤 드롭, 다른 엔티티 필드들의 엔티티 프로토 타입을 참조합니다.
Back to top