views:

96

answers:

2

OK, so I have a play field of 512x512 which wraps around, -32 becomes 512 for both x and y.

Now I need to calculate the angle between two entities, I have the following code as a kind of workaround, it works most of the time, but sometimes it still fails:

Shooter.getAngle = function(a, b) {
    var ax = a.x;
    var bx = b.x;
    if (a.x < this.width / 4 && b.x > this.width - this.width / 4) {
        ax += this.width;

    } else if (a.x > this.width - this.width / 4 && b.x < this.width / 4) {
        bx += this.width;
    }

    var ay = a.y;
    var by = b.y;
    if (a.y < this.height / 4 && b.x > this.height - this.height / 4) {
        ay += this.height;

    } else if (a.y > this.height - this.height / 4 && b.y < this.height / 4) {
        by += this.height;
    }
    return this.wrapAngle(Math.atan2(ax - bx, ay - by) + Math.PI);
};

I just can't figure out how to project a onto b so that the wrapping is accounted for correctly.

If anyone could help, it would be great, since homing missiles that fly away from their target aren't that fun.

For clarification, if the player is moving right, the missile should follow him over the edge of the field to the left side, just as another player would do.

EDIT:

Here's a test version, the examples show that it wraps correctly but in cases were it doesn't have to wrap it breaks:

function getAngle(a, b) {
    var tx = a.x - b.x;
    var ty = a.y - b.y;

    // actual area goes from -16 to 512 due to the border space that's out of screen
    tx = ((tx + 16) % (480 + 32)) - 16;
    ty = ((ty + 16) % (480 + 32)) - 16;
    var r = Math.atan2(ty, tx) * (180 / Math.PI);

    // example
    // |> b                    a >|
    // a.x = 460
    // b.x = 60

    // missile should go over the right border
    // tx = 400 
    // r = 0
    // missile goes right, OK

    // example2
    // |< a                    b <|
    // a.x = 60
    // b.x = 460

    // missile should go over the left border
    // tx = -400 
    // r = 180
    // missile goes left, OK


    // example3
    // |     a  >>>>  b                  |
    // a.x = 60
    // b.x = 280

    // missile should go right
    // tx = -220
    // r = 180
    // missile goes left, WRONG

    // example4
    // |     b  <<<<  a                  |
    // a.x = 280
    // b.x = 60

    // missile should go left
    // tx = 220
    // r = 0
    // missile goes right, WRONG

    console.log(ty, tx);
    console.log(r);
}

function Point(x, y) {
    this.x = x;
    this.y = y;
}

getAngle(new Point(460, 240), new Point(60, 240));
getAngle(new Point(60, 240), new Point(460, 240));
getAngle(new Point(60, 240), new Point(280, 240));
getAngle(new Point(280, 240), new Point(60, 240));

It seems the only way to getting it work is by checking for cases when a.x < width * 0.25 and b.x > width * 0.75 etc. but that's buggy, at least in the version I posted above :/

+5  A: 

I'd use this approach: Use the target as the center of your coordinate system (and wrap the missile's coordinates properly), then calculate the angle. So, pseudo-code should be this (EDIT2: Corrected code, a similar problem also led to the problems in your tests):

// missile.x/y = missile's coordinates
// target.x/y = target's coordinates
temp.x = missile.x - target.x;
temp.y = missile.y - target.y;

// The wrapping code - feel free to adjust,
// from the comments it seems you handle this
// a bit differently
while (temp.x < -240)
  temp.x += 480
while (temp.y < -240)
  temp.y += 480
while (temp.x > 240)
  temp.x -= 480
while (temp.y > 240)
  temp.y -= 480

// Now you can calculate the angle your missile must go,
// it has to fly across the line from temp.x/y to (0,0)

EDIT: I've seen the video and I think there's some error in your code, but I'm not sure where. Using mathematical functions and angles can be a bit tricky because usually math coordinates have the Y axis going up, screen coordinates have the Y axis going down.

Best way now would perhaps be to start some units test, something like this:

// Create a function that returns the angle a missile must
// fly and cares about all the dirty details with wrapping etc.
MissileAngle(missile.x, missile.y, target.x, target.y)
// Now create some tests for it, f.e.:
MissileAngle(missile top left corner, target bottom right corner)
  Expected Result: Northwest
MissileAngle(missile bottom right corner, target top left corner)
  Expected Result: Southeast

Note I didn't use coordinates here because it depends heavily on the coordinate system you're using and this might actually help you to get a better overall feeling for the problem (and of course, hopefully helps you to solve the bug, at least it's a fast way to check if it's working correctly without having to play the actual game, which is more time consuming).

schnaader
Tried this before, but there's still one problem. When `missle.x` / `missle.y` `<` `target.x` / `target.y` the missile turns away.
Ivo Wetzel
Update here's a vid of how it looks right now: http://www.youtube.com/watch?v=Nl6gwMKrZxg (the border is a bit hard to see...)
Ivo Wetzel
In this case, I guess there's something wrong with your angle calculation. The code in your question, however, seems fine, `atan2(dx, dy)` and adding Pi (180°) to rotate the missile towards the target. So it might be `wrapAngle`, what is this doing?
schnaader
wrapAngle just wraps everything thats above `Math.PI` back to `-Math.PI`.
Ivo Wetzel
Hm.. seems fine, too. By the way, you can eliminate the additional `wrapAngle` and adding 180° calculation by just switching target and missile: `atan2(bx - ax, by - ay)` should do just the same as `wrapAngle(atan2(ax - bx, ay - by) + Math.Pi)`, only faster and with less space for those annoying bugs :)
schnaader
Another interesting thing I just noticed when watching the video again: at the beginning, the red ship is in the top left and shoots two missiles that are using the correct way along the X axis (to the right), but are wrong along the Y axis (going up), after these two, the ship rotates a bit and fires again, but this time Y-way is right (down) and X-way is wrong (to the left). So there seems to be some additional factor to (missile.x/y < target.x/y) like the initial angle of the ship.
schnaader
Updated my question with some example results of schnaader's version.
Ivo Wetzel
The updated code works perfectly, maybe I should have drawn the whole thing out on actual paper instead of just thinking about it in my head, anyways, thank you very much :)
Ivo Wetzel
A: 

Just to make the obvious suggestion: shouldn't it be Math.atan2(ay-by,ax-bx)? Apologies if that was just a typo in your post, but I can certainly see that causing bizarre behaviour in your missiles.

Edited to add: never mind, I just saw your comment.

Peter Milley