튜토리얼 1. 함선 움직여 보기 (2) - GameEgg/STARPOO-II GitHub Wiki

# 2. 특정 위치로 움직여 보기

자 그럼 빠르게 두번째 목표인 특정 위치로 함선을 움직여 보려합니다.

# 목차

  1. 극좌표란?
  2. 함선과 원점 (0, 0)사이의 거리 및 각도 구하기
  3. 원점으로 이동시켜보기
  4. 원점이 아닌 특정 위치로 이동시켜보기
  5. 원점이 아닌 특정 위치로 이동시켜보기 with 보정

# 1. 극좌표란?

특정 위치로 움직이기전에, 극좌표를 알고 넘어가야 합니다.

극좌표(polar coordinate)란
polar coordinate

STARPOO의 좌표계는 극좌표(polar coordinate) 입니다. 기준점을 중심으로 한 점을 x,y가 아닌, 거리(r)와 각도(θ)로 표현하는 좌표계입니다. STARPOO에서는 각도로 degree 단위를 사용하고 있습니다. 위 그림과 같이, 원점을 중심으로 오른쪽 정방향이 0도이며 각도가 증가하면 반시계방향으로 회전하게 됩니다.

# 2. 함선과 원점 (0, 0)사이의 거리 및 각도 구하기

자 그럼 가장 쉽게 (0, 0)인 지점인 원점으로 함선을 이동시켜 보도록 하죠.

먼저 현재 함선의 위치를 가져와야겠죠?

STARPOO API 에서 제공하는 polarFrom({x, y, rot}, {x, y}) 함수를 사용하면 쉽게 함선과 target 사이의 거리와 각도를 구할 수 있습니다.

polarFrom({x, y, rot}, {x, y}) 함수의 반환값인 rot과 r은

  • r : 첫번째 인자와 두번째 인자간의 거리를 의미하며,
  • rot : 첫번째 인자가 바라보고 있는 방향을 기준으로 두번째 인자와의 각도를 의미합니다.

tutorial-3-move-specific point

지금 우리는 원점과 함선사이의 거리와 각도를 구할 것이기 때문에 첫번째 인자에 myShips[0]을, 두번째 인자에 {x: 0, y: 0}를 넣어주면 됩니다. 따라서 현재 함선의 위치에서 원점까지 거리와 각도를 구하기 위해 아래와 같이 코드를 작성한 뒤 log를 살펴 봅시다.

function update()
{
    var pos = polarFrom(myShips[0], {x: 0, y: 0});

    // 로그 찍어보기
    log("rot : " + pos.rot);
    log("r : " + pos.r);
}

# 3. 원점으로 이동시켜보기

자, 이제 우리는 현재 함선과 원점 사이의 거리와 각도를 알았습니다. 이 정보를 토대로 함선을 원하는 위치인 원점으로 이동시키려합니다. 이를 위해서 함선이 수행해야하는 일은 두가지로 볼 수 있습니다.

  1. 함선의 뱃머리를 목표지점으로 돌리기
  2. 함선이 목표지점에 도착하지 않았다면 전진

위의 두가지 수행을 토대로 코드를 작성하여 봅시다.

var b_once = true;

function update() 
{
    var ship = myShips[0];
    var destination = {x: 0, y: 0};
    var pos = polarFrom(ship, destination);

    // 함선이 목표지점에 도착하지 않았다면
    if (pos.r > 0) 
    {
        myShips[0].setRotSpeed(pos.rot);    // 1. 함선의 뱃머리를 목표지점으로 돌리기
        myShips[0].setSpeed(shipMaxSpeed);  // 2. 전진
    }
    
    // 목표 지점 도착
    else 
    {
        // 정지
        myShips[0].setRotSpeed(0);
        myShips[0].setSpeed(0);

        if (b_once) {
            b_once = false;
            log("x : " + ship.x);
            log("y : " + ship.y);
        }
    }
}

원점으로 가 멈추는 모습을 볼 수 있습니다.

# 4. 원점이 아닌 특정 위치로 이동시켜보기

그렇다면 위에서 작성한 script에 목적지만 (200, 700)으로 변경한 뒤 스크립트를 돌려 봅시다.

var b_once = true;

function update() 
{
    var ship = myShips[0];
    var destination = {x: 200, y: 700};
    var pos = polarFrom(ship, destination);

    ...
}

정확한 점에 멈추지 않고 빙글빙글도는 모습을 보게됩니다.. 왜 이런걸까요?

이러한 현상의 원인을 아래와 같이 3가지 정도로 추려볼 수 있습니다.

  1. 목표 지점에 거의 도착할때 감속하지 않아서 지나쳐가게됨
  2. 마찬가지로 목표 각도에 거의 도착할때 각도 회전을 감속하지 않아서 지나쳐가게됨
  3. 거리(r)의 오차 범위 고려 안함

