Isometric tile picking

downloadDownload the ready to use Eclipse project for this tutorial

This tutorial will teach you how to achieve picking in an isometric 2D game. It assumes you’ve got some basic knowledge about how to draw isometric worlds and basic game programming knowledge.

When I first made an isometric game, a question came to my mind: how to know on which tile the user clicks. This is a fundamental question and you’ll need this for sure in any isometric world. Searching the web didn’t bring any interesting results; some of them were actually really wrong, using very complex math wizardry for what can be achieved by a simple transform matrix.

Flare, a typical isometric game.

Flare, a typical isometric game.

The problem

A classic isometric game is made of tiles of a length twice the size of the height. For instance, 64×32 tiles are pretty popular. For the rest of this tutorial, we will draw the tiles with a width of 1.0f and a height of 0.5f. It doesn’t really matter; if you wish to achieve a pixel perfect drawing of your tiles, you could use 64.0f and 32.0f or 32.0f and 16.0f. The result would be the same.
I personnally like to work with meaningful units rather than pixels; so a tile that is 1 in width makes more sense.

The problem of an isometric world is that it is drawn using classical cartesian, orthogonal coordinates.

An isometric tile

An isometric tile

Several tiles, like the one above -courtesy of opengameart- are drawn on top of each others. They have transparent sides but they are drawn as classical rectangles.

The problem rises when you click on the map. Which tile is clicked? Take a very close look at this drawing:

Isometric coordinates

Isometric coordinates

In this drawing, we can see there is no simple way to know where the user clicks. Let’s say the user clicks in the region of the isometric tile {1,1}:

Zoom on the 1,1 tile

Zoom on the 1,1 tile

 

The bounding box of the tile {1,1} is of no use: a bit of it belongs to the tile {0,1}, a bit of it to {1,2}… So using bounding boxes will not give you an accurate picking result for isometric world.

Matrix to the rescue

What we need to do is defining a transform matrix that matches our drawing of the tiles. After all, an isometric world is just a glorified bunch of rectangles rotated by 45 degrees; and with a height divided by 2. So let’s define this matrix. (note: the translation is only there because the origin of the isometric coordinates do not match the origin of the cartesian coordinates. Otherwise, it is completely superfluous)

 

//create the isometric transform
isoTransform = new Matrix4();
isoTransform.idt();
isoTransform.translate(0.0f, 0.25f, 0.0f);
isoTransform.scale(1.0f, 0.5f, 1.0f);
isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f)

Does this work? The answer is NO.

It took me a while to figure out why this naive approach didn’t work. The answer lies with your maths from high school. Yes, nothing more. You see, if you take a square and you rotate it by 45 degrees:

Square rotated by 45°

Square rotated by 45°

Then the width of the shape is not 1.0f anymore but sqrt(2.0f). That is, straight from the Pythagorean theorem.

So, to rescale to the right width, we need to multiply by 1/sqrt(2), or multiply by sqrt(2)/2. Whatever floats your boats, these are the same numbers (~0.707). The height is still half the width. So we get:

 

//create the isometric transform//create the isometric transform
 isoTransform = new Matrix4();
 isoTransform.idt();
 isoTransform.translate(0.0f, 0.25f, 0.0f);
 isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
 isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f);

The code

The code running

The code running, the red tile is the picked tile

 

The code contains several parts. We have the creation of a tileset from this 256×64 texture (this is GPL’d, I took art from Clint Bellanger to repack it in this simple tileset). and then the creation of a 10×10 map:

 

Tileset

The tileset used for the demo

//create the isometric transform//load the tileset
textureTileset = new Texture("data/tileset.png");
textureTileset.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
tileSet = new TextureRegion[4];
for(int x=0;x<4;x++){
  tileSet[x] = new TextureRegion(textureTileset, x*64, 0, 64, 32);
}

