2d 게임:화재에서 움직이는 대상을 예측하여 교차로의 발사체 및 단위

StackOverflow https://stackoverflow.com/questions/2248876

  •  20-09-2019
  •  | 
  •  

문제

좋아요 이 모든 걸리는 곳에는 좋은 간단한 2D 세계...:)

한 두 가지가 아닌 정적 객체에 위치 Apos,그리고 선형으로 이동체에서 B Bpos 와 bVelocity,와 탄약은 라운드와 속도 Avelocity...

는 방법을 찾겠 각도는 있을 촬영을 칠 B,고려 B's 선속도 및 속도의 탄?

지금 목적의 현재 위치에 있는 객체의 것을 의미하는 시간으로 내 발사체가의 단위에 이동하는 안전 위치:)

도움이 되었습니까?

해결책

먼저 AB가 세로가되도록 축을 회전시킵니다 (회전을 통해)

이제 B의 속도 벡터를 X 및 Y 구성 요소 (BX 및 BY)로 분할하십시오. 이것을 사용하여 촬영 해야하는 벡터의 X 및 Y 구성 요소를 계산할 수 있습니다.

B --> Bx
|
|
V

By


Vy
^
|
|
A ---> Vx

당신은 필요합니다 Vx = Bx 그리고 Sqrt(Vx*Vx + Vy*Vy) = Velocity of Ammo.

이것은 새로운 시스템에서 필요한 벡터를 제공해야합니다. 오래된 시스템으로 다시 변환하면 (다른 방향으로 회전을함으로써) 완료됩니다.

다른 팁

내가 쓴 것을 목표로에 대한 서브 루틴 xtank 입니다.내가 노력하는 배치했던 방법니다.

면책 조항: 내가 만들 수 있습니다 하나 더 많은 어리석은 실수를 어디서나 여기에서;나는 단지를 재구성하려는 추론을 가진 나 녹스는 수학 능력입니다.그러나 나는 절단을 쫓아 첫째로,이 프로그래밍 Q&A 대 수학 수업:-)

그것을 어떻게

귀결하는 해결 방정식의 형태로 되어 있습니다.

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

참고는여 sqr I mean square,반대로 사각형 뿌리입니다.다음과 같은 값을 사용:

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)

지금 우리가 볼 수 있는 판별하여 결정하는 경우 우리가 가능한 솔루션입니다.

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

는 경우 판별 0 보다 작은 잊을 타격의 대상을-당신의 발사체하지 못할 수 있는 시간입니다.그렇지 않으면을 봐 두 후보는 솔루션:

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

참고하는 경우 disc == 0 다음 t1t2 같습니다.

이 없는 경우 다른 고려 사항 등으로 개입 장애물,단순히 선택한 작은 긍정적인 값입니다.(정 t 값을 필요로 발 뒤에서 사용하는 시간!)

대체 선택 t 값으로 다시 대상의 위치를 방정식을 얻을 수의 좌표를 선도하는 점을 목표로해야한다:

aim.X := t * target.velocityX + target.startX
aim.Y := t * target.velocityY + target.startY

유도

T 시간에서,발사체해야(유클리드)거리 대포에서 같은 경과 시간을 곱하여 발사체의 속도입니다.이 제공한 방정식,원형 파라미터에 표시됩니다.

sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
  == sqr(t * projectile_speed)

마찬가지로,시간,대상이 따라 이동 벡터에 의해 시간을 곱한 값으로 속도:

target.X == t * target.velocityX + target.startX
target.Y == t * target.velocityY + target.startY

발사체를 칠 수 있는 대상을 때의 거리 대포에서 일치하는 발사체의 거리입니다.

sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
  == sqr(target.X - cannon.X) + sqr(target.Y - cannon.Y)

Wonderful!대체하는 표현에 대한 대상입니다.X 습니다.Y 제공

sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
  == sqr((t * target.velocityX + target.startX) - cannon.X)
   + sqr((t * target.velocityY + target.startY) - cannon.Y)

대체 방정식의 다른 측면이 제공:

sqr(t * projectile_speed)
  == sqr((t * target.velocityX + target.startX) - cannon.X)
   + sqr((t * target.velocityY + target.startY) - cannon.Y)

...빼 sqr(t * projectile_speed) 양쪽에서 내리고 그 주위:

sqr((t * target.velocityX) + (target.startX - cannon.X))
  + sqr((t * target.velocityY) + (target.startY - cannon.Y))
  - sqr(t * projectile_speed)
  == 0

