An Interesting Journey in Creating a 2D Isometric Platformer

Sept. 19, 2017
protect

Article and schemes by Sven Duval
Graphic Design by Antoine Schmoll
Studio Ernestine – Strasbourg, FR
More information about our game on @Monsieur_PAF
In development, using Unity

 

Our game, called “An Interesting Journey Of Mr Paf”, is born out of experiments with a 2D isometric concept. As 3D was not our artistic choice, the original idea was to try and manage a vertical axis in addition to the horizontal plane, in order to build bridges that the characters could both walk over and under. The result being more and more convincing as we progressed in our tests, we developed a GamePlay on top of this engine as we were creating it.

 

I was really surprised when I read Martin Pane's article on making an isometric game. His approach of the problem is very interesting and also very different from mine because he doesn’t seem to use a TileMap concept, and thanks to that, the performances of his game might increase. My approach is much more conventional and inspired from many tutorials and examples I have found on the web. That is why, to echo Martin Pane’s reflections and also to help those who would like to try out 2D isometric, I decided to give an overview of my work to inspire other projects. Then it is up to you to choose the method best suited to your project, or even experiment yourself with a mix of both.

 

First of all, I want to make it clear that if you want to produce an isometric platformer, you should move towards a 3D rather than 2D approach for a start. You will dramatically reduce your development time using an orthographic camera, the physics and other powerful tools available on Unity, and the result will be much more optimized. However, if you can't or don’t want to work in 3D — if you really don’t want to — the following content can help you.

 

 

First Step: Create a TileMap

 

The classical way to build a 2D isometric world is to use a TileMap. This principle allows you to simulate a 3D world view with a 2D coordinates system. You can find a lot of tutorials about what it is and how it works on the web (ex: https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps). This concept is almost as old as video games. So I will not dwell much on this definition and all the theories that derive from it.

 

For our project, we chose to use tiles with a diamond form (to have an isometric view) with a 2:1 ratio:

Tile size definition

Fig. 1: Tile Size Definition

 

Then we defined the origin and the axes of the isometric reference to stay consistent with what is commonly considered as 3D environment:

TileMap axes definition

Fig. 2: TileMap Axes Definition

 

Then I added a vertical axe named K, defining its unit as the height of an isometric cube. 1K is therefore equal to the height of a tile. Unlike the I and J axes, that can only be defined by integers, the K-axis is floating, which will be needed to implement the gravity in the next phases of the project.

K axis definition

Fig. 3: K Axis Definition

 

At the code level, you could be tempted to define a two dimensional array to reference the tiles. But a more optimal option consists in converting the isometric coordinates (i,j) to an integer index and then use a simple array. This conversion is based on the TileMap size:

Tile coordinates to index conversion

Fig. 4: Conversion of (i,j) Position to a Unique Index

 

To define and reference the isometric objects, I created a component representative of each logical object in the isometric reference, indicating its position on the [I,J,K] axes and size. I named it “Mapper”. Now you can place the mapper on the scene, depending on its isometric position:

Determine tile position

Fig. 5: Determine Tile Position

 

Each mapper in the isometric world is referenced over one or more tiles depending on its size. However, instead of creating a list for each tile entity, I grouped them in a unique TileMap entity in order to increase the performance. It doesn’t contain a tile entities array, but an array of mappers reference lists. By doing that, the tile entity becomes a simple structure (and not a class! See https://msdn.microsoft.com/en-us/library/aa664465(v=vs.71).aspx) containing shortcut methods to the TileMap entity. Then the code becomes much faster and uses less memory.

 

 

Second Step: Handling Logic and Building With Primitive Object

 

Once the TileMap is operational, you can build your world. The sprite, which is the visual part of the logical object, is associated to the mapper with the SpriteRenderer component. I also made sure that it takes into account possible children GameObjects with SpriteRenderer, so that we could keep using more sprites for one mapper, and even particles systems.

 

As for every game development, we needed a level editor because waiting for sprites to build levels is not a viable option. So I made a primitive entity that allowe building the levels and the game logic without the sprites.

 

In Unity, a sprite is nothing more than a 3D plane on which a material with a texture is applied. Like a 3D object, it is therefore defined by vertices, triangles and a UV mapping. You can easily verify this, by importing a sprite and putting it on the scene. Then change the “Draw Mode” to “Wireframe” and see the vertices of the sprite’s mesh.

Unity's Draw Mode Options

Fig. 6: Unity's Draw Mode Options

 

Note: It seems to be more efficient to add some vertices to cut a picture out instead of asking the GPU to process a lot of full transparent pixels. When you import a picture, Unity automatically cuts the transparency out and creates the vertices for you. Since the 5.6 version, the Sprite Editor contains an “Edit Outline” option that allows you to modify the vertices of your sprites. This option can be very useful.

Edit Outlines

Fig. 7: Edit Outlines

 

A primitive is the representation of a cube, and you can change its size and colour. This cube contains 3 visible faces, Top, Left and Right. You could just use one mesh to draw those 3 faces, but I chose to divide it in 3 faces to be able to apply an outline and different colours on each side, and make the scene more readable.

Primitive's Hierarchy

Fig. 8: Primitive's Hierarchy

 

See this post for more information about creating meshes from code:

http://answers.unity3d.com/questions/139808/creating-a-plane-mesh-directly-from-code.html

 

Building level with primitives

Fig. 9: Example of Building Level With Primitives

 

Third Step: Determine the Object’s Sorting Order

 

To use the words of Martin Pane:

The priority order when rendering a sprite in Unity goes like this, from highest to lowest:

  • Sorting Layer of the Renderer component

  • Order in Layer of the Renderer component

  • The z position of the object

If two sprites share the same “Sorting Layer” and “Order in Layer”, the one closest to the camera (in 3D World coordinates) gets rendered first.

 

The isometric world is made of 2D sprites. It implies that you have to write an algorithm to determine the drawing order of all the sprites. If all the objects were static, it would be easy, but as there are characters moving in this world, things get more complicated.

Character moving into the isometric world

Fig. 10: Character Moving in the Isometric World

 

The sorting order is defined by the position and size of the objects on the TileMap. It is therefore possible to determine from the coordinates system of the isometric reference an exhaustive order of the objects. This order is then sent to the SpriteRenderers by the “OrderInLayer” property (from the code, this property is called « sortingOrder ») ranging from 0 (farthest) to N (nearest). The z position can be used to organize children and make animations inside an object with further renderers.

 

Note: Sorting properties (“sortingLayerId”, “sortingLayerName”, “sortingOrder”, etc) are not specific to the “SpriteRenderer” component but inherit from the “Renderer” component. You can access and modify them from code even for a “MeshRenderer” or a “ParticleSystem” component. (See https://docs.unity3d.com/ScriptReference/Renderer.html)

 

Working in full 2D isometry can make you forget that you are not in a 3D environment and that some things are not possible. For instance:

3D objects association

Fig. 11: Assembly of 3D Objects

 

If you try to do that in 2D, you will have a problem: considering 1 sprite per cube, the blue one must be displayed in front of the green one, the green one must be displayed in front of the red one, the red one must be displayed in front of the blue one… You end up with what we called an “infinite triangle”, reminiscent of the Penrose triangle. “Infinite” because we got the following effect when executing the game:

Impossible assembly of 2D isometric objects

Fig. 12: Impossible Assembly of

JikGuard.com, a high-tech security service provider focusing on game protection and anti-cheat, is committed to helping game companies solve the problem of cheats and hacks, and providing deeply integrated encryption protection solutions for games.

Read More>>