//creat a 10x10 isometric map
map = new int[][]{
    {0, 0, 0 ,0, 0, 0, 0, 0, 0, 0},
    {0, 1, 1 ,1, 1, 1, 1, 1, 1, 0},
    {0, 1, 2 ,2, 0, 0, 0, 0, 1, 0},
    {0, 1, 2 ,2, 0, 0, 0, 0, 1, 0},
    {0, 1, 0 ,0, 0, 0, 0, 0, 1, 0},
    {0, 1, 0 ,0, 0, 0, 0, 0, 1, 0},
    {0, 1, 0 ,0, 0, 0, 0, 0, 1, 0},
    {0, 1, 0 ,0, 0, 0, 0, 0, 1, 0},
    {0, 1, 1 ,1, 1, 1, 1, 1, 1, 0},
    {0, 0, 0 ,0, 0, 0, 0, 0, 0, 0}
};

A simple isometric map renderer (I won’t go into the details of this; you can refer to any tutorial on the web on how to draw an isometric world):

//create the isometric transformprivate void renderMap(){
  for (int x = 0; x < 10; x++){
    for(int y = 10-1; y >= 0; y--){

      float x_pos = (x * tileWidth /2.0f ) + (y * tileWidth / 2.0f);
      float y_pos = - (x * tileHeight / 2.0f) + (y * tileHeight /2.0f);

      if(x==pickedTileX && y==pickedTileY)
        spriteBatch.setColor(1.0f, 0.0f, 0.0f, 1.0f);
      else
        spriteBatch.setColor(1.0f, 1.0f, 1.0f, 1.0f);
        spriteBatch.draw(tileSet[map[x][y]], x_pos, y_pos, tileWidth, tileHeight);

    }
  }
}
And the most important part: the code that actually detects which tile is clicked!
//create the isometric transform@Override
public boolean touchDown(int screenX, int screenY, int pointer, intbutton) {
  touch.set(screenX, screenY, 0);
  cam.unproject(touch);
  touch.mul(invIsotransform);

  pickedTileX = (int)touch.x;
  pickedTileY = (int)touch.y;

  return false;
}

 

No math wizardry, no complex functions: it is just a matrix multiplication!

Enjoy the power of the matrix and happy coding!

downloadDownload the ready to use Eclipse project for this tutorial