...이제를 해결합한 결과를 제곱 하위 식...

sqr(target.velocityX) * sqr(t)
    + 2 * t * target.velocityX * (target.startX - cannon.X)
    + sqr(target.startX - cannon.X)
+ sqr(target.velocityY) * sqr(t)
    + 2 * t * target.velocityY * (target.startY - cannon.Y)
    + sqr(target.startY - cannon.Y)
- sqr(projectile_speed) * sqr(t)
  == 0

...고 그룹 유사한다.

sqr(target.velocityX) * sqr(t)
    + sqr(target.velocityY) * sqr(t)
    - sqr(projectile_speed) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
    + 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startX - cannon.X)
    + sqr(target.startY - cannon.Y)
  == 0

...그런 다음 그들을 결합하...

(sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)) * sqr(t)
  + 2 * (target.velocityX * (target.startX - cannon.X)
       + target.velocityY * (target.startY - cannon.Y)) * t
  + sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
  == 0

...을 주는 표준 방정식에서 t.을 찾는 긍정적인 실제 제이 방정식을 제공합(제로,하나 또는 두 개의)가 충돌 위치를 수행할 수 있습 차 formula:

a * sqr(x) + b * x + c == 0
x == (-b ± sqrt(sqr(b) - 4 * a * c)) / (2 * a)

Jeffrey Hantin의 훌륭한 답변에서 +1. 나는 Google을 Google으로하여 내가 관심이있는 경우 (2D 공간의 단순한 일정한 속도 발사체)에 대해 너무 복잡한 솔루션을 찾았습니다. 그는 자체 포함 된 JavaScript 솔루션을 생산하는 데 필요한 것입니다.

내가 추가 할 한 가지 요점은 판별자가 부정적이라는 몇 가지 특별한 경우가 있다는 것입니다.

  • "A == 0": 대상과 발사체가 같은 속도로 이동하는 경우 발생합니다. (솔루션은 2 차가 아닌 선형입니다)
  • "a == 0 및 b == 0": 대상과 발사체가 모두 고정 된 경우. (C == 0, 즉 SRC & DST가 동일하지 않는 한 해결책이 없습니다.)

암호:

/**
 * Return the firing solution for a projectile starting at 'src' with
 * velocity 'v', to hit a target, 'dst'.
 *
 * @param Object src position of shooter
 * @param Object dst position & velocity of target
 * @param Number v   speed of projectile
 * @return Object Coordinate at which to fire (and where intercept occurs)
 *
 * E.g.
 * >>> intercept({x:2, y:4}, {x:5, y:7, vx: 2, vy:1}, 5)
 * = {x: 8, y: 8.5}
 */
function intercept(src, dst, v) {
  var tx = dst.x - src.x,
      ty = dst.y - src.y,
      tvx = dst.vx,
      tvy = dst.vy;

  // Get quadratic equation components
  var a = tvx*tvx + tvy*tvy - v*v;
  var b = 2 * (tvx * tx + tvy * ty);
  var c = tx*tx + ty*ty;    

  // Solve quadratic
  var ts = quad(a, b, c); // See quad(), below

  // Find smallest positive solution
  var sol = null;
  if (ts) {
    var t0 = ts[0], t1 = ts[1];
    var t = Math.min(t0, t1);
    if (t < 0) t = Math.max(t0, t1);    
    if (t > 0) {
      sol = {
        x: dst.x + dst.vx*t,
        y: dst.y + dst.vy*t
      };
    }
  }

  return sol;
}


/**
 * Return solutions for quadratic
 */
function quad(a,b,c) {
  var sol = null;
  if (Math.abs(a) < 1e-6) {
    if (Math.abs(b) < 1e-6) {
      sol = Math.abs(c) < 1e-6 ? [0,0] : null;
    } else {
      sol = [-c/b, -c/b];
    }
  } else {
    var disc = b*b - 4*a*c;
    if (disc >= 0) {
      disc = Math.sqrt(disc);
      a = 2*a;
      sol = [(-b-disc)/a, (-b+disc)/a];
    }
  }
  return sol;
}

Jeffrey Hantin 은이 문제에 대한 좋은 해결책을 가지고 있지만 그의 파생은 지나치게 복잡합니다. 다음은 맨 아래의 결과 코드로이를 도출하는 더 깨끗한 방법입니다.

나는 XY를 사용하여 벡터 도트 생성물을 나타냅니다. 벡터 수량이 제곱되면 그 자체로 점을 찍고 있음을 의미합니다.

origpos = initial position of shooter
origvel = initial velocity of shooter

