인프런 커뮤니티 질문&답변

고우진님의 프로필 이미지
고우진

작성한 질문수

[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템

Multi Layered State Machine

Statemachine 관련질문이 있습니다.

작성

·

100

0

격투게임이나 몬스터헌터처럼 애니메이션의 특정 프레임 이후에 입력받아야 특정 애니메이션이 실행가능하게 하려하는데 이걸 어떻게 해야할까요?

기존에는 애니메이션 이벤트를 이용해서 구현했었는데 어떻게 해야할까요?

캡처.JPG

애니메이션 이벤트를 활용하려고 하니 오버로드된 함수들 때문에 이런 경고창이 뜨긴하는데 애니메이션 이벤트의 함수들은 오버로드된 함수들이 아니라 괜찮은가요?

아니면 다른 좋은 방법이 있는지 알려주실수있을까요?

답변 3

1

Developer G님의 프로필 이미지
Developer G
지식공유자

간단하게 만든다면 얘기하신대로 만드시면 될거 같구요, 좀 더 제대로 만든다고하면 SpecialCombo처럼 하드 코딩하는 것보다 Command Map을 만들어서 Check하는게 더 좋습니다.

[Serializable]
class Command
{
    public string name;
    public KeyCode Key;
    public Command[] linkedCommand;
}

Q -> W -> E -> R // name: Special Combo
  ㄴ E -> Q // name: Skill 1
  ㄴ E // name: Skill 2
고우진님의 프로필 이미지
고우진
질문자

답장해주셔서 감사합니다.

0

고우진님의 프로필 이미지
고우진
질문자

답변해주셔서 감사합니다

커맨드를 저장하는건 이런식으로

List<KeyCode> keyList = new List<KeyCode>();
void Update()
{
if (Input.anyKeyDown)
{

foreach (KeyCode key in System.Enum.GetValues(typeof(KeyCode)))

{

if (Input.GetKeyDown(key)) { keyList.Add(key); } }

}
}

리스트로 구현하고 공격 애니메이션이 끝날때마다 리스트를 초기화 해준 다음에


커멘더 콜렉터에 bool값을 만든 다음에 해당 리스트에서 특정 조합과 순서가 해당되는게 있다면 참으로 만든 다음에
애니메이션의 transition 넘어가는 조건으로 해당 bool값을 넣어주는 방식으로 구현도 괜찮을까요?
MakeTransition<AttackState, SpecialAttackState>( state => Owner.CommandController.SpecialAttackBool)


입력받은 키의 비교는 예를들어 qwer이 순서대로 눌려야 한다면
List<KeyCode> SpecailCombo = new List<KeyCode> { KeyCode.Q, KeyCode.W, KeyCode.E, KeyCode.R };

bool SpecialAttackBool()

{

for (int i = 0; i < SpecailCombo.Count; i++)

{

if (keyList[i] != SpecailCombo[i])

{

return false;

}

}

return true;

}

0

Developer G님의 프로필 이미지
Developer G
지식공유자

수강해주셔서 감사합니다.

격투 게임과 몬스터헌터의 경우 공격이 커맨드 방식(=약공, 강공 조합)인데요,
공격 시작 시에 커맨드 수집을 끄고, 애니메이션 이벤트를 통해서 원하는 타이밍에 다시 커맨드 수집을 시작하게 만들거나, State내에서 애니메이션의 플레이타임을 확인하여 애니메이션이 어느 정도 진행되면 다시 커맨드 수집을 시작하게 만들면 됩니다. 예시 코드는 다음과 같습니다.

애니메이션 이벤트를 사용

먼저 말로 정리하면 다음과 같습니다.

CommandCollector를 통해 Command를 수집함 -> AttackingState로 들어가서 Command에 따른 Animation(=강공, 약공) 실행 및 Command 수집을 막음 -> Animation Event를 통해 특정 Frame부터 다시 Command를 수집함 -> Command 입력이 있다면 AttackState로 재진입하여 Combo를 이어감

 

// PlayerEntity - CommandCollector.cs
// Player 입력에 따라 Command(=약공, 강공, 점프 등)를 수집함
void Update()
{
    // Player의 Key 입력이 있을 시 Key에 맞는 Command를 찾아옴
    LastCommand = commands.Select(x => x.key == Input.GetButtonDown());
}

void OnDisable()
{
    LastCommand = default;
}


// EntityStateMachine.cs
public void MakeTransitions()
{
    ...
    // 입력된 Command가 있고 Attack Type이라면 다음 Combo로 이어감
    MakeTransition<AttackingState, AttackingState>(x => x.Entity.CommandCollector
.LastCommand.Type == CommandType.Attack);
}


// AttackingState.cs
// 공격 상태
void override void Enter()
{
    ...
    // Animation을 통한 공격 수행
    // name == "strongAttack" or "lightAttack"
    Entity.Animator.SetTrigger(Entity.CommandCollector.LastCommand.name);
    // Command 수집을 멈춤
    Entity.CommandCollector.enable = false;
}

void override void Exit()
{
    ...
    Entity.CommandCollector.enable = true;
}


// AnimationEvents.cs
// Animation Event로 등록하여 사용
public void ActivateCommandCollector()
{
    GetComponent<CommandCollector>().enable = true;
}

 

애니메이션 플레이 타임을 이용하는 방법은 애니메이션 이벤트 대신 AttackingState에서 애니메이션 플레이 타임을 확인해주면 됩니다.

플레이 타임을 확인

// AttackingState.cs
public override Update()
{
    // 현재 실행중인 Animation 정보를 가져옴
    var animationInfo = Entity.Animator.GetCurrentAnimatorStateInfo(0);
    // normailzedTime(=PlayTime) 0 ~ 1
    // Animation이 85퍼 진행되면 Command를 다시 수집함
    if (animationInfo.normalizedTime >= 0.85f)
        Entity.CommandCollector.enable = true;
}

 

언제나 그렇듯 보여드린 예시 코드가 무조건적 정답은 아니구요, 강의 내용을 활용해서 이렇게 만들 수 있다 정도로 보시면 됩니다.

 

오버로딩 경고는 애니메이션 이벤트가 SendMessage 방식이라 이름이 같은 함수들이 있으면 그 함수들 중에서 무엇이 실행될지 알 수 없기 때문에 나오는 경고입니다. Fuction에 해당 함수들을 사용하지 않으면 괜찮구요, 해당 함수들을 사용해야할 경우 래핑 함수 하나를 만들어서 사용해주시면 됩니다.

ex.
Show라는 함수들이 여럿 있을 경우 Show 대신 사용하려는 Show 함수를 래핑한 ShowCallback 함수를 사용해줌
public void ShowCallback()
{

display.Show();

}

 

감사합니다.

고우진님의 프로필 이미지
고우진

작성한 질문수

질문하기