69 Comments

  • Fabio Cunha says:

    Hi,
    congratulations for your tutorial, it’s very well explained, the images on it remember the diablo series game.
    But I need to admit, the math is complex for me.
    This tutorial has potencial to become a good mobile game.

    • Tony Pottier says:

      Thank you Fabio. I realize it is quite difficult to explain these kind of things. Sometimes it can be quite abstract. My best advice would be to download the code and toy with it.

      • Fabio Cunha says:

        For the moment I’ll not work with your code, I learn I precious lesson. Finish a game before begin another.
        When I fully completes my last game Shuriken Attack, I’ll inspect yours, but since you are the developer of the main code, I don’t think I can create a better isometric game than you.

        • Tony Pottier says:

          haha thank you but I am far from being an expert though. I just happen to to like the maths behind games :-)

  • Doug says:

    Great tutorial. I am looking at starting an Iso game but I am looking for the ability to stack tiles for height changes. I was wondering if you have thought about this and how you would go about picking a tile that is technically at a lower point but the screen position is a row or two above.

  • Valerio says:

    Hi Tony,
    I just wanted to thank you for this great tutorial, it has helped me immensely.

  • Paul says:

    Hi, thanks for this great article, I am trying to drag an image over the isometric map, it works fine on the X axis, but it jumps 2 tiles in the Y axis… I created an image with gimp with an isometric shape of 64×64, and with setSize(1, 0.5f) to render it 64×32( otherwise it is stretched). Then, I use this :

    Vector3 screen = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
    camera.unproject(screen);
    screen.mul(invIsotransform);
    Vector2 iso = new Vector2(screen.x, screen.y);
    Vector3 newIso = new Vector3(iso.x, iso.y, 0);
    newIso.mul(isoTransform);
    Vector2 newScreen = new Vector2((int)newIso.x, (int)newIso.y);
    myImg.setPosition(newScreen.x, newScreen.y);

    But it jumps 2 tiles in the Y axis… Would you know how to fix this?

    • Paul says:

      I solved it actually, thanks for the article, I can now start my game ;-)

      • Paul says:

        I fixed it like that :

        if ( objectTaken ){
        Vector3 screen = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
        camera.unproject(screen);

        screen.mul(invIsotransform);
        Vector2 iso = new Vector2(screen.x, screen.y);
        Vector3 newScreenTemp = new Vector3((int)iso.x, (int)iso.y, 0);

        newScreenTemp.mul(isoTransform);
        Vector2 newScreen = new Vector2(newScreenTemp.x, newScreenTemp.y);
        curPreview.setPosition(newScreen.x, newScreen.y);
        }//objectTaken

  • tuanpn says:

    Hi, thanks for this great article,
    how convert position map to pixel world anh world to map.
    thank you!

  • Tomaž says:

    Hi,
    you wanted to say, that your “picking” algorithm is missing a very important thing: height map. You don’t consider height map at all, and not using a height map in an isometric game is not very realistic (unless you want to do all flat terrain, which is usually not the case).

  • romualdo says:

    This article is useful for beginners….
    http://clintbellanger.net/articles/isometric_math/

  • Carlos Lopez says:

    Explaining (sorry for my bad English translation)

    “Rendering” of sprites / tiles isometrically is a little more complicated than the process explained. The “explained” serves as a working basis and for scenarios where “no sprite” is above / below another (the sprites are just front or back at the same level). In this case the algorithm is clear: follow the drawing order +(Z-X).

    But what happens when I have floating elements to a level/height Y?

    a-) If the sprites are placed on top of other sprites exactly without overlapping/intersecting with any of them (on the Y axis), the order can be used paint +(Z-X)-Y.

    b-) If the sprite “intersects/overlaps” more then 1 sprite simultaneously (in X, Y and/or Z) WE HAVE A PROBLEM: In what order will draw them?. Then we need a complex sorting algorithm to determinate the order. Not to mention when we have “a lot of” floating sprites to be “integrated”.

    I, after a long analysis (and much suffering), was able to implement an algorithm. I would never have imagine complexity (honestly I thought it will be easier) of this “affair” and I was finding as I started developing my first game in cocos2d-x 2.5D. An algorithm of this type requires, in the first instance, a “comparison “everything with everything” but is possible to make a lot of optimizations for reducing calculation process so much.

    An example (demo) of the algorithm result can be seen here: https://www.youtube.com/watch?v=tU1uJYdxUY4 https://www.youtube.com/watch?v=tBwxX44c6eE

    • Tony Pottier says:

      Hello Carlos,

      If you look at my game currently in development, you have height and various objects on top of each others (walls, trees, etc) and it all works perfectly fine and it’s drawn correctly without any sort of complex sorting.

      The game is actually drawn in layers, which gives good performance (sorting is slow) and allows to cache entire portions of a map into a vertexbufferobject for maximum performance (the parts which will always be drawn behind any other stuff)

      This way I can reach 60fps on almost all android devices without bleeding processing power :)

  • romualdo, romualdorojo97 in twitter says:

    The math behind this is so easy, matrices are magical, i will try to explain a little bit this article, first you should know I don’t speak a good english…

    CONTENT
    What you must to understand first?
    1. What is a matrix
    2. How use a matrix in real-life(games in this case)
    3. Where are the matrices normally in videogames?
    Why this example work?

    WHAT YOU MUST TO UNDERSTAND FIRST?

    1. WHAT IS A MATRIX

    A matrix is simply a set of numbers, that is all, only a set of numbers.
    Mathematically, a matrix is a rectangular grid of numbers. We can identify a number, or an element, in a matrix by his corresponding row number and column.

    Ref 1.1:
    https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/
    read the phagrap “MATRIX ALGEBRA REFRESHER”

    2. HOW USE A MATRIX iN REAL LIFE(VIDEOGAMES)

    In videogames you can imagine the matrices like a Pandora box that ransforms objects or points or vertex that are multiplied for them, these Pandora boxes can transform any object in diferent ways, they can translate a point, they can rotate a point, they can scale a point remember translate, rotate or scale, you could read the following for understand how these Pandora boxes work…

    Ref 2.1: http://www.mathplanet.com/education/geometry/transformations/transformation-using-matrices
    Read all for basic understanding in transformation matrices

    Ref 2.2:
    https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/
    read the paragraph “THE MODEL MATRIX”

    3. WHERE ARE THE MATRICES IN VIDEOGAMES

    Normally you dont use matrixes in video games but they are in your engine under the hood, for example the camera is really a matrix that is multiplied by another matrix for create what you see in screen, in video games there are three types of matrices commonly used these are .

    A. The model matrices, are used for transform the position, rotation and scale of your objects in your world, you could say the model matrix transform points from model coordinates to world or game coordinates, you could also say that the points or vertexes that conform you game objects(model) are now relative to a common coordinate system

    B. The view matrix, this matrix make the points of your world relative to the camara, e.g now your world cordinate system is relative to the camera

    C. The projection matrix, this matrix make your 3D or 2D world a flat world, this flat world is known as the projection of your game objects to the screen

    Ref 3.1:
    http://www.codinglabs.net/article_world_view_projection_matrix.aspx
    Read for understand more about the model, view and projection matrices

    Ref 3.2:
    https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/

    Ref 3.3:
    http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
    more about the model, view and projection matrices plus the homogeneous coordinate system concept

  • romualdo, romualdorojo97 in twitter says:

    WHY THIS EXAMPLE WORK?

    Now let’s understand the code behind this article

    // this simply is a matrix that we will use for transfor something later
    isoTransform = new Matrix4();

    // now convert this matrix to identity, e.g now all its element are equal to 1
    isoTransform.idt();

    // set the translate elements of this matrix to 0 in X, 0.25 in Y and 0 in Z
    // now if we multiply a point by this matrix the point will be translated in the
    // direction the matrix specify
    isoTransform.translate(0.0f, 0.25f, 0.0f);

    // if we multiply a point by this matrix that point will be translated and scaled
    // we can see also that its scale in Y is the half of 1 this because isometric tiles
    // are created with a size proportion of 2 units width : 1 unit height
    isoTransform.scale(1.0f, 0.5f, 1.0f);

    // now if we multiply a point by this matrix we will get the same point translated
    // 0.25 in Y, scaled 0.5 in Y and rotated 45 degrees in clockwise also in Y axis
    isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f)

    Now our dear friend tony say us this wont work,,, ok he explain also why but you should now also this extra info that could be useful

    1. The image itself is 1×0.5 units
    2. The image contains a picture rotated 45 degrees and scaled 0.5 units in Y

    What I want you notice is the image width is one but its content no because its content is rotated

    How to solve this?

    Using the pythagorea theorem .

    Why?
    refer to the tony image above for understand why….

    The hypotenuse of a triangle is the “hypotenuse squared = opposite leg squared plus adjacent leg squard”

    If we solve the hypotenuse

    Hypotenuse = squareRoot( power(oppositeLeg) + power(adjacent Leg) )

    Our dear friend tony divide 1 against the hypotenuse for get a value between 0 and 1, remember his tiles are 1×0.5, you could also devide against (power(oppositeLeg) + power(adjacent Leg)) for the same reason

    Now the new version of the code

    // just the matrix and some transformations
    isoTransform = new Matrix4();
    isoTransform.idt();
    isoTransform.translate(0.0f, 0.25f, 0.0f);

    // this is what I just said about the hypotenuse
    isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
    isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f);

    Now follow the most important part like tony said

    public boolean touchDown(int screenX, int screenY, int pointer, intbutton) {

    // we hava a vector3 for store the position in screen where user clicks
    touch.set(screenX, screenY, 0);

    // unproject convert from screen coordinate to world coordinates
    cam.unproject(touch);

    // remember when I said a matrix in video games is like a Pandora box that
    // transform points when they are multiplied by…. ok here is that pandora box
    // working, we’re getting a screen coordinate converted to world
    // coordinates(remember this is just a point) and later we are multiplying it
    // by a matrix that will translate, scale and rotate any point that
    // it to be multiplied by the matrix
    touch.mul(invIsotransform);

    // now we only need to cast the values and voila!!!
    pickedTileX = (int)touch.x;
    pickedTileY = (int)touch.y;

    return false;
    }

  • ravitandon says:

    great explanation.

    How to work with isometric map with layers and collision detection. Plz help

  • Vincent says:

    Can someone please explain this line better:
    isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);

    I need to change this to use tiles that are 128×64 and im not sure how.

  • Ale says:

    Hi Guys,
    I am trying to move the camera in this example, but i cannot get success
    I used cam.lookAt(…) and other methods, no one works.
    .
    Could you please provide me an example about moving cameras in this isometric scenario. I cannot find good documentation or working examples
    Thanks!

    • Max says:

      Hi Ale,

      If you’re using LibGDX, you can adjust the camera’s position by adjusting camera.position. Example:

      if (Gdx.input.isKeyPressed(Input.Keys.E)) {
      camera.position.x += 2;
      } else if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
      camera.position.x -= 2;
      }

      Good luck!

      Max

    • Max says:

      Oh, and don’t forget to update your camera!

      if (Gdx.input.isKeyPressed(Input.Keys.E)) {
      camera.position.x += 2;
      camera.update();
      } else if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
      camera.position.x -= 2;
      camera.update();
      }

  • Smithc29 says:

    Good blog! I really love how it is easy on my eyes and the data are well written. I am wondering how I could be notified whenever a new post has been made. I have subscribed to your feed which must do the trick! Have a nice day! dddefffbcbceaded

  • Smithe158 says:

    Very informative blog post.Really thank you! Keep writing. bebbdkkdagdedegd

  • without says:

    I appreciate your wordpress web template, exactly where did you down load it through?

  • How To Buy Cheap Generic Ceclor 500mg

  • blog says:

    Write more, thats all I have to say. Literally, it seems as though you relied on the video to make your point. You clearly know what youre talking about, why throw away your intelligence on just posting videos to your weblog when you could be giving us something enlightening to read?

  • I loved your blog article. Really Cool.

  • cialis says:

    what are some superior and in demand websites for blogs? ?? .

  • I value the post.Thanks Again. Awesome.

  • generic says:

    I truly prize your function, Wonderful post.

  • I’m glad that it turned out so effectively and I hope it will continue in the future because it is so worthwhile and meaningful to the community.

  • danger says:

    weblog. Loads of gratitude sharing.

  • dosage says:

    My brother recommended I may like this website. He used to be totally right. This post truly made my day. You can not consider simply how so much time I had spent for this info! Thanks!

  • pharmacy says:

    Excellently written writeup, doubts all bloggers offered the same content material because you, the internet is actually a greater location. Please maintain it up!

  • now says:

    Hahahahahahaha, this politics related YouTube video is really so comical, I loved it. Thanks in favor of sharing this.

  • cheap_cialis says:

    It’s a mammoth playground built of mountains, hills, lakes, rivers, valleys, woodlands,and beaches.

  • loan says:

    It’s truly a great and useful piece of information. I’m satisfied that you shared this helpful information with us. Please stay us informed like this. Thank you for sharing.

  • viagra says:

    Mudbox is a software for 3D sculpting and painting which is developed

  • viagra says:

    I was looking through some of your blog posts on this internet site and I conceive this web site is rattling informative ! Keep on posting .

  • order_cialis says:

    You are my inspiration , I have few web logs and very sporadically run out from to brand.

  • cialis says:

    Appreciate it for helping out, great information.

  • 20 says:

    Howdy very nice web site!! Guy .. Excellent .. Superb .. I’ll bookmark your web site and take the feeds additionallyI am glad to search out so many helpful info here in the put up, we want develop more techniques in this regard, thanks for sharing.

  • buy_cialis says:

    Farmville farms even include free gift that is especially

  • Hi, Neat post. There is a problem with your web site in internet explorer, would check this IE still is the market leader and a large portion of people will miss your magnificent writing because of this problem.

  • buy_viagra says:

    I like the valuable info you provide in your articles. I will bookmark your blog and check again here regularly. I am quite certain I’ll learn a lot of new stuff right here! Best of luck for the next!

  • I’ve long suggested that people seeking to gett a good understanding of this speciific topic spread their research acrooss many blogs

  • soft says:

    I do trust all of the ideas you’ve presented in your post. They’re very convincing and can certainly work. Nonetheless, the posts are very short for beginners. May just you please lengthen them a bit from next time? Thanks for the post.

  • viagra says:

    I have been surfing online more than 3 hours today, yet I never found any interesting article like yours. It is pretty worth enough for me. In my view, if all webmasters and bloggers made good content as you did, the internet will be much more useful than ever before.

  • today says:

    Howdy! I basically would like to give a huge thumbs up for the good data you’ve got here on this post. I will probably be coming once again to your weblog for far more soon.

  • Noel says:

    This did the trick for screen to iso, but how about cartesian to iso? can’t make it work. anyone?

    So i have libgdx and I use a camera, the thing is this article help me to manage screen clicks to iso, but now I have the problem to convert camera coords to Iso, that is cartesian to iso I believe?

  • Hey esto es un gran poste. Puedo utilizar una porcin en ella en mi sitio? Por supuesto ligara a su sitio as que la gente podra leer el artculo completo si ella quiso a. Agradece cualquier manera.

  • loan says:

    You are my inhalation, I have few web logs and rarely run out from post

  • cialis says:

    Hi there! This is my first visit to your blog! We are a group of volunteers and starting a new initiative in a community in the same niche. Your blog provided us beneficial information to work on. You have done a marvellous job!

  • buy says:

    Hi there, I found your web site by the use of Google while searching for a similar matter, your site came up, it seems to be good. I’ve bookmarked it in my google bookmarks.

  • cash says:

    Maintain the excellent job mate. This web blog publish shows how well you comprehend and know this subject.

  • payday says:

    It’s perfect time to make some plans for the long run and it is time to be happy. I have learn this publish and if I may I want to suggest you few attentiongrabbing things or advice. Maybe you could write next articles regarding this article. I desire to read even more things about it!

  • cialis_cheap says:

    I really appreciate this post. I’ve been looking all over for this! Thank goodness I found it on Bing. You’ve made my day! Thanks again!

  • This is really attentiongrabbing, You’re a very professional blogger. I have joined your rss feed and sit up for in search of extra of your fantastic post. Also, I have shared your site in my social networks!

  • cialis|buy says:

    That is some inspirational stuff. Never knew that opinions could be this varied. Be certain to keep writing.

  • loans says:

    Hello! Do you use Twitter? I’d like to follow you if that would be okay. I’m undoubtedly enjoying your blog and look forward to new posts.

  • cialis says:

    I will immediately clutch your rss feed as I can not to find your email subscription link or enewsletter service. Do you have any? Please let me know in order that I may just subscribe. Thanks.

  • viagra says:

    I got what you intend, thankyou for putting up.Woh I am glad to find this website through google. It is a very hard undertaking to seek to please everybody. by Publilius Syrus.

  • Please add more movies related to cooking if you have, because I wish for to learn more and more about all recipes of cooking.

  • I will immediately clutch your rss feed as I can not to find your email subscription link or enewsletter service. Do you have any? Please let me know in order that I may just subscribe. Thanks.

  • What’s up it’s me, I am also visiting this site daily, this website is actually pleasant and the visitors are really sharing fastidious thoughts.

Leave a Reply

css.php