Faking isometric graphics in a 2D space game

First off, I know exactly what my drawing problem is, and I have various ideas on how to approach solving it. I am here to figure out how to adjust which frame is drawn so that the isometric "illusion" is maintained.

I am writing a 2D, birds-eye view game that takes place in space. I am trying to use isometric graphics in a world where Up is North, no rotating of the game world like in traditional isometric games (remember, the game takes place in space, no terrain). Here is an example sprite I am trying to use: http://cell.stat-life.com/images/stories/AsteroidOutpost/Spaceship64.png Rotated 64 times about it's own vertical axis with a viewing angle of 35 degrees (aka, iso). The image was generated, so this can be changed.

For the sake of clarity, I have dictated that North (Up) is 0 degrees, and East (Right) is 90.

My problem is that my sprite doesn't always look like it's facing where the game thinks it is due to a difference in the 3D planes used. Not sure if I'm using the terminology correctly, but my spaceship was rotated on one plane, and the view plane is expecting things to be rotated on its own plane. Here's a depiction of the problem:

On the left, I have side views, on the right I have top-down views. On the top, I have the space ship/sprite sheet view of the world. On the bottom, I have what the sprite looks like when it is drawn to the screen.

In the top right is a simplified version of how my spaceship was rotated (even degree separations between each frame). In the bottom right is the angle that the sprite looks like it's facing when a particular angle is drawn on the screen. The problem is most evident at around 45 degrees. Here is an image that is overlaid with a "screen plane" line that is pointed in the direction that the ship is supposed to be facing: http://cell.stat-life.com/images/stories/AsteroidOutpost/Spaceship64RedLine.png The red line should always be pointed in the exact same direction as the ship, but as you can see, the projection issue is causing a problem. I hope I am describing the issue well enough.

EDIT: The red line is rotated on the screen plane, while the space ship is rotated on the "ship plane", hence the large difference between the ship angle and the screen angle when it is drawn. END EDIT

It looks rather odd when this ship is shooting a laser beam at a target that is off to one side, when it should be shooting straight ahead.

So... the solutions that I have come up with are:

1. Stop trying to fake an isometric view, go big or go home. Rotate my world already. (PS: this will fix my problem, right?)
2. Alter my sprite sheet (somehow) to have frames that represent the "screen angle" that the model will be viewed at. So when I ask for a 45 degree image, it will actually look like it's facing 45 degrees on the screen.
3. Alter my sprite rendering system to grab a different frame from the sprite sheet (somehow), knowing that grabbing the "normal" frame will look funny. So instead of grabbing the 45 degree frame, it will grab the ... 33 degree frame (just guessing) so that it looks correct to the user.
4. Just use 3D! It's so much easier man

For one: Which solution would you recommend? I'm leaning toward 2, even though I know it's wrong. Remember that I have full access to the sprite generation tool. Method 3 would be my second choice. Both of these methods simply require that I apply some math to the angle(s) to get my desired result. Btw, I added #4 in there as a joke, I don't want to move to 3D.

For two: Using methods 2 or 3, how would I adjust the input or output angles? I'm not all that good with 3D projections, and 3D matrices (let alone 2D matrices), and whatever other math might be used to achieve me desired result.

Thinking out loud here: if I take a unit vector facing in the direction that I want the ship to look (on the screen plane), then project that onto the ship plane, then figure out what the angle between that projected line and the plane's north is, I should have the angle of the ship graphic that I want to display. Right? (solution for #3?) Man... I can't work that out.

EDIT:

I know the problem is hard to visualize with static images, so I have complied my Sprite Library Visual Tester to display the issue: http://cell.stat-life.com/downloads/SpriteLibVisualTest.zip It requires XNA 4.0 This application is simply rotating the sprite, and drawing a line from its centre point outward at the angle the game thinks the ship should be facing.

EDIT Sept 8

Continuing from my "thinking out loud" paragraph: Instead of using a projection (because it looks hard) I'm thinking I could take a north facing unit vector (0x,1y,0z), use a matrix to rotate it to the angle the sprite is actually facing, then rotate it again from the "viewing plane" outward to the "sprite plane" around the X axis, then... flatten? the vector (essentially project it) back onto the viewing plane by only using the X and Y (ignoring Z). Then using that new flattened vector, I could determine its angle and use that to... something something. I'll think more later.

