ABOUT ME

개인 프로젝트의 진행 사항과 공부하면서 개인적으로 기억해둬야 하거나 나중에 필요할듯한 내용을 정리해서 올립니다.

  • [UE5 | FPS] 반동을 담하는 ControlShake와 반동 패턴
    언리얼엔진/FPS 프로젝트 2024. 5. 6. 17:02

     

     

    사격했을때의 반동을 구현해야하는데 필요한 조건을 생각해봤다.

     

    1. 카메라가 흔들려야하고 원래의 위치로 돌아와야함.

    2. 크로스헤어도 흔들려서 이어서 발사되는 총알은 흔들린 크로스헤어 방향으로 나아가야함.

    3. 카메라의 흔들림은 짧은 시간동안 유지되지만, 끊기지 않고 부드럽게 이어져야함.

    4. 총기에 따라 다양한 반동을 표현할 수 있어야함.

     

    4번의 예시. 에임이 올라갔다가 원을 그리듯이 둥글게 내려오는걸 볼 수 있다.

     

    여기에 추가적인 조건으로는 구현 방식에 따라 무작위 반동과, 정해진 패턴이 있는 반동으로 나눌 수 있다.

    내가 알고있기로는 경쟁적인 요소가 있는 FPS게임은 주로 반동 패턴이 정해져있는 것으로 기억한다.

    마침 예전에 찾아두었던 에이펙스 레전드의 반동 패턴 데이터가 있어서 이 값을 적용해보면 비슷하게 될지도 궁금했고, 데이터는 패턴이 루프되도록 구성되어있는데 루프되는 반동 패턴은 어떤 느낌인지 궁금하기도 해서 정해진 반동 패턴을 만드는 방향으로 정했다.

     

     

     


     

     

     

    방식 연구

    언리얼 엔진에서 카메라를 흔들려고 한다면 카메라 쉐이크를 제일 먼저 떠올릴 수 있다.

    카메라 쉐이크 패턴을 웨이브 오실레이터 카메라 셰이크 패턴으로 하면 사인파를 따라서 화면을 흔들게 할 수 있다.

    초기 오프셋을 Zero로 지정해서 처음부터 재생되게 하고, 사인파 한 파장의 반만큼 재생되게 하면 하나의 반동처럼 흔들리는 카메라 쉐이크를 만들 수 있다.

     

    하지만 이전 글에서 다뤘듯이 이 프로젝트에서 구현한 방식으로는 카메라 쉐이크가 크로스헤어에 영향을 주지 않는다.

    https://mstone8370.tistory.com/36

     

    [UE5 | FPS] 1인칭 카메라의 애니메이션을 위한 구조 설계

    1인칭 게임인 FPS는 몰입감을 위해서 특정 행동을 하는 애니메이션에는 카메라 애니메이션이 포함되어있다.카메라 애니메이션 덕분에 자신이 조종하는 캐릭터가 어떠한 행동을 하는 동안 머리

    mstone8370.tistory.com

    따라서 카메라 쉐이크를 적용하면 아래처럼 된다.

     

    카메라 쉐이크 적용 결과

    카메라 쉐이크는 크로스헤어에 영향을 주지 않아서 반동으로 사용할 수 없다.

    하지만 흔들림이 부드럽게 이어지고, 카메라의 원래 위치로 돌아오게 설정할 수도 있어서 카메라 쉐이크의 방식을 응용하면 괜찮을듯하다.

    그리고 이렇게 카메라와 컨트롤이 구분된 방식이 오히려 더 좋은 효과를 보여줄것으로 예상된다.

     


     

    크로스헤어는 캐릭터의 컨트롤 로테이션 방향을 가리키고, 컨트롤 로테이션은 플레이어의 입력에 따라 조작된다.

    따라서 크로스헤어를 조작하려면 플레이어의 입력이 적용되는 방식을 사용하면 된다.

    컨트롤 로테이션은 플레이어 컨트롤러의 AddYawInputAddPitchInput함수로 조작할 수 있으니, 이 함수를 이용해서 반동값을 넣어주면 된다.

     

    예전에 이런 방식으로 반동을 주었던 프로젝트가 있었는데 결과물이 만족스럽지 않았다.

     

    예전에 제작했던 결과물

    반동값이 크게 주어지면 카메라 각도가 순간이동하듯이 튀어올라서 자연스럽지도 않고 보기에도 너무 불편하다.

    원래의 각도로 돌아올때 보간한것처럼, 반동이 주어질 때에도 각도를 보간하면 더 자연스러울 것이다.

    그러기 위해서는 타이밍을 포함해서 반동이 증가할때와 감소할때의 보간 속도를 다르게 하는 등의 고려해야할 부분이 많고, 위에서 언급했던 총기마다 다른 반동을 줘야하는 경우에는 추가적으로 복잡한 문제가 얽히게 된다.

     


     

    문제를 복잡하게 다루기보단 간단하게 해결하고싶어서 카메라 쉐이크를 응용해보기로 했다.

    일단 카메라 쉐이크는 카메라에만 영향을 주므로 카메라 쉐이크를 이용할수는 없고, 작동 원리를 따라하는 클래스를 새로 생성하기로 했다.

    클래스의 이름은 컨트롤 로테이션을 쉐이크 한다고 해서 ControlShake로 정했다.

     

     

     


     

     

     

    구현

    먼저 카메라 쉐이크가 어떻게 작동하는지 간단하게 알아봤다.

     

    카메라 쉐이크는 하나의 인스턴스만 작동하게 할 수도 있고, 한 클래스의 여러개의 인스턴스가 동시에 작동할 수도 있다.

    각 인스턴스마다 각자의 정보를 가지고 시간마다 업데이트된다.

    카메라 쉐이크의 인스턴스는 UCameraModifier_CameraShake라는 카메라 모디파이어에서 관리한다.

    UCameraModifier_CameraShake에는 ExpiredPooledShakesMap 라는 맵이 있는데 이 맵은 작동이 끝난 카메라 쉐이크 인스턴스를 클래스 별로 구분해서 저장해둔다.

    그리고 카메라 쉐이크가 추가될때 새로운 인스턴스를 생성하기 전에 이 맵에 동일한 클래스의 인스턴스가 있는지 살펴보고 있는 경우에는 이 인스턴스를 재활용 한다.

     


     

    이런 방법을 참고해서 ControlShake는 다음과 같이 구현하기로 했다.

     

    1. ControlShake도 쉐이크를 인스턴스로 관리하고, 작동이 끝난 인스턴스를 재활용한다.

    대신에 각 인스턴스는 간소화 해서 클래스별로 구분된 맵에 저장하지 않고 배열에 저장하며, 주어진 정보를 통해 멤버 변수를 초기화해서 재사용한다.

    테스트 결과 ExpiredPool의 크기는 6개정도면 충분한듯 하다.

    참고로 카메라 쉐이크는 클래스별로 5개의 인스턴스를 보관한다.

     

    2. 쉐이크 패턴은 CurveVector를 이용해서 원하는대로 설정할 수 있게 했다.

    이 방법으로 총기마다 다른 형태의 반동을 구현하는것도 가능해진다.

    Pitch 커브과 Yaw 커브를 따로 설정하지만 하나의 에셋으로 관리하기 적합한 CurveVector를 사용한다.

     

    3. ControlShake 인스턴스는 캐릭터가 가지게될 ControlShakeManager 컴포넌트에서 관리한다.

    ControlShakeManager는 매 틱마다 활성화되어있는 ControlShake 인스턴스에 DeltaTime을 제공하고, ControlShake 인스턴스는 누적된 시간을 계산해서 시간에 맞는 벡터값을 리턴해준다.

    커브의 끝에 도달한 ControlShake 인스턴스는 ExpiredPool 배열로 옮겨서 따로 관리한다.

     

    4. 현재 상황에 맞는 반동의 방향을 정해서 새로 활성화되는 ControlShake에게 제공한다.

    각각의 인스턴스들이 각자의 방향대로 벡터의 값을 리턴하게 되고, 한 틱동안 계산된 값을 모두 더해서 적용한다.

     

    여기에서 현재 상황에 맞는 반동의 방향은 정해진 반동 패턴을 의미한다.

     

    방식은 간단하지만 코드는 길어져서 깃허브로 대체한다.

    https://github.com/Mstone8370/NL_GAS

     

    GitHub - Mstone8370/NL_GAS

    Contribute to Mstone8370/NL_GAS development by creating an account on GitHub.

    github.com

    ControlShakeManager는 Components 폴더에, ControlShake는 Objects 폴더에 있다.

     


     

    이제 반동 패턴에 대한 정보가 추가적으로 필요하고, 관련된 다른 정보들도 같이 관리하기 위해서 데이터 에셋을 만들어서 각 무기별로 정보를 다루기로 했다.

     

    이렇게 만들어진 데이터 에셋에는 다음과 같은 정보가 필요하다.

     

    1. 패턴 정보.

    이 정보도 CurveVector에 저장하면 된다.

     

    2. 이 패턴에 맞는 하나의 반동 CurveVector.

    이 CurveVector를 ControlShake에 전달하고, ControlShake는 이 커브를 통해 결과값을 구하게 된다.

     

    3. 패턴이 초기화되는데 걸리는 시간.

    이 시간 내에 사격하면 패턴이 계속 이어지고, 시간이 지나면 처음부터 다시 시작된다.

     

    4. 하나의 반동이 지속될 시간.

    CurveVector를 수정하지 않고, float값만 수정해서 재생 속도를 조절하기 위한 정보다.

     

    5. 반동 루프 정보

    반동 패턴의 끝까지 간 경우 어느 구간을 루프해서 반복할지를 나타내는 정보다.

     

    이런 정보들을 구조체로 묶어서 무기별로 맵에 저장한다.

     

    반동 패턴 정보 예시
    Single Recoil 커브 예시
    반동 패턴 예시. R: Pitch, G: Yaw, B: Roll

    위 패턴 커브에서 Roll 정보도 같이 들어있는데 이 정보는 컨트롤 로테이션에 영향을 주지 않지만 애니메이션에 사용된다.

    플레이어의 팔 애니메이션에서 반동을 더 표현하기 위해 본의 위치를 조작하는데 이때 ControlShake의 값을 사용한다.

     

    그리고 패턴의 형태를 보면 이 방법으로 반동을 관리하는 방법의 단점을 미리 알 수도 있다.

     

     

     


     

     

     

    결과

    적용 결과는 이렇다.

    https://youtu.be/PyJdanNyS-I?si=5_OgaI5GQgmGevsQ

     

    패턴을 잘 따라서 의도대로 작동하고 반동이 루프되는 것도 확인할 수 있다.

     

    그리고 총기마다 다른 반동 커브를 설정하면 아래처럼 다른 느낌을 줄수도 있다.

     

     

    아래와 같은 효과도 가능하다.

     

     

     


     

    사용 범위 확장

    ControlShake는 총기 반동뿐만 아니라 플레이어의 에임을 흔들어야하는 모든 상황에 사용할 수 있다.

     

    예시로는 숨쉬는 것을 표현하기 위한 에임 흔들림이 있다.

    주로 높은 배율의 조준경을 통해 보고있는 경우에 체감할 수 있다.

    이런 효과는 ControlShake를 루프되게 하면 표현할 수 있다.

     

    2배속

    ControlShakeManager에서 루프되는 ControlShake를 따로 관리하는 방법을 사용했다.

     

    추가로 적에게 피해를 입으면 에임이 흔들리는 에임 펀치에도 사용 가능하다.

     


     

    단점

    위에서 잠깐 언급했듯이 이 방법의 단점이 있다.

    하나의 ControlShake에는 반동이 증가했다가 감소하는 커브까지 포함되어야한다.

    따라서 증가하는 반동과 감소중인 반동이 같이 존재한다면 값이 상쇄되는 문제가 있다.

    이런 문제를 해결하기 위해 반동 패턴 커브의 Pitch를 계속 증가하게 만들었다.

     

    반동 패턴 Pitch 커브

    그래도 반동 패턴이 정해져있는 경우에는 상황에 맞게 패턴을 만들면 되니 큰 문제가 되지 않는다.

    하지만 패턴이 정해져있지 않다면 이 문제를 해결하거나, 위처럼 누적 반동을 계산하는 방식을 고려해봐야한다.

     

    그리고 에이펙스 레전드의 총기 반동 패턴 데이터를 보니 Pitch값이 계속 증가하는 추세를 보이지 않아서 이 값을 그대로 넣어봐도 비슷한 결과를 만들지 못했다.

    해당 값들을 누적되게 만들어봐도 비슷한 패턴을 만들지 못해서 에이펙스 레전드에서는 사격 중간중간에 반동이 감소하는 타이밍이 존재한다는걸 추측할 수 있다.

     

    개선 방법

    사격으로 인한 반동을 다룰 때에는 ControlShake의 커브를 반동이 증가하는 방향으로만 설정하고, 일정 시간이 지난 후에는 원점으로 interp하는 방식을 생각해 볼 수 있다.

    1초에 14발을 발사할 수 있다고 가정해보면 약 0.07초 안에 이런 과정이 진행되어야하므로 시간 설정을 미세하게 조절해야한다.

    그리고 총기마다 Pitch와 Yaw의 interp 속도, 반동이 감소하는 타이밍을 다르게 설정하면 다양한 느낌의 반동을 구현할 수 있을듯 하다.

     

     

     

Designed by Tistory.