강의, 책/[DirectX, C++] 게임 매니악스 알고리즘 시리즈

[슈팅 알고리즘] Chapter 2. 탄환 (방향탄)

hye3193 2024. 4. 19. 16:36

2.4 방향탄

조준탄과 달리 특정 캐릭터를 향해 날아가는 게 아니라 임의의 방향으로 날아가는 탄환

void InitDirectedBullet() {
    // M_PI: 원주율
    vx = cos(M_PI / 180 * theta) * speed;
    vy = sin(M_PI / 180 * theta) * speed;
}

위 공식을 이용하여 각도를 가지고 탄환의 속도 벡터(방향)을 구할 수 있다

탄환을 발사할 때는 위에서 구한 속도 벡터를 좌표에 더해주면 된다

 

2.5 테이블을 이용한 방향탄

2.4에서는 실수형 데이터를 사용하였는데, 방향 벡터 테이블을 미리 만들어 발사하는 방법을 사용할 수도 있다

// 3의 속도로 16방향으로 향하는 방향탄
void InitDirectedBullet16_3(
    float& vx, float& vy,
    float theta // 발사 속도
) {
    static int v3[][2] = {
        {3, 0}, {3, 1}, {2, 2}, {1, 3},
        {0, 3}, {-1, 3}, {-2, 2}, {-3, 1},
        {0, 3}, {-3, -1}, {-2, -2}, {-1, -3},
        {0, -3}, {1, -3}, {2, -2}, {3, -1}
    };
    
    // 각도(0~360)를 16방향(0~15)으로 변환
    int dir = (int)(theta * 16 / 360);
    
    // 탄환의 속도 벡터 구하기
    vx = v3[dir][0];
    vy = v3[dir][1];
}

단순하게 테이블을 이용할 수도 있으나, 벡터 정규화 작업을 거치지 않으면 방향에 따라 탄환의 속도가 미묘하게 달라지는 현상이 나타난다

 

2.6 DDA를 사용한 방향탄

2.2에서 DDA를 이용해 조준탄을 구현한 것을 응용하여, 가상의 플레이어를 향해 조준탄을 발사하면 방향탄을 같은 방식으로 구현할 수 있다.

void InitAimingBulletDDA(
    int theta, // 발사 각도
) {
    // 가상의 캐릭터 멀리 배치하기
    int mx = cos(M_PI / 180 * theta) * 1000;
    int my = sin(M_PI / 180 * theta) * 1000;
    
    // 이후는 DDA를 사용한 조준탄 처리와 동일
}

* 이때 배치하는 플레이어의 위치는 시작점에서 어느정도 멀리 떨어진 위치에 배치해야 방향의 정확도가 올라간다.

* 삼각함수의 인자로는 기존의 theta값이 아닌 rad 값(파이 사용)이 들어가야 하기 때문에 위와 같은 변환 과정을 거치는 것

 

cos, sin 등의 함수를 사용하면 실수형을 사용하게 되므로, 미리 테이블을 만들어 정수형으로만 처리할 수도 있다

int mpos[360][2];

void MakeTable() {
    for (int i = 0; i <360; i++) {
        mpos[i][0] = cos(M_PI / 180 * i) * 1000;
        mpos[i][1] = sin(M_PI / 180 * i) * 1000;
    }
}

void InitDirectedBulletDDA2() {
    int dir = (theta % 360 + 360) % 360;
    int mx = mpos[dir][0];
    int my = mpos [dir][1];
    
    // 이후는 DDA를 사용한 처리와 동일
}

* dir를 구할 때 굳이 중간에 360을 더해준 다음 다시 % 연산을 하는 이유는 theta 값이 음수여도 최종값을 양수로 반환시키기 위함이다