targpos = initial position of target
targvel = initial velocity of target

projvel = velocity of the projectile relative to the origin (cause ur shooting from there)
speed   = the magnitude of projvel
t       = time

우리는 t 시간은 일부 방정식으로 설명 할 수 있습니다.

curprojpos(t) = origpos + t*origvel + t*projvel
curtargpos(t) = targpos + t*targvel

우리는 이것들이 어느 시점 (교차점)에서 서로 동등하기를 원하므로 서로 동등하게 설정하고 자유 변수를 해결해 봅시다. projvel.

origpos + t*origvel + t*projvel = targpos + t*targvel
    turns into ->
projvel = (targpos - origpos)/t + targvel - origvel

원래의 개념과 목표 위치/속도를 잊어 봅시다. 대신, 한 가지의 움직임은 다른 것과 관련이 있기 때문에 상대적인 용어로 일합시다. 이 경우, 우리가 지금 가지고있는 것은입니다 relpos = targetpos - originpos 그리고 relvel = targetvel - originvel

projvel = relpos/t + relvel

우리는 무엇을 모른다 projvel 그러나 우리는 우리가 원하는 것을 알고 있습니다 projvel.projvel 동일합니다 speed^2, 그래서 우리는 양쪽을 제곱하고 우리는 얻을 수 있습니다

projvel^2 = (relpos/t + relvel)^2
    expands into ->
speed^2 = relvel.relvel + 2*relpos.relvel/t + relpos.relpos/t^2

이제 유일한 자유 변수는 시간임을 알 수 있습니다. t, 그런 다음 우리는 사용할 것입니다 t 해결하기 위해 projvel. 우리는 해결할 것입니다 t 2 차 공식으로. 먼저 그것을 분리하십시오 a, b 그리고 c, 그런 다음 뿌리를 해결하십시오.

그러나 해결하기 전에 우리는 최고의 솔루션을 원한다는 것을 기억하십시오. t 가장 작지만 우리는 t 부정적이지 않습니다 (과거에는 무언가를 때릴 수 없습니다)

a  = relvel.relvel - speed^2
b  = 2*relpos.relvel
c  = relpos.relpos

h  = -b/(2*a)
k2  = h*h - c/a

if k2 < 0, then there are no roots and there is no solution
if k2 = 0, then there is one root at h
    if 0 < h then t = h
    else, no solution
if k2 > 0, then there are two roots at h - k and h + k, we also know r0 is less than r1.
    k  = sqrt(k2)
    r0 = h - k
    r1 = h + k
    we have the roots, we must now solve for the smallest positive one
    if 0<r0 then t = r0
    elseif 0<r1 then t = r1
    else, no solution

이제 우리가 있다면 t 값, 우리는 플러그를 꽂을 수 있습니다 t 원래 방정식으로 돌아가서 해결하십시오 projvel

 projvel = relpos/t + relvel

이제 발사체를 촬영하기 위해 발사체의 결과적인 글로벌 위치와 속도는 다음과 같습니다.

globalpos = origpos
globalvel = origvel + projvel

그리고 당신은 끝났습니다!

LUA에서 솔루션을 구현하는데, 여기서 vec*vec은 벡터 도트 제품을 나타냅니다.

local function lineartrajectory(origpos,origvel,speed,targpos,targvel)
    local relpos=targpos-origpos
    local relvel=targvel-origvel
    local a=relvel*relvel-speed*speed
    local b=2*relpos*relvel
    local c=relpos*relpos
    if a*a<1e-32 then--code translation for a==0
        if b*b<1e-32 then
            return false,"no solution"
        else
            local h=-c/b
            if 0<h then
                return origpos,relpos/h+targvel,h
            else
                return false,"no solution"
            end
        end
    else
        local h=-b/(2*a)
        local k2=h*h-c/a
        if k2<-1e-16 then
            return false,"no solution"
        elseif k2<1e-16 then--code translation for k2==0
            if 0<h then
                return origpos,relpos/h+targvel,h
            else
                return false,"no solution"
            end
        else
            local k=k2^0.5
            if k<h then
                return origpos,relpos/(h-k)+targvel,h-k
            elseif -k<h then
                return origpos,relpos/(h+k)+targvel,h+k
            else
                return false,"no solution"
            end
        end
    end
end

다음은 C ++의 극지 좌표 기반 조준 코드입니다.

직사각형 좌표와 함께 사용하려면 먼저 대상 상대 좌표를 각도/거리로 변환하고 x/y 속도를 각도/속도로 변환해야합니다.

