2d game : fire at a moving target by predicting intersection of projectile and unit

后端 未结 11 772
庸人自扰
庸人自扰 2020-11-28 22:26

Okay, this all takes place in a nice and simple 2D world... :)

Suppose I have a static object A at position Apos, and a linearly moving object B at Bpos with bVeloci

相关标签:
11条回答
  • 2020-11-28 22:48

    I made a public domain Unity C# function here:
    http://ringofblades.com/Blades/Code/PredictiveAim.cs

    It is for 3D, but you can easily modify this for 2D by replacing the Vector3s with Vector2s and using your down axis of choice for gravity if there is gravity.

    In case the theory interests you, I walk through the derivation of the math here:
    http://www.gamasutra.com/blogs/KainShin/20090515/83954/Predictive_Aim_Mathematics_for_AI_Targeting.php

    0 讨论(0)
  • 2020-11-28 22:50

    Following is polar coordinate based aiming code in C++.

    To use with rectangular coordinates you would need to first convert the targets relative coordinate to angle/distance, and the targets x/y velocity to angle/speed.

    The "speed" input is the speed of the projectile. The units of the speed and targetSpeed are irrelevent, as only the ratio of the speeds are used in the calculation. The output is the angle the projectile should be fired at and the distance to the collision point.

    The algorithm is from source code available at 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;
    }
    
    
    0 讨论(0)
  • 2020-11-28 22:52

    I grabbed one of the solutions from here, but none of them take into account movement of the shooter. If your shooter is moving, you might want to take that into account (as the shooter's velocity should be added to your bullet's velocity when you fire). Really all you need to do is subtract your shooter's velocity from the target's velocity. So if you're using broofa's code above (which I would recommend), change the lines

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

    to

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

    and you should be all set.

    0 讨论(0)
  • 2020-11-28 22:54

    Jeffrey Hantin has a nice solution for this problem, though his derivation is overly complicated. Here's a cleaner way of deriving it with some of the resultant code at the bottom.

    I'll be using x.y to represent vector dot product, and if a vector quantity is squared, it means I am dotting it with itself.

    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
    

    We know that the position of the projectile and target with respect to t time can be described with some equations.

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

    We want these to be equal to each other at some point (the point of intersection), so let's set them equal to each other and solve for the free variable, projvel.

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

    Let's forget about the notion of origin and target position/velocity. Instead, let's work in relative terms since motion of one thing is relative to another. In this case, what we now have is relpos = targetpos - originpos and relvel = targetvel - originvel

    projvel = relpos/t + relvel
    

    We don't know what projvel is, but we do know that we want projvel.projvel to be equal to speed^2, so we'll square both sides and we get

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

    We can now see that the only free variable is time, t, and then we'll use t to solve for projvel. We'll solve for t with the quadratic formula. First separate it out into a, b and c, then solve for the roots.

    Before solving, though, remember that we want the best solution where t is smallest, but we need to make sure that t is not negative (you can't hit something in the past)

    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
    

    Now, if we have a t value, we can plug t back into the original equation and solve for the projvel

     projvel = relpos/t + relvel
    

    Now, to the shoot the projectile, the resultant global position and velocity for the projectile is

    globalpos = origpos
    globalvel = origvel + projvel
    

    And you're done!

    My implementation of my solution in Lua, where vec*vec represents vector dot product:

    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
    
    0 讨论(0)
  • 2020-11-28 22:59

    I've seen many ways to solve this problem mathematically, but this was a component relevant to a project my class was required to do in high school, and not everyone in this programming class had a background with calculus, or even vectors for that matter, so I created a way to solve this problem with more of a programming approach. The point of intersection will be accurate, although it may hit 1 frame later than in the mathematical computations.

    Consider:

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

    In the standard implementation of this problem [S,E,P,Es,D] are all givens and you are solving either to find T or the angle at which to shoot so that you hit T at the proper timing.

    The main aspect of this method of solving the problem is to consider the range of the shooter as a circle encompassing all possible points that can be shot at any given time. The radius of this circle is equal to:

    Sr = P*time
    

    Where time is calculated as an iteration of a loop.

    Thus to find the distance an enemy travels given the time iteration we create the vector:

    V = D*Es*time
    

    Now, to actually solve the problem we want to find a point at which the distance from the target (T) to our shooter (S) is less than the range of our shooter (Sr). Here is somewhat of a pseudocode implementation of this equation.

    iteration = 0;
    while(TargetPoint.hasNotPassedShooter)
    {
        TargetPoint = EnemyPos + (EnemyMovementVector)
        if(distanceFrom(TargetPoint,ShooterPos) < (ShooterRange))
            return TargetPoint;
        iteration++
    }
    
    0 讨论(0)
提交回复
热议问题