This document is about: QUANTUM 2
SWITCH TO

애니메이션


Available in the Gaming Circle and Industries Circle
Circle

개요

전투 게임에서 캐릭터 애니메이션은 플레이어의 입력(공격, 방어, 콤보)에 반응하여 게임을 구동하고 주어진 시간에 각 캐릭터의 현재 히트 박스와 하트 박스가 어디에 있는지 알게 해주기 때문에 게임 플레이에 매우 중요합니다.

전투 템플릿은 커스텀 애니메이터를 사용하여 결정론적 애니메이션을 활성화하며(자세한 내용은 Addons > Custom Animator 페이지 참조) 전투 게임에 필요한 기능을 포함하도록 확장됩니다.

애니메이터 매개변수

커스텀 애니메이터 그래프는 유니티 애니메이터의 애니메이션 매개 변수를 베이킹할 때와 동일한 순서로 베이킹 됩니다. 이러한 매개변수의 런타임 조작을 용이하게 하기 위해 전투 템플릿은 CAParameters 열거 값을 생성했습니다. 기본적으로 전투 템플릿에 포함된 모든 파라미터를 나열하고 정확한 인덱스를 명시적으로 할당합니다.

이러한 방식으로 CAP 매개 변수를 설정하면 다음과 같은 인터페이스를 사용할 수 있습니다.:

  • 런타임에 enum을 int로 캐스팅하여 파라미터 설정과
  • 새로운 입력 시퀀스 생성에 사용되는 InputCommandDataBase내의 매개변수 표현.

커스텀 애니메이터 확장

전투 템플릿은 FighterAnimatorState, CustomAnimatorBehaviour 그리고 CustomAnimatorStateSystem를 가진 커스텀 애니메이터를 확장합니다.

커스텀 애니메이터 상태 시스템

CustomAnimatorStateSystem는 간단한 SystemSignalsOnly 시스템입니다. 이 기능의 목적은 애니메이터 상태가 발생되는 OnEnter, OnUpdate 및 OnExit 상태 관련 신호에 반응하는 것입니다.

C#

ISignalOnAnimatorStateEnter
ISignalOnAnimatorStateUpdate
ISignalOnAnimatorStateExit

그런 다음 엔티티의 커스텀 애니메이터에서 선택된 매개 변수를 설정하고 FightAnimationState에 포함된 동작을 반복하며 이러한 매개 변수(OnEnter, OnUpdate 및 OnExit)에 대한 보완 방법을 실행합니다.

파이터 애니메이터 상태

기본값으로, CustomAnimator는 업데이트, 블렌드 및 특정 애니메이션 상태의 모션의 취득을 위해서 AnimatorState 에셋을 사용합니다. 템플릿은 이 에셋을 FigherAnimatorState 에셋에 참조를 포함함으로써 확장합니다.

C#

// See CustomAnimatorState.cs
public AssetRefFighterAnimatorState StateAsset;

FighterAnimatorState의 목적은 두 가지 입니다:

  • 여기에는 특정 상태와 관련된 다양한 FighterAnimatorBehaviours가 포함되어 있습니다. 이러한 FighterAnimatorBehavioursQTASB Container 자산을 베이킹하기 전에 유니티 애니메이터의 QTASB Container의 일부로 편집할 때 설정됩니다.
  • 특정 애니메이션 상태에 대한 HurtBoxSet 형태의 캐릭터의 상처 상자 위치를 포함합니다. HurtBoxSet는 애니메이션의 각 프레임에 대해 캐릭터의 상처 상자 위치를 지정하고 그에 따라 Fighter 컴포넌트의 hurtBoxList 변수를 업데이트합니다.

Quantum 애니메이터 상태 행동

파이팅 템플릿은 추상 에셋 클래스 CustomAnimatorBehaviour를 통해 커스텀 애니메이터용 애니메이션 상태 행동을 사용할 수 있도록 해 줍니다.

C#

public abstract unsafe partial class CustomAnimatorBehaviour
{
    public abstract void OnEnter(Frame f, EntityRef entity, CustomAnimator* animator);

    /// <summary>
    /// Performed during a state's update.
    /// <returns>If true, the state will stop updating behaviours.  Usually done if a transition occurs mid-state</returns>
    public abstract bool OnUpdate(Frame f, EntityRef entity, CustomAnimator* animator);

    public abstract void OnExit(Frame f, EntityRef entity, CustomAnimator* animator);
}

FighterAnimatorBehaviour

FighterAnimatorBehaviourCustomAnimatorBehaviour 에셋으로부터 파생됩니다. 역시 추상 클래스로서 OnEnter(), OnUpdate() 그리고 OnExit() 메소드를 커스터마이징하여 “active” 상태의 견고한 구현을 작성하기 위해서 사용됩니다.

C#

public abstract unsafe class FighterAnimatorBehaviour : CustomAnimatorBehaviour
{
   public override unsafe void OnEnter(Frame f, EntityRef entity, CustomAnimator* animator) {
       GetFighterAndTransform(f, entity, out Fighter* fighter, out Transform3D* transform);
       OnEnter(f, entity, fighter, animator, transform);
   }