EDIT Sept 8 (2)

I tried following my idea from above with some success, yay! So... to get a red line to look like it is coming straight out of my spaceship (testing purposes only), I did the following:

``````Vector3 vect = new Vector3(0, 1, 0);
vect = Vector3.Transform(vect, Matrix.CreateRotationZ(rotation));
vect = Vector3.Transform(vect, Matrix.CreateRotationY((float)((Math.PI / 2) - MathHelper.ToRadians(AngleFromHorizon))));
Vector2 flattenedVect = new Vector2(vect.X, vect.Y);

float adjustedRotation = (float)(getAngle(flattenedVect.X, flattenedVect.Y));
``````

Where `rotation` is the screen angle and `adjustedRotation` is now that screen angle looks on the sprite plane. I don't know why I needed to rotate Y instead of X, but it's probably something to do with 3D that I don't know about.

So, now I have an additional piece of information, but how do I use this to pick a more appropriate frame? Maybe instead of rotating from the view plane into the sprite plane, then projecting back, I should try to do it the other way around. Here's my sprite with an adjusted red line, but what I really want to do is to adjust the model, not the line: http://cell.stat-life.com/images/stories/AsteroidOutpost/Spaceship64RedLineCorrected.png

EDIT Sept 9

HORAY! It's fixed! I will describe what I did to fix it:

I followed eBusiness's advice and "squished" by world coordinate system (or stretched...?). This is essentially solution #1, although I was under the impression that I would have to rotate my world coordinate system in relation to the screen coord system. Not true! Up is still +y, and Right is still +x. I modified my WorldToScreen and ScreenToWorld methods to properly convert between the world and screen coordinates. These methods may not be optimal, but here are the only two methods in my codebase that had to change.

``````public Vector2 ScreenToWorld(float x, float y)
{
float deltaX = x - (size.Width / 2f);
float deltaY = y - (size.Height / 2f);

deltaX = deltaX * scaleFactor / (float)Math.Sqrt(3);
deltaY = deltaY * scaleFactor;

return new Vector2(focusWorldPoint.X + deltaX, focusWorldPoint.Y + deltaY);
}

public Vector2 WorldToScreen(float x, float y)
{
float deltaX = x - focusWorldPoint.X;
float deltaY = y - focusWorldPoint.Y;

deltaX = deltaX / scaleFactor * (float)Math.Sqrt(3);
deltaY = deltaY / scaleFactor;

return new Vector2(size.Width / 2f + deltaX, size.Height / 2f + deltaY);
}
``````

That was it! Changing those two methods affected everything else: what I was looking at, where the objects where drawn, where I was clicking, everything. My spaceship now looks directly at the target it is shooting, and everything is falling into place. I will be experimenting with the orthographic projection, see if it makes everything seem more 'real'.

-
Congratulations. Don't forget to poke me once you have something playable, I'd like to see where this goes. – aaaaaaaaaaaa Sep 10 '11 at 5:51
@eBusiness As promised: cell.stat-life.com/images/stories/AsteroidOutpost/… and cell.stat-life.com/images/stories/AsteroidOutpost/AOOhNoes.png Notice how the ship looks like it's looking at that tower? :D Thanks! – John McDonald Sep 12 '11 at 3:16
@eBusiness, A video youtube.com/watch?v=Q8pR9KDBsCo There's also a link to download, since you asked. – John McDonald Feb 22 '12 at 22:14

3 Answers

When you have chosen isometric perspective you need to correct the scale between the X and the Y-axis. If a given distance projects to 1 on the screens Y-axis then the same distance will project to sqrt(3) on the screens X-axis. So if you draw stuff flat on the screen you'd do something like:

``````draw(object.image, object.x*sqrt(3), object.y)
``````

Non-answer notes:

• Why would you mess with sprite sheets and stuff? You can get a 3D renderer to do isometric if you want.
• Since you are using sprite sheets, why ain't they anti-aliased?
• Isometric in space, I don't think I have ever seen a game use that, I'm not sure how well it will work given the lack of a clearly isometric ground for visual reference.
-
I think... This could fix everything. It's an easy fix, and I can leave the sheet alone. I will try this out asap and let you know how it goes. To answer your notes: 1) I am using 2D because it's what I am familiar with, and I am generating my sheets from 3D models. 2) The edges should be sharp, but inside the hard edges I could use the alpha channel to anti-alias, that will come. 3) I don't plan to simulate the third dimension (much), I mainly want 2D graphics that are not top-down and allow you to see the sides of things. – John McDonald Sep 9 '11 at 15:16

