Jump to content
  • Advertisement
Sign in to follow this  
Danie Clawson

Creating an isometric camera system

This topic is 1401 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Okay I have a terrain with the structure: terrain[x][y][z], and

I want to create an isometric camera system, using a grid approach.

 

Each element is assumed to be a cube, and it's default value will be 0 for air (1 for dirt, 2 for water, etc). This will allow for terrain of multiple heights, and also overhangs/caves. Player x,y,z should be converted to the exact center of the screen. This means there is sort of a hypothetical flat "view grid", with copies extending up and down to max and min elevation, respectively. I think this should work fine. It will allow me to use tricks like testing if there is "dirt" at z+1 to see if I need to render the top face of the current cube.

 

So that being said,

What is the best way to establish this base "view grid?"

 

Some things to keep in mind:

  1. Screen size will does not have a set ratio
  2. Cube size does have a set ratio
  3. The player is not limited to cube-size movement increments, but instead can have an effective location of ex. terrain[0][0.1][0.5].

Therefore:

  1. The cubes will not always fill the screen exactly, and will need to be padded.
  2. This padding will need to be accounted for with screen offsets.
  3. The cubes will need to be offset by an additional small amount: the player's distance from the center of the closest one.

I need to know the basic math, or a standard practical approach to this. Any help or thoughts would be greatly appreciated!

 

 

For clarification, I need the methods of getting the positions of the grey tiles in this image:

[attachment=24167:ISOEXDRAW.png]

 

Notice how the player (blue) is off center to the tile, but is still center in the screen. These are hypothetical locations which are to be translated into world coordinates.

Share this post


Link to post
Share on other sites
Advertisement

Is this meant to be a 2d game, or a 3d game rendered in "isometric" orthographic projection?

 

You need to divide whatever screen size you're in by the isometric tile width and height, and then add one extra tile for your minimum width and height (if you aren't rounding up from a half or less). 

 

This will only very rarely overdraw one tile (whenever you're exactly in the center, and not off by even one pixel), but this won't hurt your performance meaningfully.  If you want to stop even that, though, you can test to see if the character position is exactly in the center of the tile (horizontally or vertically) and then draw one less tile in that case.

 

You will need the starting coordinate, for one of the top corner tiles if you're drawing in 2d, to get started.  This is based on the player character's position relative to the tile it is on.  When it passes the center (vertically or horizontally) the vertical or horizontal position iterates to the next tile and jumps accordingly.

 

However, this will not completely solve your draw problem if you have height variations in tiles.  Do you?  If so, you'll need to do test each tile for visibility to your camera plane based on its offset.  If you do this in 3d, your frustum will pretty much handle it for you, and tell you which you need to draw or not based on their world positions.

Share this post


Link to post
Share on other sites

Well I'm honestly not sure of the technically correct answer to that, let me try to explain better. Of course as I showed I have some 3D information in there, the separate layers of terrain. It's basically voxels. But it will all be represented by sprite blitting. No meshes. 

 

 

 

You need to divide whatever screen size you're in by the isometric tile width and height, and then add one extra tile for your minimum width and height (if you aren't rounding up from a half or less). 

 

Not sure what you mean here, but I do get this part:

 

 

You will need the starting coordinate, for one of the top corner tiles if you're drawing in 2d, to get started.  This is based on the player character's position relative to the tile it is on.  

Though again I got lost on what happens when it passes the center of the tile?

 

As far as height variation, as I said, terrain data is stored as a 3d array like terrain[x][y][z] with each element being basically either: on and having a material type, or off and just being "open air", and all being same sized cubes. Like minecraft, but again, rendered in isometric perspective with sprite blitting. Thanks for trying to help I'm sorry if I seem really dense!

 

Edit: Trying to clarify a little further, here.  This first image is a good example of the geometry-side of it but I never want the player to see world edges like that (the world will be toroidally wrapped so if you walk to the top it starts drawing the bottom - already worked that out in top-down view).

86446_4084.png

Here's a more style-appropriate image showing exactly the kind of output I'm looking for but it doesn't highlight the "voxel-like" nature of the data as much.

gnom-gldf-01.jpg
 

Edited by noxabellus

Share this post


Link to post
Share on other sites

OK, well, start with drawing a flat terrain, because dealing with the height differences will be more complicated.  I'll try to help you with that after you get the flat bit done, OK? smile.png

 

Tiles across = ( Screen Width / tile width [if any remainder at all, round up, even from .1]) + 1

 

Same for tiles, vertically.

 

If the character is in the exact center of a tile, and you have an odd number of total tiles, your starting point is:

 

( Tiles in row x tile width ) / 2  left of the screen center, and ( Tiles in column x tile height ) / 2 above the screen center.

 

Then you just need to iterate diagonally through your tile array, stepping over a tile width to draw each one, until you reach the row number.

