액터
개요
ActorSystem
은 사용자 지정 엔티티 행동을 생성하는 편리한 방법을 제공합니다.
설정
새로운 액터 생성은 다음 단계로 설정합니다:
- 새로운 컨트롤러 클래스를 생성합니다.
ActorController
또는UpdatableActorController
로부터 상속을 받습니다.- 특정 롤백 가능한 액터 데이터를 위한 새로운 Quantum 컴포넌트를 생성합니다. ( 선택 사항 );
- 컨트롤러에 롤백 할 수 없는 속성을 추가합니다.
OnInitialize()
,OnDeinitialize()
그리고OnTick()
메소드를 오버라이드 합니다.- 엔티티 로직을 작성합니다.
- Quantum 솔루션을 다시 빌드하고
EntityComponentController
컴포넌트에 이 클래스의 새 인스턴스를 만들어 유니티에서 이 컨트롤러 타입을 사용합니다.
그림에 표시된 것처럼 ActorController
클래스는 엔티티의 초기화 및 초기화 해제를 처리하는 반면 UpdatableActorController
는 업데이트 가능한 모든 것을 관리합니다.
구현 예제
이 섹션에서는 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);
}
}
}
피해 영역
이전 섹션에 표시된 체력 영역에서 체력을 빼서 실드를 피해 영역에 추가하는 것으로 변경할 수 있습니다.
새로운 액터 생성하기
다음은 FPS 템플릿에 새로운 액터 를 만드는 단계별 절차입니다. 그 단계는 플레이어 액터와 AI 액터 모두 동일합니다.
- Quantum 솔루션에서, 다음 클래스로부터 상속을 받는 새로운
MyActorController
클래스를 생성합니다.ActorController
: 정적 액터용 또는UpdatableActorController
: 매 프레임마다 갱신되는 동적 액터용.
- 컨트롤러에 직렬화 가능한 필드를 추가합니다 (환경 구성)
- 필요시
MyActor
컴포넌트 정의를 포함하고 롤백 가능한 데이터와 같은 데이터 구조체를 위해MyActor.qtn
을 생성합니다. - 컨트롤러에서
Initialize()
,Deinitialize()
그리고Update()
메소드를 구현합니다. - 사용할 수 있는 컨트롤러 인터페이스를 구현합니다 (예:
IInputController
). quantum.code
솔루션을 재 컴파일합니다.- 유니티에서 빈 게임 오브젝트를 생성하고 하위 객체로 비주얼 컴포넌트 / 모델을 추가합니다.
- 게임 오브젝트에 3개의 핵심 스크립트인
Entity
,EntityPrototype
및EntityComponentController
를 추가합니다. Entity
컴포넌트에서,Transform Synchronization
을 다음과 같이 설정합니다None
: 스폰 이후 위치 / 회전이 절대로 변경되지 않는 경우Simple
: 위치 / 회전을 보간할 필요가 없을 때Interpolate
/Error Correction
: 부드러운 위치 / 회전 동기화를 위해
EntityPrototype
컴포넌트에서Transform
을 3D로 설정합니다.EntityComponentController
컴포넌트에서ActorController
드롭다운 메뉴에서MyActorController
를 선택하고 정의된 속성으로 채웁니다.- 엔티티 행동 정의를 위해 기타 엔티티 컴포넌트들을 추가합니다.(
EntityComponentMyActor
,EntityComponentHealth
,EntityComponentPhysicsCollider3D
등). - 엔티티의 프리팹을 생성합니다 (선택).
Quantum > Generate Asset Resources
메뉴를 통해 에셋 리소스 생성을 실행합니다.- 이제 엔티티를 사용할 준비가 되었고 - 씬으로 드래그 앤 드롭, 다른 엔티티 필드들의 엔티티 프로토 타입을 참조합니다.