Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.97a is out! (02/02/24); New blog post: Simulator Enhancements (03/13/24)

Pages: [1] 2

Author Topic: LeadVector  (Read 7060 times)

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
LeadVector
« on: May 08, 2014, 12:35:08 PM »

Derail Moved from
http://fractalsoftworks.com/forum/index.php?topic=7966.30

LeadVector
Code: java
    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .0001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return new Vector2f(0,0);
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return new Vector2f(0,0);
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }

this is actualy designed to calculate the intercept of a bullet fired from point Mloc with Mvel speed, at target at Tloc with Tvel velocity
**fixed

for the purposes of this calculation even in cases such as missiles Mvel can be asumed to be the objects maximum speed, as long as object HAS a maximum speed, and IS allways accelerating


IF cases:
Spoiler
1) IF Tvel and Mvel are in the same direction: LeadPoint defaults to Tloc (the objects location)
2) IF Mvel is insufficiet to reach T: LeadPoint defaults to Tloc (the objects location)
3) IF Mvel is still accelerating: by a large this is irelevant as it will default to (2)
4) IF Tvel is changing(trying to djink): LeadPoint will produce the shortest intercept point every time (so as long as it is recalculated say 1/sec it will be on target)
5) IF chasing object has no maximum speed(or very high compared to acceleration): it will produce the best lead it can for its curent speed, not ideal, but accounting for this case is largely irrelevant (error will be insignificant, usualy a few extra % time above the "ideal" time to intercept)
[close]
« Last Edit: May 08, 2014, 01:08:24 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 23988
    • View Profile
Re: LeadVector
« Reply #1 on: May 08, 2014, 12:44:43 PM »

Reposting from that other thread:

(I ended up giving that code a quick spin in the vanilla missile AI, using the result of LeadVector as the point the missile aims for... hm. Missiles will happily head away from the (slower!) target under certain conditions, and it doesn't actually do a good job of tracking it when it does head for it. Maybe something else you're doing in the AI - or the stats of the missiles you're using - is/are somehow making up for it, but you might want to take a look at it.)

Side note: it's not using the MVx/MVy values anywhere. Might be indicative of an issue.


for the purposes of this calculation even in cases such as missiles Mvel can be asumed to be the objects maximum speed, as long as object HAS a maximum speed, and IS allways accelerating

That assumption only makes sense if the acceleration is very high. That's what I've been trying to say all along :)
Logged

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #2 on: May 08, 2014, 12:57:42 PM »

Fixed (for some reason i had it returning Tloc instead of Vector2f(0,0) in the failure to intercept case)

Quote
Side note: it's not using the MVx/MVy values anywhere. Might be indicative of an issue.
Vector2f MVel is the one actualy used, i just seperated it out into MVx/MVy for some weird reason



Quote
That assumption only makes sense if the acceleration is very high. That's what I've been trying to say all along
nope, if it it is still accelerating

it will either return Vector2f(0,0) (meaning it cant intercept, and should head straight for target)

or it will give the best intercept at its curent speed, the acceleration does not actualy matter "that" much (the real difrence between taking it into acount or not is less then 5 degrees)


@Xeno
Quote
There is a weighted function there; it's not sufficient to do the quadratic interception every frame.  Anyhow, that's a major derail; perhaps a new thread?
not sure what you mean
« Last Edit: May 08, 2014, 01:12:22 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: LeadVector
« Reply #3 on: May 08, 2014, 01:13:24 PM »

OK... first off... bullet-bullet intercepts just need to use this to get the desired angle to intercept, it's not costly and it works:

