Creating an isometric camera system

Started by
21 comments, last by StarMire 9 years, 5 months ago

Here, I doodled up a quick 32 pix tile for you to test with (in case you don't have some):

[attachment=24226:32tile.png]

This is what it looks like at 400%

[attachment=24227:400percent.gif]

In practice, it's actually 15 pixels tall, because you have to overlap them. Not sure how your other tiles functioned.

Advertisement

*raises from the dead*

Okay, StarMire. I hope this is readable. There is still a lot of clean-up to be done, but I have working code. I am hoping, now, that you can walk me through fixing the iteration of the blocks vertically so that I don't draw really tall things off the top of the screen, and really tall things come into the bottom properly. I feel like sub-block movement shouldn't be too hard so we don't have to focus on that, at least for now.

Here's the important parts of what I've got:


var testSize = {
    x: Math.round((canvas.width-200)/terrainImg.width)*terrainImg.width,
    y:  Math.round((canvas.height-200)/terrainImg.width)*terrainImg.width
};


function screenPos(X,Y)
{
    return {
        x: (X-Y)*(terrainImg.width/2),
        y: (X+Y)*(terrainImg.width/4)
    };
}

function cameraDraw(position)
{
    ctx.clearRect(0,0,canvas.width,canvas.height);
    
    var cen = {
        a: position.x + position.y,
        b: position.x - position.y
    },
        max = { //Use testSize instead of canvas size so we can see overdraw
            a: Math.floor(testSize.y/(terrainImg.width/2)), 
            b: Math.floor(testSize.x/terrainImg.width)
        },
        startPos = {
            x: (((cen.a - max.a) + (cen.b - max.b))/2),
            y: (((cen.a - max.a) - (cen.b - max.b))/2)
        },
        offset = screenPos(startPos.x, startPos.y);
    
    offset.x -= 100 - terrainImg.width/2;
    offset.y -= 100 - terrainImg.width/4;
    
    for(var z = 0; z < world.depth; z++) {
        offset.z = (terrainImg.width/2)*z;
        
        for(var a = cen.a - max.a-1; a < cen.a + max.a+1; a++) {            
            for(var b = cen.b - max.b; b < cen.b + max.b+1; b++) {
                if ((b&1) != (a&1))
                    continue; 
                
                var x = (a+b)/2,
                    y = (a-b)/2,
                    sPos = screenPos(x,y);
                
                sPos.x -= offset.x;
                sPos.y -= offset.y;
                sPos.y -= offset.z;
                
                if(world.blocks[x][y][z]) {
                    ctx.drawImage(terrainImg, sPos.x, sPos.y); 
                }
                
                if(x == position.x && y == position.y && position.z == z) {
                    ctx.fillRect(sPos.x+terrainImg.width/2-5, sPos.y+terrainImg.width/4, 10, 20);
                }
            }
        }
    }
}

As far as the issues I've had previously, everything is great. The camera position is always the exact center of the screen. As you can see in the JSFiddle there are still issues. Really tall structures go off the screen at the top, and are not visible as soon as they should be when scrolling in from the bottom.

Here's a pic of what I'm talking about, just to be clear.
FantasticLargeAfricancivet.gif

I imagine the solution is to use these a/b positions to relate to the tiles differently. They are not actual positions but potential screen positions, and things with a higher Z should just move into the next a/b tile up....I'm not sure how to do that. Any advice would be great, so sorry I let the topic die so long. I had to fight some serious burn out to get back into this.

Draw your map in multiple "layers".

Layer 1, 2 3 4 etc.

When you read the map data for the first, you do it normally. This is your ground plane.

For the next one, you read the map data from the plane right above the ground plane, but you read it from a different corner position (one voxel down in screen space). So, you're still just reading from your 3d array, but you're reading different coordinates (lower in screen space y) as you go UP in world space and through the layers that you draw your map in.

Hopefully that description is clear enough.

Draw your map in multiple "layers".

Layer 1, 2 3 4 etc.

When you read the map data for the first, you do it normally. This is your ground plane.

For the next one, you read the map data from the plane right above the ground plane, but you read it from a different corner position (one voxel down in screen space). So, you're still just reading from your 3d array, but you're reading different coordinates (lower in screen space y) as you go UP in world space and through the layers that you draw your map in.

Hopefully that description is clear enough.

Well, I've been working on this idea for about an hour now and I'm honestly not sure about this approach. There can only be like, roughly, max.a*2*max.b*2 tiles on screen at any time. However, i'll be doing max.a*2*max.b*2*world.depth iterations against the voxels, and then more checks to determine if they are visible....What would make more sense, to me, is to find the correlation between a, b, and x,y,z so that I can iterate over all a,b positions and simply grab the voxel which, having a matching x,y & a valid voxel, is the highest on z axis....I'm really bad at finding these types of correlations in the data though. I'll update later with my progress

You can optimize later (culling out invisible tiles, drawing to a texture, etc.), just get it to draw a few layers first to get the right appearance.

You can optimize later (culling out invisible tiles, drawing to a texture, etc.), just get it to draw a few layers first to get the right appearance.

That is excellent advice, and I have taken it. To good ends...Check the JSFiddle.

Obviously, I need to implement some sort of checking to see if a tile is covered by others. I need to know about all 3 face's coverage..How do I find that?

Are there any other ways I can improve this, other than sub-block movement?

When should I start on sub-block movement? Any recommendations as far as that goes?

Obviously, I need to implement some sort of checking to see if a tile is covered by others. I need to know about all 3 face's coverage..How do I find that?


That's not obvious to me. Performance seems fine for now. And really, you don't want anything that tall in a typical map. If something really is that tall, you'll want to do a cutaway or a fade at the upper heights.


Are there any other ways I can improve this, other than sub-block movement?


Collision, gravity, more complex maps using procedural terrain generation.

You should decide if you want to make it so these maps can be rotated or not. If not, you will need to be even more careful with your terrain generation.

You'll also want to work on systems to show the character when behind things, by fading large objects (as mentioned a bit above).



When should I start on sub-block movement? Any recommendations as far as that goes?


Start smooth scrolling right away, it's easy. But sub-block movement isn't necessarily required.

You will want animations of characters moving between blocks, and smooth scrolling, but not necessarily positions that stop between blocks.

It should be pretty straightforward.

If you want the characters to have per pixel movement, that makes things a little more complicated.

What kind of game are you wanting to make? If it's strategic, clear block positions make a lot of sense. If it's more ARPG, then per pixel location could work.

That's not obvious to me. Performance seems fine for now. And really, you don't want anything that tall in a typical map. If something really is that tall, you'll want to do a cutaway or a fade at the upper heights.


Collision, gravity, more complex maps using procedural terrain generation.

Well, I plugged in a terrain algo I had already written, and I got rather poor results performance wise. It took over a second to render a frame sometimes. That said I've already worked out how to cull 90% of the extra tiles which is good enough for now, and gets it back to a playable rate.


I think next I will work on smooth scrolling. ARPG+Minecraft is basically what I'm going for so I think sub-block movement will also be part of that work. I think I've got it pretty much handled, anyways.

How can I do camera rotations? That has always been an important feature in my head, but I really have no idea how to alter my camera loops and offsets to do that....

Glad you figured out how to cull tiles; it's pretty easy if it's causing problems, I just didn't want you optimizing prematurely.

Rotating the map 90 degrees is easy, you just have to read the map data from a different direction. It is also very disorienting, and that's harder to remedy.

To avoid disorientation, you may have to rotate all of the tiles slowly around the screen to the new position, which will mean it will be drawing incorrectly (no longer tiling properly) for a fraction of a second, but it should at least help the user follow what's moving and where. Your culling system will have to be updated for that too.

You could also make intermediate Isometric tiles, and then allow less than 90 degree rotation This is relatively easy programming-wise, but requires more art. It looks quite a bit better and allows a little more user control on the rotation.

Do you have an artist you are working with? Because some of these decisions affect art production quite a bit.

Well, actually, I am the artist for this project as well. My professional training is actually just in art and I'm learning the programming as I go. Of course, I don't really know any art terms regarding games, either, :[


To avoid disorientation, you may have to rotate all of the tiles slowly around the screen to the new position, which will mean it will be drawing incorrectly (no longer tiling properly) for a fraction of a second, but it should at least help the user follow what's moving and where. Your culling system will have to be updated for that too.

I had looked into 2d array rotation previously, figuring that 90-degree rotations would be the easiest way to go. I think that I could probably pull off that on my own but I definitely want to do everything I can to keep the player from getting disoriented. I think this is roughly what you were saying in this quote but, I had thought that perhaps I might just increase the overdraw to roughly double the screen size, draw a frame, and then rotate that frame on the screen incrementally through the 90 degrees, then fade that frame into the next frame which is drawn properly with the new 90* perspective. Of course, I'd have to pause the gameplay for whatever the transition time was, so pretty much any other solution would probably work better for the flow of the game.


You could also make intermediate Isometric tiles, and then allow less than 90 degree rotation This is relatively easy programming-wise, but requires more art. It looks quite a bit better and allows a little more user control on the rotation.

I will google around about this "intermediate isometric tiles," and see if I can find anything in the mean time, but can you elaborate on this point? Definitely seems more interesting.

Oh and here's the new JSFiddle with my progress on smooth-scroll/sub-block. You may see a few issues at the edges but it's just from JSFiddle's CSS Styles, it's working normally in the real build. You might also notice, I've refactored the whole thing a good bit so it's not a hideous mess.

This topic is closed to new replies.

Advertisement