Làm thế nào để dẫn đầu một mục tiêu di chuyển từ một game bắn súng di chuyển


7

Tôi thấy câu hỏi này: Dự đoán vị trí của kẻ thù để có một đối tượng dẫn đầu mục tiêu . Tình hình của tôi là một chút khác nhau mặc dù.

Mục tiêu của tôi di chuyển, và người bắn di chuyển. Ngoài ra, vận tốc của người bắn được thêm vào vận tốc của viên đạn, tức là viên đạn được bắn trong khi trượt sang phải sẽ có vận tốc lớn hơn về phía bên phải.

Những gì tôi đang cố gắng làm là khiến kẻ thù có thể xác định nơi chúng cần bắn để đánh người chơi. Sử dụng giải pháp SO được liên kết, trừ khi người chơi và kẻ thù đứng yên, sự khác biệt vận tốc sẽ gây ra sai sót. Làm thế nào tôi có thể ngăn chặn điều đó?


Đây là giải pháp được trình bày từ câu trả lời tràn stack. Nó sôi sùng sục để giải một phương trình bậc hai có dạng:

a * sqr(x) + b * x + c == 0

Lưu ý rằng ý sqrtôi là vuông, trái ngược với căn bậc hai. Sử dụng các giá trị sau:

a := sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)
b := 2 * (target.velocityX * (target.startX - cannon.X)
      + target.velocityY * (target.startY - cannon.Y))
c := sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)

Bây giờ chúng ta có thể nhìn vào phân biệt đối xử để xác định xem chúng ta có một giải pháp khả thi hay không.

disc := sqr(b) - 4 * a * c

Nếu người phân biệt đối xử nhỏ hơn 0, hãy quên việc bắn trúng mục tiêu của bạn - đạn của bạn không bao giờ có thể đến đó kịp thời. Nếu không, hãy nhìn vào hai giải pháp ứng cử viên:

t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)

Lưu ý rằng nếu đĩa == 0 thì t1 và t2 bằng nhau.


3
bạn có thể vui lòng cố gắng ngưng tụ câu hỏi của bạn. Tôi biết bạn nghĩ rằng bạn cần phải đưa ra những lời giải thích thực sự chi tiết, nhưng thực sự tôi đã đi được nửa chặng đường và cảm thấy rằng tôi đang đọc một cuốn sách văn bản
vườn06

1
Bạn có thể vui lòng sử dụng một cái gì đó như sq()thay vì sqr()? Nó làm cho nó thực sự khó hiểu để đọc.
sam hocevar

@ gardenian06 Mã mà không có bất kỳ lời giải thích nào liên quan là ở cuối câu hỏi.
Azaral

@Sam Hocevar Tôi đã thay thế tất cả biến * của tôi bằng std :: pow (biến, 2).
Azaral

Nếu nó hoạt động khi bạn đứng yên và mục tiêu di chuyển, nhưng không phải khi cả hai bạn di chuyển, thì hãy trừ đi vận tốc của bạn khỏi mục tiêu để có được vận tốc tương đối và sử dụng nó.
George Duckett

Câu trả lời:


5

Được rồi, chúng ta hãy đặt sự tỉnh táo vào việc này. Tôi e rằng bạn không làm cho nó dễ dàng chút nào, mã của bạn không biên dịch, không nhất quán liên quan đến tên biến ( playerVelocityXtrở thành playerXvelocitysau một vài dòng? Cái gì xVelocity?) Và quá dài dòng. Về cơ bản là không thể gỡ lỗi vì sợ bạn bỏ công sức đáng kể vào nó.

Vì vậy, đây là những điều cần khắc phục:

Tốc độ đạn

Tốc độ đạn phải là 30, thời gian. Không cần phải tính toán bạn đang làm: sự thay đổi của khung tham chiếu chính xác là có để tránh sự phức tạp. Bạn chỉ thêm vận tốc của kẻ thù sau khi bạn tìm thấy giải pháp, khi bạn quay lại khung tham chiếu chính.