How is the rotated sprite generated? Are these screenshots of a rotating 3D model? The facing angle could be off due to the camera perspective of the 3D modeling tool. Isometric perspective is not an accurate representation of the 3D space. Stuff far away from the 'camera' does not get smaller.

If you just want a quick fix. Solution 2 might be difficult to get right. Solution 3 seems easy just swap the frames not matching the angle with one that fits the angle better.

The lasers will still be off sometimes. As you are not doing a smooth rotation but showing different frames for certain degree ranges.

EDIT:

Your problem is that the generated screenshots are not isometric perspective, but a normal 3D camera view.

Isometric view simulates 3D space but is not an accurate representation. Things get smaller with increasing distance from the camera. This is not done for isometric perspective as this would cause ugly scaling artifacts in the sprite.

Your space ship screenshots are made with 3D camera perspective. Thats why the laser shooting is off. Compare the red line in the 'isometric' and the 'perspective'.

Your sprite generator should use Matrix.CreateOrthographic() instead of Matrix.CreatePerspective() for the projection matrix.

-
The sprite is generated using a 3D to 2D tool that I am developing, so the problem could very well be in that tool. The tool simply loads a 3D model, then to achieve Spaceship64.png (above), it rotates the model by (360 / 64) = 5.625 degrees, saves that as a frame, then rotates it again by the same amount until it has all 64 frames. The code used to render the model is here: code.google.com/p/sprite-maker/source/browse/trunk/XNAWindow/… Rotating is done in an other class. I was also thinking that solution 2 and 3 were the inverse of one another, no? – John McDonald Sep 8 '11 at 14:06
Edited my answer. See above. – Stephen Sep 9 '11 at 20:06
I think I might need to do both: make my sprite generator use an orthographic projection AND alter my world coordinate system like what @eBusiness is saying. I tried the Orthographic projection last night, and it certainly changed how my ship looked, but it alone did not fix the angle problem I am experiencing. – John McDonald Sep 9 '11 at 20:29
Just saw your edit. The corrected sprite looks like the center of the ship is off from the center of the red line. Maybe it's enough to draw the ship one or two pixels higher then the starting point of the red line. Does the visible error change while the ship moving away from your world origin? Then scaling the X-Axis might fix this. – Stephen Sep 9 '11 at 21:00

I think what you've got here is simply how isometric looks. You're right in that, visually speaking, there appears to be a larger difference between 0deg and 10deg than between 90deg and 100deg . . . but that's because isometric intrinsically compresses one axis. If you don't want that look, you don't want isometric.

That said, I think the visual issues you're having are thanks to not having a visual reference point. Your brain is assuming you're looking at it top-down - hey, it's space, why wouldn't you be - and so is interpreting it as a standard top-down view. For the sake of testing, try adding some floating holographic reference points. A 45-degree angled grid, for example, or a set of range circles around your spacecraft. Make sure they're appropriately distorted as if they were on the same isometric plane your spaceship is rendered on. I'd wager that your brain will figure out the perspective and it will suddenly look right.

Now, figuring out how to get that across to your users . . . that's another matter altogether.

-
Won't I also need to rotate the world axis to complete the illusion using this method (method #1 in the question)? – John McDonald Sep 8 '11 at 4:18
As near as I can tell, the ship is already being rendered correctly - the side views show it at the angle it should be for isometric to look "correct". I suppose I'm not sure what you mean by "rotate the world axis". – ZorbaTHut Sep 8 '11 at 10:54
By "rotate the world axis", I mean rotate the world in relation to the camera by 45 degrees to make "North" be toward the top-right or top-left of the screen at a diagonal instead of "North" being straight upward toward the top of the screen. – John McDonald Sep 8 '11 at 13:58
– John McDonald Sep 8 '11 at 14:28
That might help, but in space it shouldn't make all that much of a difference anyway. Unless you have a grid overlaid on the world it just shouldn't matter. – ZorbaTHut Sep 9 '11 at 4:28