Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game
Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game
Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Unity 2D Tile-Based Isometric and Hexagonal ‘Sokoban’ Game

Final product image
What You’ll Be Creating

In this tutorial, we will be converting a conventional 2D tile-based Sokoban game into isometric and hexagonal views. If you are new to isometric or hexagonal games, it may be overwhelming at first to try following through both of them at the same time. In that case, I recommend choosing isometric first and then coming back at a later stage for the hexagonal version.

We will be building on top of the earlier Unity tutorial: Unity 2D Tile-Based Sokoban Game. Please go through the tutorial first as most of the code remains unchanged and all the core concepts remain the same. I’ll also link to other tutorials explaining some of the underlying concepts.

The most important aspect in creating isometric or hexagonal versions from a 2D version is figuring out the positioning of the elements. We will use conversion methods based on equations to convert between the various coordinate systems.

This tutorial has two sections, one for the isometric version and the other for the hexagonal version.

1. Isometric Sokoban Game

Let’s dive right in to the isometric version once you have gone through the original tutorial. The image below shows how the isometric version would look, provided we use the same level information used in the original tutorial.

the isometric version of the sokoban level

Isometric View

Isometric theory, conversion equation, and implementation are explained in multiple tutorials on Envato Tuts+. An old Flash-based explanation can be found in this detailed tutorial. I would recommend this Phaser-based tutorial as it is more recent and future proof.

Although the scripting languages used in those tutorials are ActionScript 3 and JavaScript respectively, the theory is applicable everywhere, irrespective of programming languages. Essentially it boils down to these conversion equations which are to be used to convert 2D Cartesian coordinates to isometric coordinates or vice versa.

We will be using the following Unity function for the conversion to isometric coordinates.

Changes in Art

We will be using the same level information to create our 2D array, levelData, which will drive the isometric representation. Most of the code will also remain the same, other than that specific to the isometric view.

The art, however, needs to have some changes with respect to the pivot points. Please refer to the image below and the explanation which follows.

isometric sprites and their offsets

The IsometricSokoban game script uses modified sprites as heroSprite, ballSprite, and blockSprite. The image shows the new pivot points used for these sprites. This change gives the pseudo 3D look we are aiming for with the isometric view. The blockSprite is a new sprite which we add when we find an invalidTile.

It will help me explain the most important aspect of isometric games, depth sorting. Although the sprite is just a hexagon, we are considering it as a 3D cube where the pivot is situated at the middle of the bottom face of the cube.

Changes in Code

Please download the code shared through the linked git repository before proceeding further. The CreateLevel method has a few changes which deal with the scale and positioning of the tiles and the addition of the blockTile. The scale of the tileSprite, which is just a diamond shape image representing our ground tile, needs to be altered as below.

This reflects the fact that an isometric tile will have a height of half of its width. The heroSprite and the ballSprite have a size of tileSize/2.

Wherever we find an invalidTile, we add a blockTile using the following code.

The hexagon needs to be scaled differently to get the isometric look. This will not be an issue when the art is handled by artists. We are applying a slightly lower alpha value to the blockSprite so that we can see through it, which enables us to see the depth sorting properly. Notice that we are adding these tiles to the occupants dictionary as well, which will be used later for depth sorting.

The positioning of the tiles is done using the GetScreenPointFromLevelIndices method, which in turn uses the CartesianToIsometric conversion method explained earlier. The Y axis points in the opposite direction for Unity, which needs to be considered while adding the middleOffset to position the level in the middle of the screen.

At the end of the CreateLevel method as well as at the end of the TryMoveHero method, we call the DepthSort method. Depth sorting is the most important aspect of an isometric implementation. Essentially, we determine which tiles go behind or in front of other tiles in the level. The DepthSort method is as shown below.

The beauty of a 2D array-based implementation is that for the proper isometric depth sorting, we just need to assign sequentially higher depth while we parse through the level in order, using sequential for loops. This works for our simple level with only a single layer of ground. If we had multiple ground levels at various heights, then the depth sorting could get complicated.

Everything else remains the same as the 2D implementation explained in the previous tutorial.

Completed Level

You can use the same keyboard controls to play the game. The only difference is that the hero will not be moving vertically or horizontally but isometrically. The finished level would look like the image below.

Isometric version finished level

Check out how the depth sorting is clearly visible with our new blockTiles.

That wasn’t hard, was it? I invite you to change the level data in the text file to try out new levels. Next up is the hexagonal version, which is a bit more complicated, and I would advise you to take a break to play with the isometric version before proceeding.

2. Hexagonal Sokoban Game

The hexagonal version of the Sokoban level would look like the image below.

hexagonal sokoban level

Hexagonal View

We are using the horizontal alignment for the hexagonal grid for this tutorial. The theory behind the hexagonal implementation requires a lot of further reading. Please refer to this tutorial series for a basic understanding. The theory is implemented in the helper class HexHelperHorizontal, which can be found in the utils folder.

Hexagonal Coordinate Conversion

The HexagonalSokoban game script uses convenience methods from the helper class for coordinate conversions and other hexagonal features. The helper class HexHelperHorizontal will only work with a horizontally aligned hexagonal grid. It includes methods to convert coordinates between offset, axial, and cubic systems.

The offset coordinate is the same 2D Cartesian coordinate. It also includes a getNeighbors method, which takes in an axial coordinate and returns a List<Vector2> with all the six neighbors of that cell coordinate. The order of the list is clockwise, starting with the northeast neighbor’s cell coordinate.

Changes in Controls

With a hexagonal grid, we have six directions of motion instead of four, as the hexagon has six sides whereas a square has four. So we have six keyboard keys to control the movement of our hero, as shown in the image below.

Hexagonal keyboard control keys

The keys are arranged in the same layout as a hexagonal grid if you consider the keyboard key S as the middle cell, with all the control keys as its hexagonal neighbors. It helps reduce the confusion with controlling the motion. The corresponding changes to the input code are as below.

There is no change in art, and there are no pivot changes necessary.

Other Changes in Code

I will be explaining the code changes with respect to the original 2D Sokoban tutorial and not the isometric version above. Please do refer to the linked source code for this tutorial. The most interesting fact is that almost all of the code remains the same. The CreateLevel method has only one change, which is the middleOffset calculation.

One major change is obviously the way the screen coordinates are found in the GetScreenPointFromLevelIndices method.

Here we use the helper class to first convert the coordinate to axial and then find the corresponding screen coordinate. Please note the use of the sideLength variable for the second conversion. It is the value of the length of a side of the hexagon tile, which is again equal to half of the distance between the two pointy ends of the hexagon. Hence:

The only other change is the GetNextPositionAlong method, which is used by the TryMoveHero method to find the next cell in a given direction. This method is completely changed to accommodate the entirely new layout of our grid.

Using the helper class, we can easily return the coordinates of the neighbor in the given direction.

Everything else remains the same as the original 2D implementation. That wasn’t hard, was it? That being said, understanding how we arrived at the conversion equations by following the hexagonal tutorial, which is the crux of the whole process, is not easy. If you play and complete the level, you will get the result as below.

hexagonal sokoban finished level

Conclusion

The main element in both conversions was the coordinate conversions. The isometric version involves additional changes in the art with their pivot point as well as the need for depth sorting.

I believe you have found how easy it is to create grid-based games using just two-dimensional array-based level data and a tile-based approach. There are unlimited possibilities and games that you can create with this new understanding.

If you have understood all the concepts we have discussed so far, I would invite you to change the control method to tap and add some path finding. Good luck.