무슨말인지 모르겠다고요? 위의 원인을 하나씩 뜯어보며 이해해 봅시다. ![네?](https://i.ytimg.com/vi/Fs54pXJ5pc0/hqdefault.jpg)

위에서 우리는 함선의 속도를 shipMaxSpeed로 설정하였습니다. 즉, 함선은 1초당 shipMaxSpeed 만큼의 거리를 이동하게 되며, 이는 1 Frame 에 (shipMaxSpeed / 초당 프레임 수) 만큼의 거리를 이동하게 됩니다.

(여기서, STARPOO 는 초당 30 Frame의 고정된 수치로 게임을 출력한다 가정해봅시다. 즉 dt = 1 / 30), shipMaxSpeed 값이 300이므로 1 Frame 에 최대 10만큼 움직이게 됩니다. 그렇다면, 만약 함선이 이동하고자 하는 목표의 거리가 701이라면 어떻게 될까요. 함선은 70 Frame이 지나고 목표 지점과 거리가 1이되지만, 우리가 원하던 r 이 0이 되지 않았으므로 71번째 Frame에는 목표지점을 지나쳐 가게 됩니다. 즉, 이러한 고질적인 문제를 가지고 있어서 1 Frame 당 이동거리인 10의 배수만큼의 거리가 아니라면 정확히 멈추긴 힘듬을 알 수 있습니다.

현재 함선이 최대 속도로 이동하고 있다는 가정하고, 그렇다면 우리는 1 Frame 동안 움직일 수 있는 최대 이동거리를 목표 지점과의 거리의 오차범위로 설정하여 이 문제를 해결할 수 있습니다.

즉, if (pos.r > 10) 함선과 목표 지점사이의 거리가 10 이내라면 이동을 멈추는 방법이죠. 아래는 적용한 모습입니다.
[주의, STARPOO가 고정 30 Frame이라는 가정하에 작성한 것이며, 실제 dt 값은 다소 다릅니다. Script API 문서 에서 제공하는 dt 값을 참고하세요]

function update() {
    var ship = myShips[0];
    var destination = {x: 0, y: 0};
    var pos = polarFrom(ship, destination);

    // 함선을 목표지점에 도착하지 않았다면
    if (pos.r > 10) {
        myShips[0].setRotSpeed(pos.rot);    // 1. 함선의 뱃머리를 목표지점으로 돌리기
        myShips[0].setSpeed(shipMaxSpeed);  // 2. 전진
    }

    ...
}

하지만, 뭔가 찝찝하지 않나요?
함선은 우리가 이동하길 원했던 지점과 거리가 최대 10까지 차이가 날 수 있습니다. (물론 기적적으로 딱 원하는 지점에 떨어질수도 있죠)
이러한 오차는 상대 함선을 조준하여 맞춰야하는 전략가 입장에서 달가운 소식은 아닙니다.



그렇기에 우리는 한가지 테크닉을 덧붙여 오차범위를 거의 0에 가깝게 최소화 하고자 합니다.
바로 **감속**입니다.

감속

우리는 최대 속도로 이동을 하다가, 목표지점에 거의 도착했을때, 다음 Frame에 최대속도로 이동을 하게되면 목표지점을 지나쳐가게 되는 경우를 생각해 봅시다. 이때 속도를 줄여 다음 Frame동안 현재 목표지점과 남은 거리만큼만 이동을 하면 완.벽.합.니.다. 멋지죠?


Q. 그렇다면 이론상 오차범위가 0이게 움직일 수 있는데 왜 0에 가깝게 최소화 한다고 하였을까요?

A. STARPOO 내부적으로 나눗셈과 곱셈이 처리되면서 정확한 정수값으로 떨어지지 못하는 경우가 생기게 되며 이로 인해서 아주 미세한 오차가 발생하게 됩니다. (거의 발생하지 않으나 아주 간혹 발생하고 오차 범위또한 미세하게 작은 값이므로 주의하세요 @_@)

회전 속도 또한, 이동속도와 마찬가지 이유로 감속이 필요합니다.

참쉽죠?

Quest! 자 그러면 직접 감속을 적용한 스크립트를 작성해 보도록 합시다.

(아래에 스크립트는 감속을 적용한 테크닉중 하나입니다. 본인이 작성한 스크립트와 꼭 동일할 필요는 없습니다. 또한 아래의 스크립트가 정답이라 단정지을수도 없다는 점 유념하세요! 가이드는 가이드일 뿐입니다 :) 더 멋진 해결 방법이 있을 수도??)













아래를 보기 전에 꼭 한번 작성해 보세요~













# 5. 원점이 아닌 특정 위치로 이동시켜보기 with 보정

위에서 설명한 감속에 대한 보정을 적용한 가이드 스크립트

var bonce = true;

function update() 
{
    var ship = myShips[0];
    var dest = {x: 200, y: 700};
    var pos = polarFrom(ship, dest);

    if (pos.r > 0.1) 
    {
        myShips[0].setRotSpeed(pos.rot / dt);
        myShips[0].setSpeed(pos.r / dt);
    }
    else 
    {
        myShips[0].setRotSpeed(0);
        myShips[0].setSpeed(0);

        if (bonce) {
            bonce = false;
            log("x : " + ship.x);
            log("y : " + ship.y);
        }
    }
}

코드에 대해 간단히 설명하자면, setSpeed 함수와 setRotSpeed 함수는 각각 인자로 들어온 값의 최대값이 존재합니다. 즉 setSpeed 함수의 인자로 10000이라는 값을 전달하여도 shipMaxSpeed 로 함선은 이동속도가 정해집니다. 마찬가지로 setRotSpeed 함수도 인자로 최대 360도까지 제한되어있습니다. 이점을 이용하여 만약 (pos.r / dt) 값이 shipMaxSpeed 보다 크거나 같다면 함선의 이동속도는 shipMaxSpeed 로 설정될 것이고, 만약 (pos.r / dt) 값이 shipMaxSpeed 보다 작다면 함선의 이동속도는 (pos.r / dt)로 설정되게 됩니다.

# 다음

  1. 직선으로 움직여보기
  2. 특정 위치로 움직여 보기
  3. 특정 위치 주변을 회전하게 만들기
⚠️ **GitHub.com Fallback** ⚠️