Then jump back to the start, jump down HALF a tile height, and jump over (right or left, doesn't matter) HALF a tile width, and draw your next row.

Then back to the start of that row, jump down half, and over the opposite direction from before half a tile (if you jumped left before, jump right this time), and draw the next row.

Keep drawing until you've reached the number of rows dictated by the screen height, as mentioned before.

 

 

Post some screenshots when you get that, then I'll tell you how to move them with the character, then I'll tell you how to deal with height.

Edited by StarMire

Share this post


Link to post
Share on other sites

I'm a newbie, but I had a thought: enlarge your viewing frustrum by one tile in every direction, that is, shift the left, right, top, and bottom clipping planes out by one tile's width; then represent a tile by a point at its center; do culling; depth sort; and draw the tiles' sprites with billboarding. Best of luck, will be lurking in thread :).

Share this post


Link to post
Share on other sites

I'm a newbie, but I had a thought: enlarge your viewing frustrum by one tile in every direction, that is, shift the left, right, top, and bottom clipping planes out by one tile's width; then represent a tile by a point at its center; do culling; depth sort; and draw the tiles' sprites with billboarding. Best of luck, will be lurking in thread smile.png.

 

I could be wrong, but my understanding is that the OP is not using a 3d engine, but that this is a 2d visualization made from scratch.

Share this post


Link to post
Share on other sites

True StarMire all from scratch. 

 

Okay so I'm not sure I've done any of this right but here is the solution I made. First, Ill put here the stripped-down drawing loop code. Note that I've removed all the "what a tile looks like" and debug-info code, so don't worry about world input var's use not bein shown

//ts = 64 th = 32 tq = 16

function drawWorld(world, center) {
    var iMax = Math.ceil(testSize.x / ts) * 2,
        jMax = Math.ceil(testSize.y / tq) + 1,

        location = {
            x: center.x - iMax / 2,
            y: center.y
        };

    ctx.translate(-th, -tq);

    for (var i = iMax; i >= 0; i--) {
        for (var j = 0; j < jMax; j++) {
            if ((i & 1) != (j & 1)) {
                continue;
            }

            var sX = i * th,
                sY = j * tq,

                sgX = (i + j) / 2,
                sgY = (j - i) / 2,

                gX = sgX + location.x,
                gY = sgY + location.y;

            drawTopFace({
                x: sX,
                y: sY
            });
        }
    }
}

This is, at least, producing a consistent overdraw and does not "under"-draw at any resolution.

 

 

The problem is finding the "center" in the layout. Currently, it's pretty close, as long as the screen is fairly close to a landscape-orientation style ratio.

 

 

Here's the live code on JSFiddle. Gotta love javascript, when you need outside input. I recommend checking that out because, as I said I did strip out a few things for this post, and it is actually semi-working. Tile picking is good and player movement in tile-size increments is also working. You can get a better feel for the offset issue.

Edited by noxabellus

Share this post


Link to post
Share on other sites

Based on your output, you're under drawing your short columns on the vertical by one tile.  The Horizontal is sometimes apparently correct, but it's inconsistent.

 

jMax = Math.ceil(testSize.y / tq) + 1,

 

This is correct, and your long columns are correct, but notice that every other column has one less tile- these shorter ones are incorrect.  This is probably a problem with your drawing loop.  You stopped short:  You need to draw one more row, which will add a tile each to your short columns.

 

You probably also need to draw one more column on the other end, once you fix this:

 

iMax = Math.ceil(testSize.x / ts) * 2,

 

While this sometimes gives acceptable results, it also creates problems in certain cases.  You need to divide by the actual width, not twice the width, and just add one.  This will be more consistent.  This has the potential to give incorrect results with certain widths.

 

I suggest you set this to dynamically update your testSize as the window size changes, you will then see the problem with this method with certain window sizes.

 
The problem is finding the "center" in the layout. Currently, it's pretty close, as long as the screen is fairly close to a landscape-orientation style ratio.
 
You're fine on that for now.  What you will need to do is find the center relative to the tile position.  Test whether the center of your screen is North, South, East, or West of center based on this original configuration.  This will update your draw starting position and your read starting position from your map data to iterate through the map.  We'll deal with that when we start moving the character.
 
I would like to confirm you're drawing the map int the correct order.
Can you write a circle, or X, or square or something into your map data, so we can confirm that it is writing correctly to screen?

Share this post


Link to post
Share on other sites

I would like to confirm you're drawing the map int the correct order.

Can you write a circle, or X, or square or something into your map data, so we can confirm that it is writing correctly to screen?

 

See this 'Fiddle revision for confirmation on this. Working on your other points now. Also implemented testSize updating.

Edited by noxabellus

Share this post


Link to post
Share on other sites

Yep, looks like it's working so far.

 

Are you going for a pixel art style to this?

 

If so, I recommend you reduce your tile size; 64 will be too difficult to produce art for, unless you have a pretty big budget for this (unless this is just a tech demo and doesn't need finished art).

 

32 pixels should be fine.  Also, you can blow these up to up to 4x or so magnification to make them read larger on the screen and show off the pixely goodness.

 

If you're going for more air-brushed instead of pixel art, 128 is probably fine.  Right now they read as a little small on the screen.

 

Airbrushed could make animation slightly easier, since you could use pre-rendered 3d (which wouldn't look good with pixel art).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!