"속도"입력은 발사체의 속도입니다. 속도와 목표 속도의 단위는 속도의 비율 만 계산에 사용되므로 관련이 없습니다. 출력은 발사체가 발사되어야하는 각도와 충돌 지점까지의 거리입니다.

알고리즘은 소스 코드에서 제공됩니다 http://www.turtlewar.org/ .


// C++
static const double pi = 3.14159265358979323846;
inline double Sin(double a) { return sin(a*(pi/180)); }
inline double Asin(double y) { return asin(y)*(180/pi); }

bool/*ok*/ Rendezvous(double speed,double targetAngle,double targetRange,
   double targetDirection,double targetSpeed,double* courseAngle,
   double* courseRange)
{
   // Use trig to calculate coordinate of future collision with target.
   //             c
   //
   //       B        A
   //
   // a        C        b
   //
   // Known:
   //    C = distance to target
   //    b = direction of target travel, relative to it's coordinate
   //    A/B = ratio of speed and target speed
   //
   // Use rule of sines to find unknowns.
   //  sin(a)/A = sin(b)/B = sin(c)/C
   //
   //  a = asin((A/B)*sin(b))
   //  c = 180-a-b
   //  B = C*(sin(b)/sin(c))

   bool ok = 0;
   double b = 180-(targetDirection-targetAngle);
   double A_div_B = targetSpeed/speed;
   double C = targetRange;
   double sin_b = Sin(b);
   double sin_a = A_div_B*sin_b;
   // If sin of a is greater than one it means a triangle cannot be
   // constructed with the given angles that have sides with the given
   // ratio.
   if(fabs(sin_a) <= 1)
   {
      double a = Asin(sin_a);
      double c = 180-a-b;
      double sin_c = Sin(c);
      double B;
      if(fabs(sin_c) > .0001)
      {
         B = C*(sin_b/sin_c);
      }
      else
      {
         // Sin of small angles approach zero causing overflow in
         // calculation. For nearly flat triangles just treat as
         // flat.
         B = C/(A_div_B+1);
      }
      // double A = C*(sin_a/sin_c);
      ok = 1;
      *courseAngle = targetAngle+a;
      *courseRange = B;
   }
   return ok;
}

다음은 재귀 알고리즘을 사용하여 예측 타겟팅 문제에 대한 솔루션을 고안하고 구현 한 예입니다. http://www.newarteest.com/flash/targeting.html

한 단계로 계산하는 것이 더 효율적으로 보이기 때문에 제시된 다른 솔루션 중 일부를 시험해보아야하지만, 제가 생각해 낸 솔루션은 목표 위치를 추정하고 그 결과가 알고리즘으로 다시 피드를 추정하는 것이 었습니다. 더 정확한 추정치가 여러 번 반복됩니다.

첫 번째 추정치의 경우, 대상의 현재 위치에서 "발사"를 한 다음 삼각법을 사용하여 샷이 해고 된 위치에 도달 할 때 대상이 어디에 있는지 확인합니다. 그런 다음 다음 반복에서 나는 그 새로운 위치에서 "발사"하고 이번에는 대상이 어디에 있을지 결정합니다. 약 4 회 반복 후 나는 정확도의 픽셀 내에 들어갑니다.

방금 2D 공간을 조준하기 위해이 버전을 해킹했는데 아직 철저히 테스트하지는 않았지만 작동하는 것 같습니다. 그 뒤에있는 아이디어는 이것입니다.

총구에서 대상으로 가리키는 벡터에 수직 인 벡터를 만듭니다. 충돌이 발생하기 위해서는 대상의 속도 와이 벡터 (축)의 발사체가 동일해야합니다! 상당히 간단한 코사인 물건을 사용 하여이 코드에 도착했습니다.

private Vector3 CalculateProjectileDirection(Vector3 a_MuzzlePosition, float a_ProjectileSpeed, Vector3 a_TargetPosition, Vector3 a_TargetVelocity)
{
    // make sure it's all in the horizontal plane:
    a_TargetPosition.y = 0.0f;
    a_MuzzlePosition.y = 0.0f;
    a_TargetVelocity.y = 0.0f;

    // create a normalized vector that is perpendicular to the vector pointing from the muzzle to the target's current position (a localized x-axis):
    Vector3 perpendicularVector = Vector3.Cross(a_TargetPosition - a_MuzzlePosition, -Vector3.up).normalized;

    // project the target's velocity vector onto that localized x-axis:
    Vector3 projectedTargetVelocity = Vector3.Project(a_TargetVelocity, perpendicularVector);

    // calculate the angle that the projectile velocity should make with the localized x-axis using the consine:
    float angle = Mathf.Acos(projectedTargetVelocity.magnitude / a_ProjectileSpeed) / Mathf.PI * 180;

    if (Vector3.Angle(perpendicularVector, a_TargetVelocity) > 90.0f)
    {
        angle = 180.0f - angle;
    }

    // rotate the x-axis so that is points in the desired velocity direction of the projectile:
    Vector3 returnValue = Quaternion.AngleAxis(angle, -Vector3.up) * perpendicularVector;

    // give the projectile the correct speed:
    returnValue *= a_ProjectileSpeed;

    return returnValue;
}

