Bacon and Games

Calculating the Distance from a Point to a Line in AS3

I recently made a game for a client that involved a giant sweeping laser of doom (the game had the laser, not the client). Sounds cool, right? Not if the laser happened to touch you while you were busy blowing up robots. Sound even cooler now that you know there were robots? It was. But that’s not what I want to talk about today. What I’d like to cover is the slightly more exciting topic of how to calculate the distance between a point and a line.

I won’t go into the math behind it, not because it’s complicated but because it’s just not that interesting or important to understand. I’ll just give you the code and then talk a little bit about why it might be useful in a game. If you really want to know how the math works, leave a comment or email me and I’ll do a follow up. For now, here’s a simple visualization (download source here):

This movie requires Flash Player 9

Once again I’ve built on a handy function written by Keith Hair that finds the point of intersection between two lines. Using his lineIntersectLine() function and my getDistanceFromLine() function you can calculate the distance between a point and a line very easily.

Why is this useful? I wrote it so that I could determine if the doom laser had overtaken my hero. Since the laser was represented in my code by two points (a line) and I knew the location of the hero (a point) all I had to do was calculate the hero’s proximity to that line. When the hero’s proximity was less than a certain distance >> kill hero. And because this calculation was done mathematically rather than being tied to a hitTestObject() based on the laser’s movieClip1, adjusting the laser’s killzone was as simple as changing one variable.

Here’s another example of how this information might be useful in a game. Suppose your game has a laser cannon which fires in a straight line, as opposed to the sweeping radar motion in my prior example. If you know each enemy’s proximity to the laser you can apply varying damage and effects to each enemy based on that information.

There are plenty of ways you could use this in a game, but I’ll leave that up to your creativity. You can download the source files for the above demo here. Oh and as is often the case with art in or around my site, the bad-ass laser and art in the screen grab above was done by my partner in crime, Ajay Karat of The Devil’s Garage. He and I are working on some games (not for clients) so expect to see some stuff from us in 2010.

  1. When designing your games you’ll want to keep your visual assets separate from your calculations. Let your calculations dictate what needs to be displayed, not the other way around. Tying calculations to visual assets is usually, if not always, inefficient and inflexible.
Tweet about this on TwitterShare on TumblrShare on FacebookShare on Google+

Categories: Code & Resources

Flash Gaming Summit 2010 » « Home Sheep Home

