top of page

Game Math] Portal System 카메라 상대적 동기화

  • 작성자 사진: 김영호
    김영호
  • 2022년 7월 27일
  • 2분 분량

최종 수정일: 2023년 4월 25일


원통 좌표계를 통해 포탈과 플레이어의 상대적 위치를 알아낸 것으로 반대쪽 포탈과 포탈 이미지를 그려줄 카메라의 위치를 계산하여 포탈들이 각 글로벌 상에서 어느 각도로 있더라도 동기화가 가능하도록 만들어 주었다.



  • 구현

동기화를 위한 값, 계산 순서

  1. 현재 포탈right Vector포탈->플레이어 Vector간의 사이각

  2. 타겟 포탈right Vector글로벌상right Vector(1, 0, 0)간의 각도 차이

  3. 1 + 2를 한것이 글로벌상에서 동기화된 카메라의 각도

  4. 각 포탈을 기준으로 플레이어가 전방이라면 카메라는 후방에 있어야함

  5. 현재 포탈과 타겟 포탈의 각도 차이


public void SyncCamToPlayer_Relative()
{                                                                                                           
    //1. 현재 포탈의 right Vector와 포탈->플레이어 Vector간의 사이각
    float angle_Cur_PortalToPlayer = Vector3.Angle(portal_Current.right, Vector_PortalToCamera());
    if (!Over180(portal_Current.right, Vector_PortalToCamera()))
         angle_Cur_PortalToPlayer += 180f; //포탈의 후방에 있음

    //2. 타겟 포탈의 right Vector와 글로벌상의 right Vector(1, 0, 0)간의 각도 차이
    float angle_TarPortal = Vector3.Angle(portal_Target.right, Vector3.right);
    angle_TarPortal = (float)((int)((angle_TarPortal + 0.005) * 100f) / 100f); //소수점 제거
    if (Over180(portal_Target.right, Vector3.right))
        angle_TarPortal = 360 - angle_TarPortal;
    
    //3. 1 + 2를 한것이 글로벌상에서 동기화된 카메라의 각도
    float angle_SyncCam = angle_Cur_PortalToPlayer + angle_TarPortal;

    //4. 각 포탈을 기준으로 플레이어가 전방이라면 카메라는 후방에 있어야함
    angle_SyncCam = ((angle_SyncCam + 180f) % 360f) * Mathf.Deg2Rad; //+180도, angle <= 360이므로 % 360, Radian으로 변환

    //현재 포탈 <-> 플레이어 간의 거리
    float dist = Vector3.Distance(portal_Current.position, cam_Player.parent.position);

    //카메라의 위치 동기화
    NewPosition(angle_SyncCam, dist);

    //5. 현재 포탈과 타겟 포탈의 각도 차이
    float angle_Portals = Vector3.Angle(portal_Current.right, portal_Target.right); 
    if (Over180(portal_Current.right, portal_Target.right))
        angle_Portals = 360 - angle_Portals;
    //카메라 회전 동기화
    SyncRotaion(angle_Portals);
}                                                                                


//포탈간 차이각으로 계산
void NewPosition(float offset_angle, float dist)
{
    //포탈과 플레이어간의 원통 좌표계를 직교 좌표계로 변환
    float x = Mathf.Cos(offset_angle);  float z = Mathf.Sin(offset_angle);

    //타겟 포탈을 기준으로 카메라가 있어야 할 위치(포탈과 카메라간의 offset)
    Vector3 calcPos = new Vector3(x, 0, z) * dist;
    
    //타겟 포탈의 위치 + 포탈과 카메라 간의 offset
    Vector3 newPos = portal_Target.position + calcPos;
    //Y축으로 내려다 본 상황에서 x, z는 위치가 동일하면 되지만 y(시야 높이)는 player와 동일해야 하기 때문에 y값을 덮어씌운다)
    newPos = new Vector3(newPos.x, cam_Player.position.y, newPos.z);
    
    //카메라의 위치 수정
    cam_Target.position = newPos;
}

  • void SyncRotation(...)

void SyncRotaion(float angle_Portals)
{
    //카메라는 플레이어와 반대방향을 봐야하므로 +180
    Quaternion portalRotationalDiff = Quaternion.AngleAxis(angle_Portals + 180f, Vector3.up);

    //플레이어 카메라의 정면에 방향을 곱해서 타겟 카메라의 Rotation을 정함
    Vector3 newCamDirection = portalRotationalDiff * cam_Player.forward;
    
    //타겟 카메라가 새로 만들어진 방향을 보게함
    cam_Target.rotation = Quaternion.LookRotation(newCamDirection, Vector3.up);
}

  • bool Over180

public bool Over180(Vector3 v1, Vector3 v2)
{
    //v1, v2의 외적 
    Vector3 cross = Vector3.Cross(v1, v2);
    if (cross.y < 0)
        return true;
    return false;
}

함수 bool Over180(...)은 Vector의 외적을 사용해서 전후방을 확인하기 위한 함수이다.

Vector의 각도(내적)는 어느 방향(시계 방향, 반시계 방향)이든 기준 Vector로부터 0~180의 값으로만 나온다.

그래서 포탈의 전후방(내적 시작 기준 Vector = 포탈의 right)을 구분하지 않으면 포탈의 차이각 계산에 오류가 발생하게 된다.

  • Facebook
  • Twitter
  • LinkedIn

©2021 by 김영호_포트폴리오. Proudly created with Wix.com

bottom of page