나는이 문제를 수학적으로 해결하는 여러 가지 방법을 보았지만, 이것은 내 수업이 고등학교에서해야 할 프로젝트와 관련된 구성 요소였으며,이 프로그래밍 수업의 모든 사람이 미적분학을 가진 배경을 가지고 있지는 않았거나 그 문제에 대한 벡터가 없었습니다. 그래서 나는 더 많은 프로그래밍 접근법 으로이 문제를 해결할 수있는 방법을 만들었습니다. 교차점의 지점은 정확합니다. 그러나 수학 계산보다 늦게 1 프레임에 도달 할 수 있습니다.

고려하다:

S = shooterPos, E = enemyPos, T = targetPos, Sr = shooter range, D = enemyDir
V = distance from E to T, P = projectile speed, Es = enemy speed

이 문제의 표준 구현에서 [s, e, p, es, d]는 모두 givens이며, 당신은 t 또는 촬영할 각도를 찾아서 적절한 타이밍에서 t를 때릴 수 있습니다.

문제를 해결하는이 방법의 주요 측면은 슈터의 범위를 주어진 시간에 촬영할 수있는 모든 가능한 포인트를 포괄하는 원으로 간주하는 것입니다. 이 원의 반경은 다음과 같습니다.

Sr = P*time

시간이 루프 반복으로 계산되는 경우.

따라서 우리는 벡터를 만듭니다.

V = D*Es*time

이제 실제로 문제를 해결하기 위해 대상 (T)에서 사수 (S)까지의 거리가 사수 (SR)의 범위보다 적은 지점을 찾고자합니다. 다음은이 방정식의 유사 코드 구현입니다.

iteration = 0;
while(TargetPoint.hasNotPassedShooter)
{
    TargetPoint = EnemyPos + (EnemyMovementVector)
    if(distanceFrom(TargetPoint,ShooterPos) < (ShooterRange))
        return TargetPoint;
    iteration++
}

여기에서 공개 도메인 Unity C# 기능을 만들었습니다.
http://ringofblades.com/blades/code/predictiveaim.cs

3D이지만 벡터3을 벡터 2로 교체하고 중력이있는 경우 중력을 위해 선택 축을 사용하여 2D에 대해 쉽게 수정할 수 있습니다.

이론이 당신에게 관심을 갖는 경우, 나는 여기서 수학의 파생을 안내합니다.
http://www.gamasutra.com/blogs/kainshin/20090515/83954/predictive_aim_mathematics_for_ai_targeting.php

기본적으로, 발사체 모션을 사용하는 한, 교차로 개념은 실제로 필요하지 않습니다. 특정 각도로 치기 만하면 촬영시 인스턴스화하여 소스에서 대상의 정확한 거리를 얻을 수 있습니다. 거리가 있으면 대상을 치기 위해 촬영 해야하는 적절한 속도를 계산할 수 있습니다.

다음 링크는 개념을 명확하게하고 도움이되는 것으로 간주되며 도움이 될 수 있습니다.항상 움직이는 대상에 부딪히는 발사체 움직임

나는 여기에서 솔루션 중 하나를 잡았지만, 그들 중 누구도 사수의 움직임을 고려하지 않습니다. 사수가 움직이면, 당신은 그것을 고려할 수 있습니다 (사수의 속도가 발사 될 때 총알의 속도에 추가되어야 함). 실제로 당신이해야 할 일은 대상의 속도에서 사수의 속도를 빼기 만하면됩니다. 따라서 위의 Broofa의 코드를 사용하는 경우 (권장 할 것입니다) 라인을 변경하십시오.

  tvx = dst.vx;
  tvy = dst.vy;

에게

  tvx = dst.vx - shooter.vx;
  tvy = dst.vy - shooter.vy;

그리고 당신은 모두 설정되어야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top