   private static void GetFighterAndTransform(Frame f, EntityRef entity, out Fighter* fighter, out Transform3D* transform) {
       fighter = f.Unsafe.GetPointer<Fighter>(entity);
       transform = f.Unsafe.GetPointer<Transform3D>(entity);
   }

   public override unsafe bool OnUpdate(Frame f, EntityRef entity, CustomAnimator* animator) {
       GetFighterAndTransform(f, entity, out Fighter* fighter, out Transform3D* transform);
       return OnUpdate(f, entity, fighter, animator, transform);
   }

   public override unsafe void OnExit(Frame f, EntityRef entity, CustomAnimator* animator) {
       GetFighterAndTransform(f, entity, out Fighter* fighter, out Transform3D* transform);
       OnExit(f, entity, fighter, animator, transform);
   }

   public abstract void OnEnter(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform);
   public abstract bool OnUpdate(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform);
   public abstract void OnExit(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform);
}

활성 상태는 게임 플레이에 즉시 영향을 미치려고 시도하는 기능(OnEnter() 또는 OnUpdate())과 타이머가 없는 것이 특징입니다. 예를 들어 QTASBResetCombo 동작은 입장할 때 엔티티의 콤보 카운트를 재설정합니다.

C#

public class QTASBResetCombo : FighterAnimatorBehaviour
{
    public override unsafe void OnEnter(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform)
    {
       fighter->comboCount = 0;
       frame.Events.OnComboCountChanged(fighter->index, fighter->comboCount);
    }
}

반면 QTASBSetBlockState는 매 프레임의 OnUpdate()에서 캐릭터의 블로킹 상태를 업데이트합니다.

C#

public override bool OnUpdate(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* anim, Transform3D* transform)
{
   if (CustomAnimator.GetInteger(frame, anim, CAParameters.Horizontal_I_Index) < 0)
   {
       fighter->blocking = CustomAnimator.GetInteger(frame, anim,
           CAParameters.Vertical_I_Index) < 0 ? BlockState.Low : BlockState.High;
   }
   else
   {
       fighter->blocking = BlockState.None;
   }
   return false;
}

QTAnimatorTriggerBehaviour

“능동적” 행동의 대안은 “수동적” 행동입니다. 이러한 설정은 한 번만 트리거 되며, 애니메이션이 triggerTime 매개변수로 정의된 시간 동안 현재 상태를 계속 유지한 후에만 트리거 됩니다. 이 동작은 추상 자산 클래스 QTAnimatorTriggerBehaviour에 의해 제어됩니다.

C#

public abstract unsafe class QTAnimatorTriggerBehaviour : FighterAnimatorBehaviour
{
   public FP triggerTime;

   public override unsafe bool OnUpdate(Frame frame, EntityRef entity, Fighter* fighter, CustomAnimator* animator, Transform3D* transform)
   {
       var triggeredList = frame.ResolveList(animator->TriggeredStateBehaviours);
       if (triggeredList.IndexOf(Guid) >= 0)
           return false;

       if (CustomAnimator.GetActiveWorldTime(frame, animator) >= triggerTime)
       {
           triggeredList.Add(Guid);
           return OnTriggerBehaviour(frame, entity, fighter, animator, transform);
       }

       return false;
   }

   protected abstract unsafe bool OnTriggerBehaviour(Frame frame, EntityRef entity, Fighter* fighter, CustomAnimator* animator, Transform3D* transform);

   public override unsafe void OnExit(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform)
   {
       var triggeredList = frame.ResolveList(animator->TriggeredStateBehaviours);
       triggeredList.Remove(Guid);
   }
}

예를 들어 QTABCreateProjectile는 플레이어가 공격을 했을 때 발사체 생성에 대한 트리거에 사용됩니다.

C#

public class QTABCreateProjectile : QTAnimatorTriggerBehaviour
{
   public FPVector3 startPoint;
   public AssetRefEntityPrototype projectileEntityPrototype;

   public override unsafe void OnEnter(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform)
   {
   }

   protected override unsafe bool OnTriggerBehaviour(Frame frame, EntityRef fighterRef, Fighter* fighter, CustomAnimator* animator, Transform3D* transform)
   {
       var prototypeRef = frame.Assets.Prototype(projectileEntityPrototype);
       var prototype = frame.Create(prototypeRef);

       var proj = frame.Get<Projectile>(prototype);
       var projT = frame.Get<Transform3D>(prototype);
       var projHB = frame.Get<HitBox>(prototype);

       proj.side = fighter->side;

       FPVector3 pos = startPoint;
       pos.X *= fighter->side;
       projT.Position = transform->Position + pos;

       projHB.active = true;
       projHB.attacker = fighterRef;
       projHB.target = frame.Global->fighters[1 - fighter->index];
       projHB.bounds.Center = projT.Position.XY;

       frame.Set(prototype, proj);
       frame.Set(prototype, projT);
       frame.Set(prototype, projHB);

       frame.Events.PlayAudioEffect(projT.Position.XY, frame.Assets.AudioPlaybackData(proj.sfxOnCreate));

       return false;
   }
}
Back to top