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

[슈팅 알고리즘] Chapter 2. 탄환 (유도탄)

hye3193 2024. 4. 19. 17:18

2.12 유도탄

유도탄은 메인 캐릭터를 향해 직진하는 탄환과 달리, 발사된 후에도 방향을 바꿔가며 메인 캐릭터를 쫓아오는 탄환이다

void MoveSimpleHomingBullet(
    float& x, float& y, // 탄환의 좌표
    float mx, float my, // 메인 캐릭터의 좌표
    float speed // 탄환의 속도
) {
    // 목표까지의 거리 d 구하기
    float d = sqrt((mx - x) * (mx - x) + (my - y) * (my - y));
    
    // 속도가 일정한 값(speed)가 되도록
    float vx, vy; // 탄환의 속도 벡터
    if (d) {
        vx = (mx - x) / d * speed;
        vy = (my - y) / d * speed;
    } else {
        // 목표까지의 거리가 0일 경우 속도 벡터를 화면 아래쪽을 향하도록
        vx = 0;
        vy = speed;
    }
    
    x += vx;
    y += vy;
}

유도탄의 이동은 위와 같이 처리하면 된다. 그러나 위와 같은 방법으로 유도탄을 만들게 되면, 메인 캐릭터가 유도탄을 피할 수 없게 되므로 탄환의 선회 각도에 제한(최대값)을 걸어 정해진 각도 이상으로 선회하지 못하게 만들면 메인 캐릭터가 유도탄을 따돌릴 수 있게 된다

이때 탄환과 메인 캐릭터 사이의 각도가 선회각도 상한치보다 큰지 아닌지를 판별해야 하는데, 이는 아래와 같이 할 수 있다

원래의 속도 벡터가 v0, 메인 캐릭터의 속도 벡터가 v1, 선회각도 상한치에 해당하는 속도 벡터가 v2라고 가정하였을 때

v1이 v2의 범위 밖에 있을 때에는 v0과 v1의 내적이 v0과 v2의 내적보다 작고, v1이 v2의 범위 내에 있을 때에는 v0과 v1의 내적이 v0과 v2의 내적보다 크다

따라서

v0 · v1 > v0 · v2 조건을 만족할 때 탄환과 메인 캐릭터 사이의 각도가 선회각도 상한치보다 작으므로 해당 각도로 선회시키면 되고, 반대의 경우는 선회각도 상한치를 초과하므로 상한치만큼만 선회시키면 된다

void MoveHomeBullet() {
    // 탄환의 원래 속도 벡터를 저장
    float vx0 = vx, vy0 = vy;
    
    // 탄환에서 메인캐릭터까지의 속도 벡터(vx1, vy1) 구하기
    
    // 상한 각도(theta)에 해당하는 속도 벡터(vx2, vy2) 구하기(시계 방향)
    float rad = M_PI / 180 * theta;
    float vx2 = cos(rad) * vx0 - sin(rad) * vy0;
    float vy2 = sin(rad) * vx0 + cos(rad) * vy0;
    
    // v1만큼 선회할지 v2만큼 선회할지 판단
    if (vx0 * vx1 + vy0 * vy1 > vx0 * vx2 + vy0 * vy2) {
        vx = vx1;
        vy = vy1;
    } else {
        // 상한 각도에 해당하는 속도 벡터(vx3, vy3) 구하기(반시계 방향)
        float vx3 = cos(rad) * vx0 - sin(rad) * vy0;
        float vy3 = sin(rad) * vx0 + cos(rad) * vy0;
        
        // 탄환에서 메인 캐릭터까지 상대 위치 벡터 구하기
        float px = mx - x, py = my - y;
        
        // 선회 방향(시계/반시계) 정하기
        if (px * vx2 + py * vy2 > px * vx3 + py * vy3) {
            // 시계 방향
            vx = vx2;
            vy = vy2;
        } else {
            // 반시계 방향
            vx = vx3;
            vy = vy3;
        }
    }
    
    x += vx;
    y += vy;
}

시계방향/반시계 방향 선회를 정하는 것도 위와 비슷하게

시계/반시계 방향 중 v1과 더 가까운 방향의 상한치 벡터와의 내적값이 크기 때문에 이를 기준으로 판별해준다