Code: java
    private Vector2f getFiringSolution(Vector2f source, Vector2f destLocation, Vector2f destVelocity, float projVel)
    {
        float tx = destLocation.getX() - source.getX();
        float ty = destLocation.getY() - source.getY();
        float tvx = destVelocity.getX();
        float tvy = destVelocity.getY();

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

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

        // Find smallest positive solution
        Vector2f finalSolution = new Vector2f();
        if(ts != null)
        {
            float t0 = ts.getX();
            float t1 = ts.getY();
            float t = Math.min(t0, t1);
            if (t < 0) t = Math.max(t0, t1);   
            if (t > 0)
            {
                finalSolution.setX(destLocation.getX() + destVelocity.getX()*t);
                finalSolution.setY(destLocation.getY() + destVelocity.getY()*t);
            }
        }
        return finalSolution;
    }
   
    private Vector2f quad(float a, float b,float c) {
      Vector2f sol = new Vector2f();
      if (Math.abs(a) < 1e-6) {
            if (Math.abs(b) < 1e-6) {
                if(Math.abs(c) < 1e-6){
                    sol.set(0f,0f);
                } else {
                    sol = null;
                }
            } else {
                sol.setX(-c/b);
                sol.setY(-c/b);
            }
        } else {
            float disc = b*b - 4*a*c;
            if (disc >= 0f) {
                disc = (float) Math.sqrt(disc);
                a = 2f*a;
                sol.set((-b-disc)/a, (-b+disc)/a);
            }
      }
      return sol;
    }

The above code works quite nicely for constant-velocity intercepts and should work fairly well if called every few frames, as that will get an update for current acceleration.  The Vector2f returned is the intercept point to reach.  ProjVelocity would be the AI fleet's maximum velocity.

The catch is determining when to abandon a chase. 