Giải pháp hợp lệ

Bạn không kiểm tra rằng timegiải pháp là tích cực.

Vô số lỗi mã hóa

Bạn đang thử nghiệm time1time2luôn luôn sử dụng time1trong kết quả.

Bạn làm playerXvelocity - yVelocitymà không nhất quán.

Bạn đang làm / 2 * athay vì / (2.f * a). Đây là lỗi tồi tệ nhất và đó là lý do tại sao mọi thứ đang đi sai.

Bạn tính toán shootxshootyvị trí cuối cùng của viên đạn, trong khi điều bạn đang tìm kiếm là vận tốc của viên đạn.

Mã cố định

float const bulletSpeed = 30.f;
/* Relative player position */
float const dx = playerX - enemyX;
float const dy = playerY - enemyY;
/* Relative player velocity */
float const vx = playerVelocityX - enemyVelocityX;
float const vy = playerVelocityY - enemyVelocityY;

float const a = vx * vx + vy * vy - bulletSpeed * bulletSpeed;
float const b = 2.f * (vx * dx + vy * dy);
float const c = dx * dx + dy * dy;
float const disc = b * b - 4.f * a * c;

shouldShoot = false;

if (disc >= 0.f)
{
    float t0 = (-b - std::sqrt(disc)) / (2.f * a);
    float t1 = (-b + std::sqrt(disc)) / (2.f * a);
    /* If t0 is negative, or t1 is a better solution, use t1 */
    if (t0 < 0.f || (t1 < t0 && t1 >= 0.f))
        t0 = t1;
    if (t0 >= 0.f)
    {
        /* Compute the ship's heading */
        shootx = vx + dx / t0;
        shooty = vy + dy / t0;
        heading = std::atan2(shooty, shootx) * RAD2DEGREE;
        /* Compute the bullet's velocity by adding the enemy's velocity */
        bulletVelocityX = shootx + enemyVelocityX;
        bulletVelocityY = shooty + enemyVelocityY;

        shouldShoot = true;
    }
}

vâng tôi đã gõ lại mã của mình và nhận thấy tôi đã làm hỏng vài thứ. Nó không phải là không phù hợp trong mã thực tế của tôi, tôi hứa lol! Tôi đã nhận thấy "Bạn đang làm / 2 * a thay vì / (2.f * a)." đêm qua và sửa nó. Tôi sẽ thử mã của bạn. Tôi đến đây sau khoảng bốn giờ cố gắng để tìm ra điều này và ở trong tình trạng khá khó chịu và tôi nghĩ đó là lý do tại sao tôi gõ mã rất khủng khiếp.
Azaral

Tôi đã thử điều này trong một đầu ra giao diện điều khiển đơn giản và nó không hoạt động. Tôi sao chép và dán nó vào một dự án mới. Tôi đã thêm các biến người chơi và kẻ thù không được khai báo từ mã của bạn. Bên trong khu vực if (t0> = 0.f) tôi đã thêm tính toán người chơi x, y và đạn x, y sau dòng nên bắn = true; playerX += playerVelocityX*t0; playerY += playerVelocityY*t0; float bulletX = ((cos(heading * DEGREE2RAD)*bulletSpeed + enemyVelocityX) * t0) + enemyX; float bulletY = ((sin(heading * DEGREE2RAD) * bulletSpeed + enemyVelocityY) * t0) + enemyY; Đầu ra là P = (84,8069,93.0386) E = (88.3793.105.132)
Azaral

Lúc này tôi sẽ không ngạc nhiên nếu tôi chỉ là một thằng ngốc. Tôi có thể chấp nhận điều đó.
Azaral

Trong ví dụ trên, dữ liệu gốc là: playerX = 50, playerY = 100, playerVelocityX = 20, playerVelocityY = -4, địchX = 145, địchY = 67, địchVelocityX = -5, địchVelocityY = 10.
Azaral

