HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Isometric City Building Game

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
cityisometricgamebuilding

Problem

I have recently been working on my first game with an isometric perspective. For those who don't know, an isometric perspective is a pseudo 3D view created from 2D tiles. I am working off of this tutorial about isometric graphics.

I believe my approach is fairly clean and efficient. I have tried to do things in an object oriented way. For some of the math involved, I am relying on the code found in the tutorial linked above. I will include this code for completeness, but it is not really my code.

Edit: I changed the rendering to load all of the textures into a Map and then create one of each type of IsoTile out of those textures at game start. When the IsoTile is instantiated, a texture is passed into it by the libGDX game. The IsoTile splits the texture into the appropriate number of TextureRegions that are all the same size. After that, the world map of IsoTiles is created. During rendering, the game checks whether or not the integer in the world data matches the type of the IsoTile in the world map, and if not then it changes that IsoTile to the appropriate type. Then the renderer renders this world map each cycle. For each IsoTile its array of TextureRegions is iterated over and drawn to the screen.

One benefit of doing things this way is that I can flip a switch and disable rendering beyond the initial TextureRegion in the array, and thereby clip the texture such that you can see the tiles behind it easily. I could also apply a different alpha value to these TextureRegions instead to make them see through.

I should mention that I am already familiar with how to use Screen objects in order to separate the rendering logic into a MainMenu and GameScene, etc. This is why the camera, batch, and textures are made public by the libGDX Game class. Eventually the different Screen objects will have their own render() and draw() methods and will need access to the loaded resources.

IsoTile.java

```
public class IsoTile {

Solution

When you're using a double for-loop, I'd stay away from i and j names for the variables. I think x and y is better, even if we're dealing with Isometric coordinates, perhaps you can come up with something even better.

There is one main concern I have in your code, and that is this part in the render method:

IsoPoint point = new IsoPoint((int)(i * this.defaultHeight * gapReducer / scaleReducer), 
                              (int)(j * this.defaultHeight * gapReducer / scaleReducer));
IsoPoint convertedPoint = IsoHelper.twoDToIso(point);


This creates new objects in each and every call to render. The render method is called, as you know, continuously. This is causes a lot of extra work for the device and puts a lot of extra work on the garbage collector. Avoid creating objects in a continuous loop. There are several SO questions about this.

In this case, you could actually perform the operations using only one IsoPoint object that you re-use every time.

// Outside render method
private IsoPoint point = new IsoPoint();

// In render method
for (int i = this.worldTiles.size() - 1; i >= 0; i--) {
    List row = this.worldTiles.get(i);
    for (int j = row.size() - 1; j >= 0; j--) {
        IsoTile tile = row.get(j);
        point.set((int)(i * this.defaultHeight * gapReducer / scaleReducer), 
                  (int)(j * this.defaultHeight * gapReducer / scaleReducer));
        point.convertTwoDToIso();

        int count = 0;
        for (int h = 0; h < tile.textures.size(); h++) {
            ...

            TextureRegion textureRegion = tile.textures.get(h);
            batch.draw(textureRegion, 
                       point.x, 
                       point.y + h * this.defaultHeight / scaleReducer, 
                       textureRegion.getRegionWidth() / scaleReducer, 
                       textureRegion.getRegionHeight() / scaleReducer);;
        }
    }
}


Note that this way, the IsoPoint object point essentially becomes a storage place for two variables, x and y and now you continuously mutate those variables, instead of creating a bunch of new variables every time.

Alternatively, you could have each IsoTile store a permanent reference to the IsoPoint object that can be used for the calculation.

Either way, Avoid creating objects in a continuous loop

Code Snippets

IsoPoint point = new IsoPoint((int)(i * this.defaultHeight * gapReducer / scaleReducer), 
                              (int)(j * this.defaultHeight * gapReducer / scaleReducer));
IsoPoint convertedPoint = IsoHelper.twoDToIso(point);
// Outside render method
private IsoPoint point = new IsoPoint();

// In render method
for (int i = this.worldTiles.size() - 1; i >= 0; i--) {
    List<IsoTile> row = this.worldTiles.get(i);
    for (int j = row.size() - 1; j >= 0; j--) {
        IsoTile tile = row.get(j);
        point.set((int)(i * this.defaultHeight * gapReducer / scaleReducer), 
                  (int)(j * this.defaultHeight * gapReducer / scaleReducer));
        point.convertTwoDToIso();

        int count = 0;
        for (int h = 0; h < tile.textures.size(); h++) {
            ...

            TextureRegion textureRegion = tile.textures.get(h);
            batch.draw(textureRegion, 
                       point.x, 
                       point.y + h * this.defaultHeight / scaleReducer, 
                       textureRegion.getRegionWidth() / scaleReducer, 
                       textureRegion.getRegionHeight() / scaleReducer);;
        }
    }
}

Context

StackExchange Code Review Q#92095, answer score: 5

Revisions (0)

No revisions yet.