2

I have a player who can rotate and move around a 2D Cartesian grid, I need to calculate where to draw the enemies on screen.

The player should have a certain viewpoint which is the size of the screen in front of the direction the player is facing. (and a little behind)

I've tried tons of ways to implement this messing with Bi-Polar co-ordinates and Trig but I havn't been able to solve the problem of calculating where on the screen the enemies should be drawn.

The problem is best represent in the form of a graph with green being the viewpoint which is a rectangle that can rotate and move around the grid, and dots representing player and enemy.

Blue is player, Red is Enemy Green represents viewpoint

So I need to work out the positions of the enemies on screen relative to the players rotation and position.

Joel
  • 587
  • 1
  • 5
  • 17

1 Answers1

2

If you're going for a Doom-like perspective, you should imagine the viewing area as a parallelogram, rather than a rectangle. Imagine that behind your character is a camera man with its own position and angle.

The camera frustum

The enemy's screen position is related to the angle between the camera and the enemy.

//indicates where on the screen an enemy should be drawn.
//-1 represents the leftmost part of the screen, 
//and 1 is the rightmost.
//Anything larger or smaller is off the edge of the screen and should not be drawn.
float calculateXPosition(camera, enemy){
    //the camera man can see anything 30 degrees to the left or right of its line of sight. 
    //This number is arbitrary; adjust to your own tastes.
    frustumWidth = 60;

    //the angle between the enemy and the camera, in relation to the x axis.
    angle = atan2(enemy.y - camera.y, enemy.x - camera.x);

    //the angle of the enemy, in relation to the camera's line of sight. If the enemy is on-camera, this should be less than frustumWidth/2.
    objectiveAngle = camera.angle - angle;

    //scale down from [-frustumWidth/2, frustumWidth/2] to [-1, 1]
    return objectiveAngle / (frustrumWidth / 2);
}

These diagrams visualize what the variables I'm using here represent:

depiction of <code>angle</code>

enter image description here

Once you have an "X position" in the range of [-1, 1], it should be easy enough to convert that into pixel coordinates. For example, if your screen is 500 pixels wide, you can do something like ((calculateXPosition(camera, enemy) + 1) / 2) * 500;

Edit:

You can do something similar to find the y-coordinate of a point, based on the point's height and distance from the camera. (I'm not sure how you should define the height of the enemy and camera - any number should be fine as long as they somewhat match the scale set by the x and y dimensions of the cartesian grid.)

side view - camera looking at enemy

//this gives you a number between -1 and 1, just as calculateXPosition does.
//-1 is the bottom of the screen, 1 is the top.
float getYPosition(pointHeight, cameraHeight, distanceFromCamera){
    frustrumWidth = 60;
    relativeHeight = pointHeight - cameraHeight;
    angle = atan2(relativeHeight, distanceFromCamera);
    return angle / (frustrumWidth / 2);
}

enter image description here

You can call the method twice to determine the y position of both the top and the bottom of the enemy:

distanceFromCamera = sqrt((enemy.x - camera.x)^2 + (enemy.y - camera.y)^2);
topBoundary = convertToPixels(getYPosition(enemy.height, camera.height, distanceFromCamera));
bottomBoundary = convertToPixels(getYPosition(0, camera.height, distanceFromCamera));

That should give you enough information to properly scale and position the enemy's sprite.

(aside: the frustrumWidths in the two methods don't need to be the same - in fact, they should be different if the screen you are drawing to is rectangular. The ratios of the x frustrum and y frustrum should be equal to the ratios of the width and height of the screen.)

Kevin
  • 74,910
  • 12
  • 133
  • 166
  • That is a truly incredible answer, I certainly wouldn't have arrived at anything like that myself, so thank you very much. I'll get to work implementing that now – Joel Feb 28 '12 at 14:12
  • I've managed to get the X from this but i'm struggling with also getting the Y – Joel Feb 28 '12 at 20:18
  • @Joel, I've added some information about calculating Y coordinates. – Kevin Feb 28 '12 at 21:29