Không sử dụng bulletSpeedtại thời điểm này. Thay thế cos(heading * DEGREE2RAD) * bulletSpeedbằng shootx. Tương tự cho shooty. Lý do là vì vận tốc của kẻ thù được thêm vào vận tốc của viên đạn, tốc độ của nó không còn chính xác nữa bulletSpeed. Trong thực tế, bạn hoàn toàn không cần headingbiến, nó chỉ hữu ích cho việc gỡ lỗi.
sam hocevar

2

Có một game bắn súng di chuyển giống hệt như có một game bắn súng đứng yên. Đơn giản chỉ cần trừ vector chuyển động của game bắn súng khỏi vector chuyển động mục tiêu.

Target [-5,0]
Shooter [4,1]
Target - Shooter = [-5,0] - [4,1] = [-9,-1]

Tính toán vectơ bắn / góc ban đầu, sau đó thêm vectơ chuyển động mục tiêu vào viên đạn như bình thường.


Nếu bạn đọc câu hỏi của tôi và xem mã, bạn sẽ thấy tôi đã làm điều này.
Azaral

@azaral có, và không. bạn đang làm điều này để có được một trong những giá trị của bạn, và sau đó làm mọi thứ với vận tốc
vườn06

@ gardenian06 bạn có thể cụ thể hơn?
Azaral

@Azaral Tôi có ấn tượng rằng bạn có một thuật toán hoạt động khi người bắn đứng yên, như được mô tả trong liên kết bạn cung cấp. Nếu nó không hoạt động trong trường hợp đó, có một lỗi trong thuật toán cần sắp xếp trước khi bạn cần lo lắng về một game bắn súng di chuyển (không phải là sự khác biệt lớn, nhưng đơn giản hơn thì dễ gỡ lỗi hơn)
Daniel Carlsson

@DanielCarlsson bạn biết đó là một điểm tốt mà tôi không bao giờ nghĩ tới. Tôi chỉ giả định thuật toán làm việc.
Azaral

1

Đây là một ví dụ mà tôi đã nghĩ ra và thực hiện một giải pháp cho vấn đề nhắm mục tiêu dự đoán bằng thuật toán đệ quy: http://www.newarteest.com/flash/targeting.html (Tôi có một game bắn súng cố định nhưng cách tiếp cận tương tự sẽ hiệu quả với game bắn súng di chuyển)

Tôi sẽ phải thử một số giải pháp khác được trình bày bởi vì có vẻ hiệu quả hơn khi tính toán nó trong một bước, nhưng giải pháp tôi đưa ra là ước tính vị trí mục tiêu và đưa kết quả trở lại vào thuật toán để tạo ra một thuật toán mới ước tính chính xác hơn, lặp lại nhiều lần.

Đối với ước tính đầu tiên, tôi "bắn" vào vị trí hiện tại của mục tiêu và sau đó sử dụng lượng giác để xác định mục tiêu sẽ ở đâu khi phát bắn đạt đến vị trí bắn vào. Sau đó, trong lần lặp lại tiếp theo, tôi "bắn" vào vị trí mới đó và xác định mục tiêu sẽ ở đâu vào lúc này. Sau khoảng 4 lần lặp lại, tôi nhận được trong một pixel chính xác.


1
Một lợi thế của câu trả lời này là ngay cả khi không có giải pháp chính xác, người bắn vẫn sẽ bắn xung quanh vị trí của người chơi. +1 cho điều đó.
sam hocevar

huh tôi thậm chí đã không nghĩ về điều đó, điểm tốt
jhocking

0

Vì bạn chỉ làm việc với vật lý 2D (không có vận tốc Z), vấn đề này có thể được đơn giản hóa rất nhiều. Cách dễ dàng để làm điều này là ngừng suy nghĩ về cả nguồn và mục tiêu di chuyển so với tọa độ thế giới và chỉ nghĩ về mục tiêu di chuyển so với nguồn (và giữ ổn định nguồn).

