ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TopDown Shooting Game / Animation 기능 만들기
    게임 개발기록 2024. 5. 25. 15:39

    움직이거나 공격 하고 맞을 때 Animation 기능이 나오게 해보자.

     

    먼저 Animation을 작업 할 때 우리는 SetBool(" Run", true) 이런 식으로 사용 해서 특정 애니메이션이 작동 되게 했었다. 

    하지만 여기서 사용 되는 문자열 자체가 비용이 크고 연산이 느리기에 효율적이라고 보기 어렵다.

     

    그래서 StringToHash 를 사용해서 해시값으로 변경을 한 후에 사용을 하는 것이 좋다. Animator 작업 할 때 자주 사용 된다.

     

    StringToHash란 문자열을 해쉬값(Hash Value)으로 바꿔주는 함수다. 이 함수는 문자열을 입력으로 받아 고정된 길이의 고유한 해시 값을 출력한다. 그로 인해 데이터가 겹칠 일이 없어서 거의 충돌이 일어나지 않는다. 똑같은 해쉬값이 나온다는 걸 충돌이라고 하는데 거의 없다. 

     

    그리고 int는 연산이 훨씬 빠르기에 더욱 효율적이다. 그리고 또 다른 특징은 일방향(단방향)으로 작동한다. 그러니까 문자열에서 해쉬값으로 변환만 가능하지 해쉬값에서 문자열로 바꿀 수가 없다. 해시 값을 통해 원래 입력 값을 역추적하는 것이 매우 어렵거나 불가능하다는 것을 의미 한다.

     

    이러한 특징으로 인해 보안성이 좋다고 할 수 있다. 암호화 같은 느낌이 된다. 비밀번호 같은 것을 직접 저장하는 대신, 해시 값을 저장함으로써 보안을 강화할 수 있다고 한다.

     

    그래서 이번엔 StringToHash를 사용 하여 Animator를 작업 해주도록 한다. 

     

    먼저 Animator를 작업 하기 위해서는 컴포넌트를 가져와 주어야 한다. 우리는 이 GetComponent만 하는 클래스를 만들어서 상속을 이용해 작업을 할 것이다. 그래서 나중에 PlayerAnimation 클래스, 뭐 다른 Anmation등등을 만들 때 확장성을 늘릴 수 있다.

     

    AnimationController 이라는 클래스를 만들고 거기에 TopDownController 클래스와 Animator를 GetComponent를 해주게 만들어 준다. 그리고 MonoBehaviour를 상속 받는다. 

     

    그리고 CharacterAnimationController이라는 클래스를 만들어서 AnimationController 을 상속 받게 한다. 이제 GetComponent를 따로 해줄 필요 없이 Animator 코드를 작성 해주면 된다. 

     

    우리는 StringToHash를 사용하여 Anmator를 작업 해주기로 했으니 필드값부터 초기화를 해준다. 

     

    private static readonly int isWalking = Animator.StringToHash("isWalking");

     

    이런 식으로 필드의 값을 초기화 해줄 것인데 필드 선언 하는 코드가 평소와 조금 다른 것을 확인 할 수가 있다. 

     

    readonly는 선언 할 때만 초기화를 하게 하는 기능이다. 그러니 선언 이후에는 읽기만 가능 하고 값을 변경 하는 게 불가능 하게 만드는 것이다.

     

    읽기전용임을 명시 하는 것이고 데이터의 무결성을 보호하고 의도치 않은 수정을 방지하는 데 사용된다. 

    프로그래밍 언어, 데이터베이스, 파일 시스템 등 다양한 환경에서 활용 된다고 한다.

     

    그리고 static 은 모두가 공유하는 공유자원이 되게 하는 것이다. 객체가 여러 개 생성이 되어도 static으로 만들어진 것은 변하지 않으며, 모든 객체가 이것을 공유한다. 객체가 생성 되어도 변하지 않으므로 정적자원이다. 

    여러 객체가 동일한 값을 가질 필요가 있을 때, 각각의 인스턴스마다 별도의 메모리를 할당하는 대신, 클래스 수준에서 하나의 복사본만을 사용하여 메모리를 절약할 수 있다. 성능이 좋아지는 것이다. 

     

    이러한 static과 readonly의 사용은 중요한 Hash의 Value가 계속 생성이 되지 않게 하고, 의도치 않게 변경이 일어나지 않게 하는, 처음 한 번 선언이 될 때 결정 되게 하고 변하지 않게 하기 위해서 보완 해주는 코드라고 생각 할 수가 있다.

     

    변경 되지 않게 HashValue를 만들어 주고 모든 객체에서 동일한 그 해쉬값을 참조 하게 함으로써 메모리를 절약 하고 성능을 올릴 수 있게 되는 것이다.

     

    그래서 isHit, attack 등 animation에 StringToHash의 값을 모두 static readonly를 사용 해서 초기화 해준다. 

     

    private static readonly int isWalking = Animator.StringToHash("isWalking");
    private static readonly int isHit = Animator.StringToHash("isHit");
    private static readonly int Attack = Animator.StringToHash("attack");

     

    이러면 이제 우리는 이 변수들을 활용 해서 기존 문자열연산보다 빠르고 효율적이게 Animator를 관리 할 수 있게 된다. 

     

    우리는 현재 TopDownShoothing Game을 옵저버패턴을 이용하여 작업 하고 있다. 그래서 이 Animation기능들도 옵저버 패턴을 이용한 이벤트 함수들을 사용 하여 작동이 되게 해준다. 

     

    먼저 Move, isWalking Animation이다. 

     

    일단 CharacterAnimationController에 Start 함수에서 이벤트 함수에 함수가 추가 되게 만들어 준다. 

     

    private void Start()
    {
        controller.OnAttackEvent += Attacking;
        controller.OnMoveEvent += Move;

    }

     

    Event에 Move 함수가 들어가 있는데 또 Move라는 이름을 쓴 이유는 그것은 오버로드.., 맞나?.. 아님 아닌데? 

     

    OnMoveEvent 는 캐릭터가 이동이 될 때 작동이 되는 Event므로 이 이벤트가 실행 될 때 Animation도 실행을 하게 해주는 것이다. 그래서 이 Move함수를 만들어 준다. 

     

     private void Move(Vector2 vector)
     {
         animator.SetBool(isWalking, vector.magnitude > magnituteThreshold);
     }

     

    이벤트가 실행 될 때 SetBool이 작동이 되게 한 것인데 여기서 바로 true가 되지 않게 했다. 왜냐하면 현재 캐릭터가 움직일 때, 그러니까 키보드를 누르고 있을 때 이 isWalking Animation이 실행 되게 할 것인데 바로 true로 바꿔서 애니메이션이 실행 되게 하는 것은 뭔가 알맞지 않다. 그래서 다른 방법을 사용 한 것을 볼 수가 있다. 

     

    vector.magnitude 라는 코드를 사용 한 것을 볼 수가 있는데 이 magnitude은 Vector의 크기를 계산 해주는 기능이다. 그러니까 magnituteThreshold 라는 변수보다 매개변수로 받은 Vector의 크기가 크면 true가 되게 해주는 것이다.

    ( magnituteThreshold 는 미리 만들어둔 해당 클래스의 변수, 현재 0.5f의 값이 들어가 있다.)

     

    만약 키보드를 눌렀을 경우에 Vector의 값은 1,1이나 1,0이 됨으로써 magnituteThreshold 커지게 되고 이 Animation이 작동 되게 하는 것이다. 이러면 자연스럽게 키보드를 눌르고 있으면 Animation이 작동 되게 해줄 수 있다. 

     

    그리고 isWalking은 우리가 StringToHash 값으로 만들어둔 int형 변수다.

     

    그리고 이제 Attacking 함수이다. 

     

     private void Attacking(AttackSO sO)
     {
         animator.SetTrigger(Attack);
     }

     

    코드를 보면 SetTrigger를 사용한 모습을 볼 수가 있다. 저것은 일회성 신호로써 해당 이벤트가 실행 됐을 때 딱 한 번 Animation이 작동이 되게 하고 다시 리셋을 한다. 

     

    그러니까 OnAttackEvent 가 실행 되면 Attack이라는 Animation이 한 번 활성화가 되고 다시 비활성화가 된다.

     

    이렇게 해서 공격을 했을 경우에 공격 했을 때 Animation이 작동 되게 할 수 있는 것이다. Bool을 이용 하여 할 경우에는 true로 했다가 false로 했다가 번거롭다. 각각에 알맞는 상황에 따라 효율적으로 작업을 해야 한다. 

     

    이제 isHit Animation인데 코드를 보면 아래와 같이 작성이 된 것을 볼 수가 있다. 

     

     private void Hit()
     {
         animator.SetBool(isHit, true);
     }

     private void InvincibilityEnd()
     {
         animator.SetBool(isHit, false);
     }

     

    똑같이 Event들이 실행 되면 true나 false로 바뀌게 되는 코드이고 Hit를 하게 되면 일정시간 무적시간이 생기고 시간 이후에 무적시간이 풀리게 된다. 그로 인해서 Hit와 InvincibilityEnd라는 함수를 만들어 Bool자료형이 변경 되는 것을 이용함으로써 isHit Animation을 작업 한 것을 볼 수가 있다. 

     

    이렇게 하면 코드 작업은 끝이 났고, Animation을 만들어 주고 Animator로 Animation들을 관리 해주면 된다. 

     

    그리고 Animator기능을 사용해서 MakeTransition 기능을 사용 해서 Animation이 전환 되게 만들어 줘야 한다. 

     

    그리고 Attack Animation같은 경우는 움직이는 게 아니고 무기만 띠용띠용 하는 것이다 보니 다른 Rayer로 빼서 관리 할 것이다. 

     

    솔직히 이건 안 적어도 된다. 다들 하는 방법 알잖아. 

     

    animator 컴포넌트 추가하고 폴더 나눠줘서 animator Assets 만들고, animation 추가 해주고.., move, attack 등등.., 녹화 버튼 누르고 .. Samples는 12.., 이후에 animator 파라미터 bool, Trigger 등등 만들어 주고,, MakeTransition 설정.., Has Exit Time은 체크 해제.., 몇 초 뒤에 실행 할 것인가임. 그리고 Conditions 추가.., 파라미터 이름 true, false 설정..,,

     

    =====

    Animator Override Controller - 애니메이션 파일만 갈아 끼워서 같은 애니메이터를 활용 하게 하는 컴포넌트.

    행동은 같은데 그림만 바뀌는 상황 일 때 유용하다.

Designed by Tistory.