ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3인칭 캐릭터 만들어보기 #2 카메라 회전및 장애물감지
    Unity유니티/캐릭터 2022. 6. 16. 01:28

    내가 어릴떄 했던 3인칭 게임들을 보면

    유저의 모니터화면이 되는 카메라가 주로 2가지 특징이 있었다

    1.캐릭터를 따라다니는 카메라

    2.캐릭터와 카메라 사이 장애물이 있을떄

     

    먼저 캐릭터를 따라다니려면 어떻게 할까 고민해보았다

    이벤트 함수의 실행 순서 - Unity 매뉴얼 (unity3d.com)

    이벤트함수 실행순서를 보면

    업데이트함수 다음 LateUpdate란것이  오는데

    유투브나 강의 영상들을보면 카메라이동등을 여기서 처리하는걸 봤다

    업데이트함수가 끝나고 실행된다고 보면되는데

    캐릭터를 따라다니는 카메라의 경우

    캐릭터의 이동이 업데이트 함수에서 계산후 처리가 되면
    픽스드업데이트에서 그걸 기반으로 처리하는 개념인듯하다

    1.캐릭터를 따라다니는 카메라

    먼저 멤버변수들

    참조할 캐릭터 _chr

     _rotSpeed, _camSpeed, _camDistance

    회전속도, 따라갈 카메라속도, 캐릭터로부터의 카메라거리

    그리고 벡터3 pos와 rot은 카메라가 도달할 위치와 회전을 의미해준다

    굳이 저런 변수로 뺴둔 이유가

    현재 캐릭터는 따로 스크립트에서 이동회전을 업데이트문에서 하고 있다 (게시글의 캡처본들은 모두 카메라스크립트)

    그리고 위 캡처된 업데이트문은 캐릭터를 따라 이동할 위치,회전값을 계산한것이다

    이렇게 한 이유가 모든걸 LateUpdate에서 처리할때 지터링, 중간에 끊기는 채로 보이거나 부자연스러운 움직임이보여서였다, 사실 이 문제를 해결하려 구글링을 겁나게 했지만

    지터링 해결에 관해 사람들이 정말 다양한 시도를 했었고 경우가 엄청 많았다

    대부분 리지드바디를 쓰는 캐릭터컨트롤 관련이라 나와 같은 경우의 글을 못봐서  애먹었는데

    결국 업데이트문다음에 레이트업데이트문이 나오는 순서를 깨닫고 위처럼 작성했다

    이동할 위치,회전량을 업데이트문에서 계산하고

    실제 이동을 LateUpdate에서 하는 것이다

    위에 업데이트문에 계산한값들을 그대로 카메라에게 적용하였다

    Lerp와 Slerp를 사용한건 부드럽게 따라가는 느낌을 내려고 사용하였다

    pos.y+=1f;는 카메라 위치를 1더 올리는게 보기 이뻐서 그랬다

    참고로 쿼터니언에서 Lerp Slerp두개가 있는데

    난 차이를 못 느꼈지만

    Lerp가 Slerp보다 연산이 빠르지만

    Slerp가 회전을 더 이쁘게 한다고한다(회전할 폭이 클수록)

    Lerp는 선형보간 Slerp는 구형보간개념으로 알고있는데
    https://youtu.be/uNHIPVOnt-Y   아래 영상에서 파란색이 Lerp 하얀색이 Slerp이다

    또한 레이트업데이트도 업데이트문과같이 델타타임으로 프레임차이를 맞추는듯하다

    키보드 WS로 앞뒤 이동
    키보드 AD로 이동없이 회전만했을떄
    이제 이동 회전 모두 섞어서 막 움직여보기

    _camSpeed 변수 값을 22이상으로 올리면
    회전을해도 캐릭터가 카메라 중앙에 빠르게 잘잡힌다



    2.캐릭터와 카메라 사이 장애물이 있을떄

     

    캐릭터를 바라보는 카메라로 모니터에 캐릭터가 그려진다

    그런데 캐릭터와 카메라 사이 장애물이있으면

    캐릭터가 안보이게 되서 안된다

    어떻게 처리할까
    레이캐스팅이 떠올랐다

    구조가 살짝 바뀌었다

    LateUpdate에 카메라를 위로 살짝 올린부분을 업데이트문에서 그냥 처리하였고

    기존에 _camDistance 즉 카메라와 캐릭터사이의 거리 변수를

    _maxDistance와 camDistance로 쪼개었다

    _maxDistance는 캐릭터와 카메라사이 최대거리를 인스펙터상에서 조절하는 용도이고

    camDistance는 스크립트내에서 변하는 값을 그떄마다 캐싱해서 사용하는 용도로 바꾸었다

    끝으로 레이를 사용할 avoidObstacle함수를 만들어 업데이트문에 넣었다

     

    월드공간에 배치된 오브젝트들중 레이어가 Wall인 오브젝트를 탐지 및 충돌했는지로 판정할생각이었다내부에 if , else가 있는데if를 먼저 설명하면if문에 들어왔다는것은 레이에 wall레이어를 지닌 오브젝트가 충돌했다는것이다(카메라와 캐릭터 사이 장애물이있단것이다)

    Vector3 dist = hit.point - _chr.transform.position;

    hit는 충돌체가 될텐데, dist는 캐릭터에서 벽으로 향하는 벡터가 된다캐릭터에서 벽으로 향하는 벡터를 알면 , magnitude함수로 그 길이또한 알수있다(float)
    camDistance = (dist.magnitude * _obstacleDist);
    그래서 캐릭터에서 벌어질 카메라의 거리값이 이 값으로 나오는데

    _obstacleDist는 1.0f값으로 하면 된다

    끝에가서 이야기할것인데 이 스크립트 내용이 2프로 부족한점이 하나 있다

    넘어가서

     

    else부분을 보면카메라와 캐릭터사이 장애물이 없다는 이야기가 된다이 else문에 들어오는 경우는 2가지가 될텐데

    1.계속 장애물이 사이에 없던경우와

    2. if문 내용실행을 한후 장애물이 없어서 else문에 올떄다

     

    camDistance = Mathf.Clamp(camDistance + (_camSpeed * Time.deltaTime), 1f, _maxDistance); 

    카메라 거리값을 Clamp함수로 _camSpeed * Time.deltaTime 증가시킨다

    그러면 인스펙터창에서 설정한 _maxDistnace ,최대값으로 커져갈것이다

    ( 만약에 커지다가, 또는 캐릭터가 회전을 하다가

     다시 장애물이 카메라와캐릭터 사이에 있으면? => if문으로 카메라 위치가 조정될것이다)

    업데이트문

    그래서 업데이트문 첫줄이 저 장애물 탐지이고

    장애물이 있는지 없는지에 따라

    변수 camDistance값이 정해지고

    그 값으로 pos의 최종값이 Update문에서 완성되며

    이후 실제로 카메라 위치이동 회전을 LateUpdate에서 완성을 시키는것이다

     

    회전을 시켜보자.. 두근두근

     

    오오 나쁘지 않은 결과물이다
    만족한다
    그러나 한가지 아쉬운점이 있는데
    장애물이 사이에있을때
    캐릭터와 장애물사이의 거리를 구하고
    그 거리값+캐릭터위치값으로
    카메라 위치를 재위치시키고있다

    바로 요놈인데

    문제가 저 _obstacleDist값을

    1~0.95사이에 두면 내가 그렸던 모습대로 나오는데
    그 이하로 두면 중간에 덜덜 떨리는 그림이 가끔나와서
    좀 아쉽다
    그러나 0.95로 둬도 뭐... 원한 결과는 나왔으니 만족해야되는걸까
    하 짚이는 부분이 있긴한데 머리가 아프다
    오늘은 여기까지 해야겠다
    다음번엔 프리룩기능을 붙여서 3인칭을 마무리 해볼까한다

Designed by Tistory.