Vector TargetInitialPosition = new Vector ( target.X - source.X ,
                                            target.Y - source.Y );
Vector TargetApparentVelocity = new Vector( target.velocityX - source.velocityX ,
                                            target.velocityY - source.velocityY );

Thông thường, vận tốc của viên đạn sẽ cao hơn nhiều so với vận tốc của người bắn nên người ta thường cho rằng viên đạn là độc lập nhưng có những trường hợp không đúng, chẳng hạn như bắn ra từ trực thăng hoặc máy bay chiến đấu.

Sau đó, chúng ta cần tính toán vận tốc viên đạn:

// Your directional vector MUST be normalized...
Vector BulletVelocity = new Vector( source.directionX * Bullet::StaticSpeed + source.velocityX ,
                                     source.directionY * Bullet::StaticSpeed + source.velocityY );

Vấn đề bạn gặp phải là mục tiêu đã di chuyển theo thời gian viên đạn chạm tới chúng.

TargetPosition = TargetInitialPosition + TargetApparentVelocity * t
BulletPosition = BulletInitialPosition + BulletVelocity * t
               = BulletVelocity * t

và giải quyết cho TargetPocation == BulletPocation vì khi đó viên đạn sẽ bắn trúng mục tiêu. Bây giờ bạn có ba ẩn số và chỉ có hai phương trình. Chúng tôi có thể xóa 't' bằng cách lấy đạo hàm bậc nhất:

TargetInitialPosition + ( TargetApparentVelocity - BulletVelocity ) * t == 0
dV / dt = TargetApparentVelocity - BulletVelocity

Bây giờ để đạt được mục tiêu, bạn muốn dV/dt == -TargetInitialPosition * k. Hằng số phải giống nhau ở tọa độ X và Y và là số giây mà viên đạn sẽ mất để bắn trúng mục tiêu.

TargetApparentVelocity.X - BulletVelocity.X == k * -TargetInitialPosition.X
k = ( BulletVelocity.X - TargetApparentVelocity.X ) / TargetInitialPosition.X
----------------------
TargetApparentVelocity.Y - BulletVelocity.Y == k * -TargetInitialPosition.Y
k = ( BulletVelocity.Y - TargetApparentVelocity.Y ) / TargetInitialPosition.Y

làm cho chúng bằng nhau:

( BulletVelocity.X - TargetApparentVelocity.X ) / TargetInitialPosition.X
= ( BulletVelocity.Y - TargetApparentVelocity.Y ) / TargetInitialPosition.Y

hoặc để mở rộng các biến:

( source.directionX * Bullet::StaticSpeed + source.velocityX - target.velocityX + source.velocityX ) / ( target.X - source.X )
 == ( source.directionY * Bullet::StaticSpeed + source.velocityY - target.velocityY + source.velocityY ) / ( target.Y - source.Y )

Sau đó đại số cung cấp cho bạn phương trình cuối cùng của bạn:

source.directionY = ( target.velocityY * ( source.X - target.X ) - 2 * source.velocityY * ( source.X - target.X ) + ( Bullet::Speed * source.directionX + 2 * source.velocityX - target.velocityX ) * ( source.Y - target.Y ) ) / ( Bullet::Speed * ( source.X - target.X ) )

Phần tiếp theo rất lộn xộn và tùy thuộc vào cách bạn muốn triển khai nó trong mã của mình, nhưng chúng tôi chỉ thay thế điều này trong và bình thường hóa vectơ.

sqrt( source.directionX ^ 2 + source.directionY ^ 2 ) == 1

Bạn kết thúc với một phương trình chỉ với một ẩn số (source.directionX) và bạn có thể giải nó cho directionX sau đó thay thế trở lại để lấy hướngY.

