Pregunta

I'm having some trouble with isometric walls.

I'm drawing isometric floor tiles using the back to front rendering method, and it works fine. I also keep my floor tiles lined up properly in a nice grid. The code (and this seems to be pretty much standard for isometric floor drawing) is as follows:

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >=0 ; y--){

        int xCo = (y+x)*(tileWidth/2);
        int yCo = (x-y)*(tileHeight/2);
        tile.draw(g, xCo, yCo);
    }            
}

This makes a nice little floor:

multiple floor tiles

The grid is constructed from this tile:

A single tile

Unfortunately, when I use the same logic for walls, it all goes right to hell.

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >= 0 ; y--){

        int xCo = (y+x)*(wallWidth()/2);
        int yCo = (x-y)*(wallHeight()/2);
        walls.draw(g, xCo, yCo);    
}
}

I'm using this as my wall tile:

Single wall tile

(it's a placeholder from google image search, but it should work all the same)

This is the result I get:

Multiple wall tiles

The rendering order for my walls is clearly correct, nearer walls render on top of farther away walls, which is what I want, but the positioning is also painfully incorrect, and I don't know exactly how I ought to correct that short of using pixel values.

For reference, I did do a trial using hardcoded pixel vals, because I found that from one wall's right corner to the next wall's right corner, the change was exactly (200, -100) pixels. When I made my rendering loop account for that

int xCo = (x+y)*200;
int yCo = (y-x)*-100;

it worked fine, but that is not a viable solution because it doesn't allow for any versatility.

So, looking for suggestions on how to make my isometric walls line up. What am I doing wrong?

Thanks!

¿Fue útil?

Solución

You can't simply use the same drawing code for walls as for floors because walls and floors are not in the same plane: floors are flat (horizontal) while walls are vertical. So you have to draw them slightly differently.

Your x and y coordinates in the floor case mean something like "left/right" and "forward/backward" in terms of the placement of the tiles. For bricks, left and right still make sense, but we want to replace forward/backward with up and down to reflect the vertical direction. So our "y" gets a new meaning.

Now, in Maths, the y axis usually points upwards while in 2D computer graphics it points down. You can take your pick - the code below assumes that it points upwards so that y = 0 means "at the floor level".

So let's start thinking about order. The example brick you posted is for a wall that would be a the (upper) left end of a floor. Because of the black parts of the brick (the depth of the wall), we have to make sure that we draw the bricks that are further right first so that the black depth on the left side will be covered by bricks that are closer. The same argument applies for the black on the top part of the wall, we have to draw the lower bricks first.

If we stick with the x- and y-directions as discussed before (x goes from left to right, y goes from bottom to top), this means that we have to run both our for-loops in negative directions:

    for (int y = 3; y >= 0; y--) {
        for (int x = 5; x >= 0; x--) {
            ...
        }
    }

The main question is now how much we have to offset the drawing of each brick with respect to the other bricks. Let's do that one direction at a time, starting with the x direction.

Let's imagine just two bricks next to each other:

two bricks horizontally

The left of the two has the black depth part visible but the right one shouldn't show it. Thus we cannot simply offset the right image by the full width of the PNG. In fact, assuming that the bricks line up with your floor tiles, the width of the actual front part of the wall should be the same as half the width of a tile.

int xCo = x * tileWidth / 2;

The black wall depth on the left should not be ignored, because we probably want to offset each brick a little bit to the left so that the x-coordinate of the front corner of the wall lines up with the floor tiles, not the x-coordinate of the back corner.

Now, the y-coordinate of each brick is a bit trickier because it does not only depend on the brick row, it also depends on the x-coordinate: the further right the higher up we should draw. But let's ignore the x-direction for a moment and try to simply draw a column of bricks:

two bricks vertically

Again, the delta between the y-coordinates of the two bricks is not the full height of the PNG. Unlike in the left/right case where we assumed that bricks line up with tiles which allowed us to use tileWidth as the delta-x, bricks can have arbitrary heights. But we can still compute the actual brick height from the image height because we know that the depth on the left side and the depth on the top have to line up.

If we look at the little transparent triangle in the upper right corner of the brick PNG, we notice that the ratio of its width and height must be the same as the ratio of a floor tile's width and height. That allows us to compute an yoffset from the xoffset computed above, and to use that to infer the actual height of the brick:

int yoffset = xoffset * tileHeight / tileWidth;
int wallHeight = wallHeight() - tileHeight / 2 - yoffset;

Note that this only works under the assumption that there's no empty space at the border of the PNG and it might still fail due to rounding errors. So you might add a Math.ceil() (or simply + 1) here if necessary.

So for simple columns, we're good to go now: we can simply multiply our y variable with the above wallHeight. But as noted before, the x-position of a brick also influences the y pixel coordinate. If we look at the first picture again with the two bricks next to each other, how much did we have to move the right brick up to line up with the left brick? Well, this one is actually easy, because it's the same as with floor tiles: half the height of a tile!

So we're all set. If we put everything together, we end up with a bit of code like this:

int xoffset = wallWidth() - tileWidth / 2;
int yoffset = xoffset * tileHeight / tileWidth;
int wallHeight = wallHeight() - tileHeight / 2 - yoffset;

for (int y = 3; y >= 0; y--) {
    for (int x = 5; x >= 0; x--) {

        int xCo = x * tileWidth / 2;
        int yCo = y * wallHeight - x * tileHeight / 2;

        walls.draw(g, xCo - xoffset, yCo - yoffset);
    }
}

(I'm assuming that wallWidth() and wallHeight() return the width and height of the brick PNG.)

Note that the three constants before the for loops can be moved out of the actual drawing code - they only depend on image properties and other constants and don't have to be re-computed every time we draw the wall.

Otros consejos

If you look at the floor tile which is shaped like a diamond, moving it up by a half width and across by half length two edges will align.
The wall tile is not a diamond so by moving half width and across by half length the edges you want to match will not align.

Given u = distance to move across and v = distance to move up and A the isometric angle

v = u*tan(A)

u in this case is the width of the front face of the image.

if face(the textured bit) of the wall image matches the edge length of the floor tile this gives

int offset = ?;// this is the width of the black stripe on the image.

for(int x = 0; x < 6; x++){
    for(int y  = 3; y >=0 ; y--){

        int xCo = ((y+x+1)*(tileWidth/2))-offset;
        int yCo = (x-y)*(tileHeight/2);
        wall.draw(g, xCo, yCo);
    }            
}

In an isometric realm, there are three axes you can move in - Z for up and down, X and Y for your 'diagonals'.

First, let's imagine the pixel representation of a 1 unit by 1 unit by 1 unit isometric cube, with all sides represented equally long:

It would be A pixels high on the Z axis. Its other edges would also be A pixels in length, but rotated 60 degrees - so it would be sin(30)*A pixels high and cos(30)*A pixels long in X and Y directions - aka, 0.5*A and sqrt(3)/2 *A.

So, to position an isometric cube-sized object in X, Y and Z we must translate its on screen x and y by the following:

y += Z*A
x += X*A/2 - Y*A/2
y += (X+Y)*A*sqrt(3)/2

As long as the assumptions I made hold this should work.

EDIT: By the way, A will have to be hard coded if the image has depth and thus you can't automatically extract A from the image dimensions.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top