부트캠프/본캠프

[내일배움캠프_2025SEP22] 이펙트(카메라), 애니메이션

Young_A 2025. 9. 22. 22:16

목차

    이펙트(카메라), 애니메이션

    오늘 할 일:

    (오후 9시 이후) 모의면접 23, 28, 33, 38번 문제 준비하기


    카메라 매니저: Cinemachine

    Camera와 관련된 효과에 집중하고 이를 나머지 이펙트들은 추후 EffectManager라는 이름으로 구현하게 될 것 같다.

    카메라 효과는 Cinemachine을 사용하여 진행하기로 했다.

     

    사용처는 플레이어나 몬스터의 전투 진행 시 카메라 효과가 필요할 때,

    각자 개발하는 것보다는 같이 개발하고 기능을 공유하는 것이 재사용성이나 유지 보수가 효율적일 것 같기 때문이다.

    Player를 따라다니기

    MainCameradp Cinemachinebrain을 달아주고,

    virtualcamera를 생성했다.

    Follow에 Player를 연결해주면 끝.

     

    모르고 Look at에도 Player를 연결해주었었는데, 이 경우 카메라의 시선이 Player를 향하느라 xyz 축으로 로테이션을 돌아서...

    디지몬 어드벤처 세계로 빨려가듯 화면이 돌아갔다. 

    보여지는 범위를 제한하기

    Cinemachine Confiner 2D를 AddExtention을 통해 추가해주고,

    *Polygon Collider 2D*를 빈 오브젝트에 생성해주고 그걸 위의 이미지와 같이 연결해주면 된다.

    이때 Player가 그라운드를 뚫고 추락하였다.

    Collider가 충돌되어서 그런 것 같아서... 아래와 같이 Player의 리지드바디에서 CameraCollider Layer를 제외하고 인식하도록 설정하였다.

    -> 다른 부분에서도 계속 오류가 나길래 Player Settings에서 매트릭스 충돌이 안나게 전부 빼버렸다.

    PolygonCollider2D의 IsTrigger를 체크해도 되었겠지만, 그 또한 Trigger 연산이 들어가니 매트릭스에서 아예 빼는게 낫겠다는 판단을 내렸다.


    애니메이션 플레이 체크

    IsAnimationPlaying 메서드의 한계!

    public static bool IsAnimationPlaying(Animator animator, int animationHash, 
        float startTime = 0.0f, float endTime = 1.0f)
    {
        if (animator == null) return false;
    
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
        return stateInfo.shortNameHash == animationHash 
               && stateInfo.normalizedTime >= startTime 
               && stateInfo.normalizedTime < endTime;
    }

    Utility에서 위와 같은 메서드를 사용하고 있었다!

    이걸로 Loop가 돌아가는 애니메이션 또한 플레이 중인지 체크하고 있었는데 자꾸 false를 반환하고 있었다.

    디버그로그로 해시값을 찍어봤는데 같았고, 비교도 해보았는데 true가 출력되는 것을 확인할 수 있었다.

     

    찾아보니 Loop 애니메이션은 normalizedTime이 1을 초과할 수 있다고 한다.

    따라서 false가 출력되면서 문제가 생기고 있었다.

     

    해결: 애니메이션 루프 여부 체크

    public static bool IsAnimationPlaying(Animator animator, int animationHash,
        float startTime = 0.0f, float endTime = 1.0f)
    {
        if (animator == null) return false;
    
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
    
        if (stateInfo.shortNameHash != animationHash)
            return false;
    
        if (stateInfo.loop)
        {
            float normalizedTime = stateInfo.normalizedTime % 1.0f;
            return normalizedTime >= startTime && normalizedTime < endTime;
        }
        else
        {
            return stateInfo.normalizedTime >= startTime && stateInfo.normalizedTime < endTime;
        }
    }

    위와 같이 메서드를 수정하였다.


    느낀점

    오전에 연출을 같이 맡기로 한 분과 CameraManager를 같이 페어 프로그래밍을 했다.

    지난 협업 프로젝트 때는 Cinemachine을 쓰려다가 뭔가 더 복잡한 것 같아서,

    코드 5줄 쓰면 되는데~ 하고 CameraFollow를 쓰고 끝냈었다.

     

    근데 이번에는 무조건 한번 써보자!! 우리 카메라 효과도 있으니까. 하고 써봤는데

    생각보다 간단하고 효과 주기도 편하고 좋은 것 같다.

     

    오후에는 컨디션이 조금 좋지 않았다.

    그렇다고 아무것도 안하고 있는 것보다는 집중력이 필요한 신규 기능 구현 대신,

    코드의 가독성과 유지보수성을 높이는 리팩토링을 진행하면서 컨디션을 회복하려고 했다.

     

    그래도 오늘 경험을 통해 Cinemachine의 주요 기능과 Collider 상호작용을 제어하는 방법을 배우고, 애니메이션 상태 체크 로직을 더 효율적으로 개선할 수 있었던 것 같다.


    내일 학습 할 것은 무엇인지

    연출 마무리.

    오늘 기획팀에게 보여주고 나서 받은 피드백

    몬스터가 백그라운드로 빠졌을 때-> 컬러값 조금 어둡게.

    OrderInLayer 조정.

     

    그리고 EffectManager를 결국 만들긴 해야할 것 같다.

    임시로 TestManager를 호출하고 있는데 이미 세번이나 호출하게 되어버림.

    이럴바에는 EffectManager를 생성을 지금 해놓는게 좋을 것 같다.