Tôi đã không kiểm tra bất kỳ mã nào trong số này và cảm thấy thoải mái khi chỉ ra bất kỳ sai lầm toán học nào tôi đã thực hiện, nhưng lý thuyết nên là âm thanh :).

Chúc may mắn.


Vâng, nó chỉ là 2D. Đang đọc bây giờ.
Azaral

Tại sao bạn lại thêm vận tốc nguồn vào vận tốc của viên đạn?
sam hocevar

@SamHocevar Đó là cách vật lý hoạt động. Một viên đạn được bắn ra từ một khẩu súng bắt đầu với vận tốc bằng với khẩu súng đã bắn nó. Sau đó, nó thêm vectơ được tạo bởi hành động bắn vào vectơ này. Hành động đang diễn ra trong không gian nên không có không khí để thay đổi vận tốc của viên đạn. Nếu bạn bắn một khẩu súng trong không gian trong khi di chuyển sang bên phải, nó sẽ luôn ở phía trước bạn cho đến khi bạn thay đổi vận tốc.
Azaral

1
Xin lỗi, nó hoạt động với các ví dụ, thật khó để trừu tượng hóa nó cho trường hợp chung. Mọi thứ thuộc về (dV / dt = TargetApparentVelocity - BulletVelocity) là lý thuyết và mã duy nhất bạn cần là vài hộp cuối cùng này. Đại số tôi nghĩ ra là cách phức tạp, nhưng Mathicala đã có thể giải nó. Lý do chúng tôi lấy đạo hàm bậc nhất là vì bạn không quan tâm viên đạn sẽ mất bao lâu, bạn chỉ muốn nó bắn trúng.
tyler.daniels

1
@Azaral đó không phải là cách hoạt động của vật lý. Trong khung tham chiếu chuyển động đều, bạn cần trừ vận tốc nguồn khỏi tất cả các vận tốc, kể cả đạn. Nếu bạn sử dụng vận tốc rõ ràng cho mục tiêu, bạn cũng cần sử dụng vận tốc rõ ràng cho viên đạn.
sam hocevar

0

Đây chỉ là một vấn đề hình học 3D.

Trước tiên, bạn cần vị trí tương đối và vận tốc tương đối của người bắn và mục tiêu:

Pos = TargetPos - ShooterPos
Vel = TargetVel - ShooterVel

Sau đó, bạn cần giải phương trình:

Pos + t * Vel = t * FireSpeed * [x , +-sqrt(1-x^2)]

Cho tx.

Điều đó làm cho:

x = ( PosX + t * VelX ) / ( t * FireSpeed )

( PosY + t * VelY ) / ( t * FireSpeed ) = +-sqrt(1 - (( PosX + t * VelX ) / ( t * FireSpeed ))^2 )
=>
( PosY + t * VelY )^2 / ( t * FireSpeed )^2 = 1 - (( PosX + t * VelX ) / ( t * FireSpeed ))^2
=>
( PosY + t * VelY )^2 + ( PosX + t * VelX )^2 = ( t * FireSpeed )^2
<=>
( Dot(Vel,Vel) - FireSpeed^2 ) * t^2 + 2 * Dot(Vel,Pos) * t + Dot(Pos,Pos) = 0

Phương trình cuối cùng trong số đó là một phương trình bậc hai đơn giản, bạn nên giải. Đối với mỗi kết quả dương tính, bạn chèn kết quả vào phương trình:

FireVector = Vel + Pos / t

Điều này sẽ cung cấp cho bạn mọi vectơ lửa có thể, tthời gian bắn sẽ là lúc nào.

Lưu ý rằng nếu FireSpeedlớn hơn độ lớn Velsẽ chỉ có một giải pháp, nhưng nếuFireSpeed nhỏ hơn có thể có hai giải pháp hoặc không có giải pháp nào, hoặc trong trường hợp đặc biệt chỉ là một giải pháp kép.

Chỉnh sửa: Tốt hơn làm toán đúng hoặc câu trả lời này sẽ không tốt lắm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.