Two fleets at the same velocity might result in a catch, but not frequently, so they should only attempt to when another fleet is sufficiently close and their current vectors are < 90 degrees from one another (i.e., they're heading towards one another), and obviously a low-speed fleet shouldn't bother to try this against a high-speed fleet with anything more than a couple of frames (in velocity, adjusted for relative vector) away.

There shouldn't be any issues with juking it, though, if called often enough; given that the effective velocity of a fleet gets lowered every time it changes heading by a little bit, it should result in an intercept every time.  While it's fairly expensive, if we're only talking 30-ish fleets in a System, max... nobody will ever notice.

The last catch is that deciding when AI fleets should attempt this is non-trivial; we obviously don't want them to always act like heat-seeking missiles, making a beeline for targets all the way across Systems... and then there is the issue of making them 'smart' about determining what fights they can actually win.
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #4 on: May 08, 2014, 01:19:34 PM »

i have this weird feeling that your code and my code are doing identical things, just written in a difrent way :P

*after looking at it closely*
yep, its identical (mine calculates time, yours calculates angle, but the actual process to get there is the same)
« Last Edit: May 08, 2014, 01:26:19 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 23988
    • View Profile
Re: LeadVector
« Reply #5 on: May 08, 2014, 01:26:26 PM »

Fixed (for some reason i had it returning Tloc instead of Vector2f(0,0) in the failure to intercept case)

I don't understand how returning the wrong answer in one specific case can fix it. If anything, returning the target location sounds like a better option at that point.

Edit: have you used it with vanilla missiles? I have this feeling like it only works under certain conditions that most vanilla missiles don't meet.
« Last Edit: May 08, 2014, 01:32:20 PM by Alex »
Logged

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #6 on: May 08, 2014, 01:41:32 PM »

LeadVector + TargetLocation = ThePointYouGoTo

:P

old one if it couldnt reach target gave you [TargetLocation] instead of [Vector2f(0,0)]
so you got TargetLocation + TargetLocation = VerryFarAway
« Last Edit: May 08, 2014, 01:43:41 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

Dark.Revenant

  • Admiral
  • *****
  • Posts: 2806
    • View Profile
    • Sc2Mafia
Re: LeadVector
« Reply #7 on: May 08, 2014, 01:43:02 PM »

I made an implementation a while ago for the next LazyLib, and tested it on vanilla missiles to great success.

Code: java
package data.scripts.weapons;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.GuidedMissileAI;
import com.fs.starfarer.api.combat.MissileAIPlugin;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipCommand;

import java.util.*;
import org.lwjgl.util.vector.Vector2f;

import org.lazywizard.lazylib.CollectionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;

public class SeekerAI implements MissileAIPlugin, GuidedMissileAI {

    private final MissileAPI missile;
    private CombatEntityAPI target;

    private final static float VELOCITY_DAMPING_FACTOR = 0.2f; // Lower numbers means it will snap in place more abruptly

    public static ShipAPI findBestTarget(MissileAPI missile) {
        ShipAPI source = missile.getSource();
        if (source != null && source.getShipTarget() != null && !source.getShipTarget().isHulk()) {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

    public SeekerAI(MissileAPI missile, ShipAPI launchingShip) {
        this.missile = missile;

        List<ShipAPI> directTargets = CombatUtils.getShipsWithinRange(launchingShip.getMouseTarget(), 100f);
        if (!directTargets.isEmpty()) {
            Collections.sort(directTargets, new CollectionUtils.SortEntitiesByDistance(launchingShip.getMouseTarget()));
            for (ShipAPI tmp : directTargets) {
                if (!tmp.isHulk() && tmp.getOwner() != launchingShip.getOwner()) {
                    setTarget(tmp);
                    break;
                }
            }
        }

        if (target == null) {
            setTarget(findBestTarget(missile));
        }
    }

    @Override
    public void advance(float amount) {
        if (missile.isFizzling() || missile.isFading()) {
            return;
        }
        
        if (target == null || (target instanceof ShipAPI && ((ShipAPI) target).isHulk()) || (missile.getOwner() == target.getOwner()) || !Global.getCombatEngine().isEntityInPlay(target)) {
            setTarget(findBestTarget(missile));
            missile.giveCommand(ShipCommand.ACCELERATE);
            return;
        }

        // Head towards the target, with target leading
        float distance = MathUtils.getDistance(target.getLocation(), missile.getLocation());
        Vector2f guidedTarget;
        if (distance <= target.getCollisionRadius() * 1.2f) {
            guidedTarget = target.getLocation();
        } else {
            guidedTarget = intercept(missile.getLocation(), missile.getVelocity().length(), target.getLocation(), target.getVelocity());
        }
        if (guidedTarget == null) {
            // If the target is unreachable, try to lead anyway
            Vector2f projection = new Vector2f(target.getVelocity());
            float scalar = distance / missile.getVelocity().length();
            projection.scale(scalar);
            guidedTarget = Vector2f.add(target.getLocation(), projection, null);
        }

        float angularDistance = MathUtils.getShortestRotation(missile.getFacing(), VectorUtils.getAngle(missile.getLocation(), guidedTarget));
        float absAngD = Math.abs(angularDistance);

        // Turn in the correct direction
        missile.giveCommand(angularDistance < 0 ? ShipCommand.TURN_RIGHT : ShipCommand.TURN_LEFT);

        // Always accelerate
        if (absAngD >= 115) {
            missile.giveCommand(ShipCommand.DECELERATE);
        } else {
            missile.giveCommand(ShipCommand.ACCELERATE);
        }

        // Course correction
        if (absAngD < 5) {
            float MFlightAng = VectorUtils.getAngle(new Vector2f(0, 0), missile.getVelocity());
            float MFlightCC = MathUtils.getShortestRotation(missile.getFacing(), MFlightAng);
            if (Math.abs(MFlightCC) > 20) {
                missile.giveCommand(MFlightCC < 0 ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
            }
        }

        //Damp angular velocity if we're getting close to the target angle
        if (Math.abs(angularDistance) < Math.abs(missile.getAngularVelocity()) * VELOCITY_DAMPING_FACTOR) {
            missile.setAngularVelocity(angularDistance / VELOCITY_DAMPING_FACTOR);
        }
    }

    // Will be in the next LazyLib, courtesy of DarkRevenant
    public static Vector2f intercept(Vector2f point, float speed, Vector2f target, Vector2f targetVel) {
        Vector2f difference = new Vector2f(target.x - point.x, target.y - point.y);

        float a = targetVel.x * targetVel.x + targetVel.y * targetVel.y - speed * speed;
        float b = 2 * (targetVel.x * difference.x + targetVel.y * difference.y);
        float c = difference.x * difference.x + difference.y * difference.y;

        Vector2f solutionSet = quad(a, b, c);

        Vector2f intercept = null;
        if (solutionSet != null) {
            float bestFit = Math.min(solutionSet.x, solutionSet.y);
            if (bestFit < 0) {
                bestFit = Math.max(solutionSet.x, solutionSet.y);
            }
            if (bestFit > 0) {
                intercept = new Vector2f(target.x + targetVel.x * bestFit, target.y + targetVel.y * bestFit);
            }
        }

        return intercept;
    }

    public static Vector2f quad(float a, float b, float c) {
        Vector2f solution = null;
        if (Float.compare(Math.abs(a), 0) == 0) {
            if (Float.compare(Math.abs(b), 0) == 0) {
                solution = (Float.compare(Math.abs(c), 0) == 0) ? new Vector2f(0, 0) : null;
            } else {
                solution = new Vector2f(-c / b, -c / b);
            }
        } else {
            float d = b * b - 4 * a * c;
            if (d >= 0) {
                d = (float) Math.sqrt(d);
                a = 2 * a;
                solution = new Vector2f((-b - d) / a, (-b + d) / a);
            }
        }
        return solution;
    }

    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
}

Additional math can be added for making it smarter in hitting targets that are actively avoiding it, but this serves only to make the missile nearly impossible to avoid.  As it is, this relatively simple guidance method is quite difficult to avoid and can make harpoons hit fighters, etc.
Logged

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: LeadVector
« Reply #8 on: May 08, 2014, 01:48:11 PM »

@Dmaiski:  We don't need time, we just need the angle.  Time is either infinite or it's not, and that has to do with the current velocities of the two entities and their distance; like I said, the only real issue is edge cases, where a low-velocity fleet might have a few frames where an intercept is possible, and should attempt it :)
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #9 on: May 08, 2014, 01:57:30 PM »

@xeno

time*TargetVelocity=location

location1 to location2 = angle

:P your code does this internaly, mine does it externaly :P

@Dark.Revenant
yea... its nasty enough to make harpoons hit... well... other harpoons(wich are chasing fighters) a la PD missiles
Much relevant post with pics:
Spoiler
im re-writing all my scripts from scratch using beter/more accurate/fixed methods

ill put them all in one post


(this is a new script(also the base for all the others))
basic long range point defence missile ai
it can hit a missile with a harpoon 99% of the time at 1000<range effectivness drops as missiles get too close
the primary point of this ai is to make and improve targeting and pathing of a missile
(it has an extreme level of tracking ability and accuracy)
Spoiler
pictures of it in action
Spoiler


yes that is a vanilla harpoon,
red dot(target lead),
green dot(nose of target (part targeting)),
white line(velocity vector of the missile),
streaking on dots indicates distance moved in last second
[close]

LRPDai
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import java.awt.*;
import java.util.*;

//import static jar.CustomAPI.LeadVector;
import static jar.CustomAPI.getEnemyMissilesInArk;
import static org.lwjgl.util.vector.Vector2f.add;

public class LRPDai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    // Our current target (can be null)
    private MissileAPI target;

    public static float searchrange = 500; //500 range default

    public static float Ark = 30;          //60deg ark   default

    boolean newlaunch=true;                //only do mouse target once on a newly launched missile

    public LRPDai(MissileAPI missile)
    {
        this.missile = missile;
        searchrange=missile.getWeapon().getRange();     //based on weapon
        Ark=5+missile.getWeapon().getArc()/4;           //based on weapon

        // Support for 'fire at target by clicking on them' behavior
        if (newlaunch=true)
        {
            newlaunch=false;
            //get targets near mouse
            java.util.List directTargets = CombatUtils.getMissilesWithinRange(
                    missile.getSource().getMouseTarget(), 100f, true);

            if (!directTargets.isEmpty())
            {
                MissileAPI tmp;
                for (Iterator iter = directTargets.iterator(); iter.hasNext(); )
                {
                    //filter out friendlies
                    tmp = (MissileAPI) iter.next();
                    if (tmp.getOwner() != missile.getSource().getOwner())
                    {
                        target = tmp;
                        break;
                    }
                }
            }
        }

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
   
   
    public static MissileAPI findBestTarget(MissileAPI missile)
    {
        //find targets in ark in front of missile
        ArrayList targets = getEnemyMissilesInArk(missile, Ark, searchrange, true);
        if (!targets.isEmpty())
        {   //pick random missile in list     //replace with .get(0) for nearest
            return (MissileAPI) targets.get((int)(targets.size()*Math.random()));
        }                                                                         
        else return null;

    }

    @Override
    public void advance(float amount)
    {
        // Apparently commands still work while fizzling
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }

        //find a target
        if (target == null)
        {
            target = findBestTarget(missile);
        }



        if (target!=null ) //if you have a target
        {
            //aming
            //1step calculations of various stats
            Vector2f MVel = missile.getVelocity();
            Vector2f MLoc = missile.getLocation();
            Vector2f TLoc = target.getLocation();
            Vector2f TVel = target.getVelocity();
            //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
            //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
            float MFace = missile.getFacing();
            double TfaceRad = Math.toRadians(target.getFacing());
            float TCol = target.getCollisionRadius();
            float sx = (float) (TCol*.5*Math.cos(TfaceRad));
            float sy = (float) (TCol*.5*Math.sin(TfaceRad));
            Vector2f TNose = new Vector2f(sx, sy);
            //float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
            //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit

            //testing InterceptPoint
            Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

            //lead target
            Vector2f TLead = add(TLoc,            //target location+
                    Lvec,       //target speed*time to get to target+
                    null);      // if its too long just assume 3 seconds is long enough to course correct



            //aim at nose (can be changed to part targeting with a few tweaks)
            Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

            //main aiming (to find angle you are off target by)
            float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
            float AtTarget = getAngleDifference(  //how far off target you are
                    MFace, AngleToEnemy);         //where missile pointing, where target is in relation

            float AbsAngD = Math.abs(AtTarget);


            //////////////////////////////////////////////////////////////////////////////////////////////


            //point towards target
            if (AbsAngD > 0.5)
            {
                missile.giveCommand(AtTarget > 0
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
            }

            if (AbsAngD < 5)
            {


                //course correct for missile velocity vector (some bugs when severly off target)
                float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                float MFlightCC = getAngleDifference(MFace, MFlightAng);
                if (Math.abs(MFlightCC)>20)
                {
                missile.giveCommand(MFlightCC < 0
                        ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                }

            }

            //stop turning once you are on target (way of the hack, ignores missile limitations)
            if (AbsAngD<0.4)
            {
                missile.setAngularVelocity(0);
            }

            ///////////////////////////////////////////////////////////////////////////////////////////////


            //acceleration code
            if (AbsAngD < 40)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }
            if (MVel.lengthSquared()<TVel.lengthSquared())
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }
            if (AbsAngD>120)        //if you missed stop and turn around
            {
                missile.giveCommand(ShipCommand.DECELERATE);
            }



            /////////////////////////////////////////////////////////////////////////////////////////////////


            //testcode    spawns particle at aim points
            CombatEngineAPI engine = Global.getCombatEngine(); //engine
            engine.addSmoothParticle(TLead, new Vector2f(0,0), 5, 1, 1, new Color(255,10,15,255));
            engine.addSmoothParticle(TNoseLead, new Vector2f(0,0), 5, 1, 1, new Color(93, 255, 40,255));
            engine.addSmoothParticle(MLoc, multV2f(MVel, 4), 5, .5f, 1, new Color(248, 244, 255,255));
        }


        //clear target code if target is gone
        if (target == null // unset
                ||  ( missile.getOwner() == target.getOwner() )        // friendly
                ||  !Global.getCombatEngine().isEntityInPlay(target) ) // completely removed
        {

            target = findBestTarget(missile);
            return;
        }
    }




    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    //multiply vectors //own code
    public static Vector2f multV2f(Vector2f Vector1, float Multiplier)
    {
        float v1x = Vector1.getX()*Multiplier;
        float v1y = Vector1.getY()*Multiplier;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }
    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .00001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
}
[close]
[close]
[close]

@Alex
i actualy made a script wich spawned missiles using this targeting method AND vanilla missile ai at the same time... long story short this method was much more accurate
« Last Edit: May 08, 2014, 02:03:36 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 23988
    • View Profile
Re: LeadVector
« Reply #10 on: May 08, 2014, 02:05:07 PM »

@xenoargh: Just looking at that code, I think it may have some issues when the target is heading towards the chaser. The proper intercept point may end up being *behind* the chaser, causing it to head away from the target. Probably doesn't come into play for projectile weapons, but may for fleets, where you'll necessarily be making assumptions about that value.

LeadVector + TargetLocation = ThePointYouGoTo

Ah, gotcha. The comment about it "getting where T will be" threw me off :) Working much better now. Still gets into orbiting behavior sometimes with low-accel missiles, and, of course, still can reliably be dodged (yes, with a slower ship). Just try it with a Pilum, with you piloting a Lasher. You should be able to dodge it all day. A high-accel missile would perform much better, though.

Anyway, not sure where I'm going with this, except to reiterate my point that acceleration matters a lot, an is in fact the determining factor in whether this approach works or not. The assumption that speed == max speed hinges on the acceleration being high enough.


(Re: vanilla missiles - they do use target leading, they're intentionally not great at it, and the leading can be improved via skills. Dodging missiles is fun. They're also slightly harder to shoot down with PD.)
Logged

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: LeadVector
« Reply #11 on: May 08, 2014, 02:07:46 PM »

Low-angle stuff can always just use the target's location, though; when two bullets are heading at each other, we just need to keep going forward :)
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #12 on: May 08, 2014, 02:13:40 PM »

@Alex

what the lead point looks like on a target with various velocity directions
(outer line is where the LeadPoint will aim you towards)


on dodging, PD, and accuracy:
i used a backwards aproach, i STARTED from perfectly accurate, then I added variables to intentionaly make the AI less accurate as needed into the rest of the code, but its true, undodgeable misiles are alot less fun if you are on the reciving end :P
« Last Edit: May 08, 2014, 02:20:31 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 23988
    • View Profile
Re: LeadVector
« Reply #13 on: May 08, 2014, 02:21:51 PM »

@xenoargh: Yeah, certainly :) Just pointing it out.