9 Comments

  1. you made my day! thank youuuuuuuuuuuuuuuuuu!!!
    very usefullllllllllllllll

  2. Thanks for this!

  3. Hi,

    I was struggling to make your code work, so I went back to searching the internet, and found this much shorter function that does the same:

    public function PointToLineDistance(p1:Point, p2:Point, p3:Point):Object
    {
    var xDelta:Number = p2.x – p1.x;
    var yDelta:Number = p2.y – p1.y;
    if ((xDelta == 0) && (yDelta == 0))
    {
    // p1 and p2 cannot be the same point
    p2.x += 1;
    p2.y += 1;
    xDelta = 1;
    yDelta = 1;
    }
    var u:Number = ((p3.x – p1.x) * xDelta + (p3.y – p1.y) * yDelta) / (xDelta * xDelta + yDelta * yDelta);
    var closestPoint:Point;
    var obj:Object = new Object;

    if (u 1)
    {
    obj.dist = -1;
    }
    else
    {
    closestPoint = new Point(p1.x + u * xDelta, p1.y + u * yDelta);
    obj.dist = Point.distance(closestPoint, new Point(p3.x, p3.y));
    obj.closest = closestPoint;
    }
    return obj;
    }

  4. Sean James McKenzie

    14.12.2011 — 3:55 pm

    Thanks for the comment. This function is slightly different, as it does not allow you to extend the segment indefinitely, if you for example wanted to know if the point was not off of the line but the infinite line your segment lies on. If you don’t need that, then the above function is certainly leaner.

  5. Hey!
    There’s a problem when the slope is perfectly vertical or horizontal.
    Here’s one way to catch it and get usable results:

    public function getDistanceFromLine(a:Point,b:Point,c:Point,as_seg:Boolean=false):Object{
    c.y *= -1; // flip for Flash
    var obj:Object = new Object();
    obj.dist = -1;
    obj.pt = new Point();
    var m:Number = (a.y – b.y) / (a.x – b.x);
    var B:Number = (1 / m) * c.x – c.y;
    var d:Point;
    var e:Point;
    if (m == Infinity || m == -Infinity)
    {
    d = new Point(c.x+1, c.y);
    e = new Point(c.x-1, c.y);
    }else if (B == Infinity || B == -Infinity)
    {
    d = new Point(c.x, c.y + 1);
    e = new Point(c.x, c.y – 1);
    }else
    {
    d = new Point(b.x, b.x * ( -1 / m) + B);
    e = new Point(a.x, a.x * ( -1 / m) + B);
    }
    var poi:Point = lineIntersectLine(a, b, d, e, as_seg); // use Keith’s function to check for an intersection
    if(poi != null){
    var dx:Number = poi.x – c.x;
    var dy:Number = poi.y + c.y;
    obj.dist = Math.pow(dx * dx + dy * dy, .5);
    obj.poi = poi;
    }
    return obj;
    }

    Thanks for sharing!

  6. Sean James McKenzie

    23.01.2012 — 7:38 pm

    I can’t believe I missed that. I will update my code. Nice catch :)

  7. LOL.
    I’m sorry but the fix I posted is bollocks.
    I ran it on my full data set and discovering this only after posting
    Here’s a method that works :)

    public function getDistanceFromLine(a:Point,b:Point,c:Point,as_seg:Boolean=false):Object{
    var obj:Object = new Object();
    obj.dist = -1;
    obj.poi = new Point();
    if (a.x == b.x) {
    obj.dist = Math.abs(a.x – c.x);
    obj.poi.x = a.x;
    obj.poi.y = c.y;
    return obj;
    }else if (a.y == b.y) {
    obj.dist = Math.abs(a.y – c.y);
    obj.poi.x = c.x;
    obj.poi.y = a.y;
    return obj;
    }
    var m:Number = (a.y – b.y) / (a.x – b.x);
    var B:Number = (1 / m) * c.x – c.y;
    var d:Point = new Point(b.x, b.x * ( -1 / m) + B);
    var e:Point = new Point(a.x, a.x * ( -1 / m) + B);
    var poi:Point = lineIntersectLine(a, b, d, e, as_seg); // use Keith’s function to check for an intersection
    if(poi != null){
    var dx:Number = poi.x – c.x;
    var dy:Number = poi.y + c.y;
    obj.dist = Math.pow(dx * dx + dy * dy, .5);
    obj.poi = poi;
    }
    return obj;
    }

    Thanks again

  8. I’m sorry to spam your board but it turns out my second revision doesn’t work when it’s away from the origin (0,0).

    I finally have a version that gives me the answer I need in all cases :)
    Note that I removed the line that flips y and simplified B, d, and e.

    public function getDistanceFromLine(a:Point, b:Point, c:Point, as_seg:Boolean = false):Object {
    var obj:Object = new Object();
    obj.dist = -1;
    obj.poi = new Point();
    if (a.x == b.x) {
    obj.dist = Math.abs(a.x – c.x);
    obj.poi.x = a.x;
    obj.poi.y = c.y;
    return obj;
    }else if (a.y == b.y) {
    obj.dist = Math.abs(a.y – c.y);
    obj.poi.x = c.x;
    obj.poi.y = a.y;
    return obj;
    }
    var m:Number = (a.y – b.y) / (a.x – b.x);
    var B:Number = 1 / m;
    var d:Point = new Point(c.x + m, c.y);
    var e:Point = new Point(c.x, c.y + B);
    var poi:Point = lineIntersectLine(a, b, d, e, as_seg); // use Keith’s function to check for an intersection
    if(poi != null){
    var dx:Number = poi.x – c.x;
    var dy:Number = poi.y – c.y;
    obj.dist = Math.pow(dx * dx + dy * dy, .5);
    obj.poi = poi;
    }
    return obj;
    }

    Cheers,

    Tomasz

  9. I came across this and found it really useful, but then I realized there was a much shorter way using basic math…but I’m just confused as to why everyone seems to be preferring these long methods. The code below gets the distance assuming you want the line to extend indefinitely.

    function GetDistanceLP(p:Point,m:Number,c:Number){
    var X:Number = p.x; var Y:Number = p.y;
    var newC:Number = (Y + (1/m) * X);
    var newM:Number = (-1/m);
    var Px:Number = (-c + newC) / (m – newM);
    var Py:Number = (m) * Px + c;

    var distance:Number = Math.sqrt(Math.pow(Px – X,2) + Math.pow(Py – Y,2));
    return distance;
    }

    p is the point you’re checking, and m and c are the gradient and y intercept of the line.

Leave a Reply

Your email address will not be published.

Copyright © 2024 Bacon and Games

Theme by Anders NorenUp ↑