New to isometric engine in java. Have a couple of things I don't understand.

So, I’ve been developing a very basic random terrain system in isometric view. I am simply applying a conversion to my camera to view everything isometric, using isometric tiles. I can do collision detection, and have no problem with the current code. My issue is I have no idea how to draw slopes, or make interesting terrain that’s not just flat. I don’t know how to do supposed “layers” as I can’t find any good tutorials which i’m able to really grasp. Is it all about an offset, but the same tile x y as the tile below it? No idea. I ask because I want to add in buildings, slopes, digging, like an isometric minecraft. I just have no idea how to wrap my head around the tile layering and would really appreciate it if someone could give me an easy to understand indepth explanation of how to perform these layered renderings, or how to render different layers of terrain which are mineable, at all.

Please, anyone.

Right now my current render code is as follows:

	// draws tiles based on a radius, follows you and "Streams" your world.


		arg2.translate(twoDToIso(-camX+420, -camY)[0],twoDToIso(-camX, -camY)[1]);
		camX = (int) (p.px - 960 / 2);
		camY = (int) (p.py - 860 / 2);

	

		for (int x =0; x < 100; x++) {
			for (int y = 0; y<100; y++) {
				float xx= (float) ((rM.map[x][y].x)*32);
				float yy= (float) ((rM.map[x][y].y)*32);
				if(calcDistance(xx,p.px,yy,p.py)<=15*32){
				arg2.drawImage(rM.map[x][y].img,twoDToIso(new Point((int)xx,(int)yy)).x ,twoDToIso(new Point((int)xx,(int)yy)).y);
				}
				
			
		}
public static Point twoDToIso(Point pt){
        
       Point tempPt=new Point(0,0);
        tempPt.x=pt.x-pt.y;
        tempPt.y=(pt.x+pt.y)/2;
        return(tempPt);
    }


It might be sloppy but I am new to this so i’m taking easy to understand, and possibly sloppy steps. All i’m doing is generating my tiles in a strictly 2d array and then shifting the camera to the isometric view which seems to work well enough. I also use a radius based rendering so that it’s not drawing every tile, only those within a certain distance.

I’ve been working on my Isometric game for a while now and can give you some of my experiences.

My game is a simple 2D isometric engine (built on LibGdx). By ‘simple 2D’ I mean the camera is set to orthographic, and everything I draw is just a simple 2D rectangular texture (sprite). I don’t do any rotation of the camera at all. It sounds like you are wanting to do a ‘3d isometric’ game, which is probably more common these days. If so, sorry I can’t offer as much help for that.

I create the sprites/textures from 3d objects in Blender and then simply render them out to png images . The Blender camera is rotated to a standard isometric viewpoint, but once rendered they’re just simple 2d images (often with transparency/alpha).

In memory I have a large array that I treat as a 3d array containing values for each location in the world. This is conceptually just a large cube that with X,Y,Z dimensions (Z is the height/altitude) where each cell is a location. I actually pre-calculate the physical 2d screen locations (X,Y) that each conceptual front most 3d location should be draw on screen and store them in a Map (hashtable) for each X,Y,Z location. Then, drawing is simply a matter of getting the ‘world location’ and then the corresponding physical screen location it should be drawn at. The Map also prvides very fast determination of which world locations are inside the physical window bounds (ie don’t draw locations that are not within the window).

Another big benefit of standard 2d isometric is that by default the 2:1 (width:height) view ratio means that every location (X,Y,Z) will completely obscure the location at position (X-1, Y-1, Z-1) and so on. This is the key to efficiently culling out a huge number of locations in a world with many z levels. I fire a ‘ray’ for each location to be drawn that moves -1,-1,-1 each step and stops as soon it hits a solid (opaque) location that obscures all locations directly behind it. This cuts out a huge number of locations that cannot be seen. I do adjust the angle of the ray slightly to draw the tiles in a way that reduces the ‘escher’ problem (this also reduces performance as it means more tiles to draw), but this is the basic overview of doing X,Y,Z isometric 2d drawing.

This is the important part of the physical 2d coordinate precalculation function ( I do it once at startup). Tilesize is the default size of a location when drawn ons screen with at 1:1 zoom (eg 64):


int tileSize=64;
int yLevelStep = tileSize / 8 * 5;
for (int z = worldDepth - 1; z >= 0; z--) {
     for (int y = 0; y < worldHeight; y++) {
           screenX = (y * (tileSize / 2));
           screenY = ((worldDepth - 1 - z) * yLevelStep + (y * tileSize / 4));

           for (int x = 0; x < worldWidth; x++) {
               // Associate the X,Y,Z with the screenX,screenY using a Map.
           }
    }
}

I also have a WorldToView() function that does the same thing for following a moving object etc:

public Vec2D WorldToView(float X, float Y, float Z) {
        int screenX = 0;
        int screenY = 0;

        int offsetX = 0;
        int offsetY = 2520;

        if (Direction == 0) {
            Y = -Y;

            int yLevelStep = tileSize / 8 * 5;

            screenX = offsetX + (int) ((X - Y) * tileSize / 2);
            screenY = offsetY + (int) ((X + Y) * -tileSize / 4);

            screenY -= (Z * yLevelStep);
      }
}

The other key part is to have a way to measure the distance of each ‘world location’ that needs to be drawn, to the viewer. This allows me to sort visible locations on this value and draw them using the ‘painters algorithm’. For this I use a 3d pythagorean distance calculation from each location to an imaginary point nearest the camera.

It’s then simple to limit the z levels of locations drawn to provide the world ‘slicing’ effect to see inside mountains etc.

This is obviously simplified, there’s a lot more going on but this is the fundamental way I do X,Y, Z isometric world, so hopefully it’s helpful. I came to this approach through lot’s of experimentation and thinking. I’m certainly not a mathematics focused developer so this approach may be a bit weird for those that are. It is working quite well though.

I do really like your approach and i’d imagine since I want an interesting terrain, and not just a simple dungeon floor, or for multiple levels in buildings or underground I will need Z levels. So far I just have a strictly x,y world with an orthographic view like you. I’m just a little confused on how to implement the z levels, how to make them visible and what not. I’d assume a 3d terrain where x and y is all the tiles for that level and the z is just the offset for each layer on top or below? thanks for any input further! I like your idea a lot.

Much progress using a 3d array but 2d projection! Kind of cool illusion it creates.

Could someone please tell me how I could get rid of the look up higher blocks still appearing on the same level as floor level blocks? like, you can see the different levels, but when blocks of different levels are adjacent they still look to be the same height. I’m thinking lighting or shadows would fix this?

I think that smart edge coloring on the tops of your boxes would help. If adjacent box faces are joined them there should be no edge but if they aren’t, draw the edge.
Shadows would be nice too!

Looks like you’ve made some good progress on the isometric rendering!

The problem you’re talking about is the classic isometric rendering problem, often called the ‘escher’ problem. It’s due to the fact isometric rendering in orthographic mode has no perspective, so two locations that are actually not at all adjacent (in the 3d data structure) look as if they are next to each other when rendered.

As simple way to reduce this problem is to draw visible edges overlaid on blocks that have an empty neighbour/s, behind them at the same Z level. You can see I do that in my game on the edge of mountains etc.

However the more advanced approach is to imagine rotating the scene slightly so that you see bit more of the front faces of your locations. You don’t actually rotate the blocks (its 2D of course). Instead you reduce slightly the distance (in screen y coordinates) in your z levels. That means when you draw you are now not just obscuring the location X-1, Y-1, Z-1, but also partly obscuring the location -2 two tiles back. That extra obscuring of locations gives a clearer indication that the locations are not neighbors. Once again, see some pictures on the dev log for what I mean.

In the code I posted previously this is what the: “int yLevelStep = tileSize / 8 * 5;” is setting up. Instead of a full tile height each time we move up a z level we only move up 5/8 of a tile. (in yLevelStep the y refers to screen coordinates).

Hope this helps.