@dmaiski: Imagine a missile that takes 10 seconds to get up to top speed. Now, imagine that it did that, and is heading towards a slow ship that can accelerate faster. That ship can dodge to the side, and the missile doesn't have enough acceleration to respond in time. It'll be *trying* to aim at the correct intercept point, it just won't have the acceleration to be able to do that. Like I said, even for Pilums - which take less than two seconds to come to full speed - this target-leading algorithm can reliably produce orbiting behavior.
Logged

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: LeadVector
« Reply #14 on: May 08, 2014, 02:34:22 PM »

actualy if missile can not catch target it will simply get a lead of Vector2f(0,0) - djinking wont have any effect

if it is already faster then the target, it will allways be aimed at the fastest route to the target - djinking will have no effect


for example, lets say missile is 2000AU away, and target is traveling at speed N in a perpendicular vector

the angle the missile holds is 20deg from DirectLineToTarget (ie. its lead point)

target does a 180deg turn
the missile only needs to do a 40deg turn to stay on target

the missile looses absolutley no speed in this manuver (asuming its acceleration is non-negligable compared to its max speed)
the target looses all its speed in this manuver

effectively by djinking you actualy make it MORE likley that the missile will hit you, not less
(:P as i said earlier evasive manuvers only work in the realms of movie magic, large turn arks, and bad targeting AI :P)
« Last Edit: May 08, 2014, 02:37:09 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!
Pages: [1] 2