Search code examples
javaandroidlibgdxtileisometric

How to get tile position on touching it in isometric libgdx


I'm creating 2d game, it's just an isometric map in libgdx it's 64x32

   public class MyGdxGame extends ApplicationAdapter implements InputProcessor
{


    public static float zoom=0.3f;
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    TiledMapRenderer tiledMapRenderer;
    final Matrix4 matrix = new Matrix4();
    public static float lastx,lasty;
    private IsometricTiledMapRenderer renderer;
    SpriteBatch sb;
    Texture texture;
    Sprite sprite;


    TextureRegion textureRegion;

    public static float translate,pick;

    float mapWidth;
    float mapHeight;
    static String c;
    TiledMapTileLayer.Cell cell;
    TiledMapTileLayer tileLayer;

    private Matrix4 isoTransform;

    private Matrix4 invIsotransform;

    @Override
    public void create () {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        tiledMap = new TmxMapLoader().load("iso.tmx");//iso
        renderer = new IsometricTiledMapRenderer(tiledMap);
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());


        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);

        //... and the inverse matrix
        invIsotransform = new Matrix4(isoTransform);
        invIsotransform.inv();

        camera.update();


        Gdx.input.setInputProcessor(this);

        sb = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("sand_128x64.png"));
        sprite = new Sprite(texture);
        sprite.setSize(50,50);



         mapWidth = tiledMap.getProperties().get("width",Integer.class);
         mapHeight = tiledMap.getProperties().get("height",Integer.class);



    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(.5f, .7f, .9f, 1);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        camera.zoom = zoom;




        camera.update();
        renderer.setView(camera);
        renderer.render();

        sb.setProjectionMatrix(camera.combined);
        sb.begin();
        //sprite.draw(sb);

        sb.end();



    }


    public Vector2 worldToCell(float x, float y) {
        float halfTileWidth = mapWidth * 0.5f;
        float halfTileHeight = mapHeight * 0.5f;

        float row = (1.0f/2) * (x/halfTileWidth + y/halfTileHeight);
        float col = (1.0f/2) * (x/halfTileWidth - y/halfTileHeight);

        return  new Vector2((int)col,(int)row);
    }

    public Vector2 screenToWorld(float x, float y){
        Vector3 touch = new Vector3(x,y,0);
        camera.unproject(touch);
        touch.mul(invIsotransform);
        touch.mul(isoTransform);
        return  new Vector2(touch.x,touch.y);
    }


    public Vector2 screenToCell(float x, float y) {
        Vector2 world = screenToWorld(x,y);
        world.y -= mapHeight *0.5f;
        return worldToCell(world.x,world.y);
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        Vector2 cell = screenToCell(screenX,screenY);

        TiledMapTileLayer layer = (TiledMapTileLayer)tiledMap.getLayers().get("Layer1");

        TiledMapTileLayer.Cell tileCell = layer.getCell((int) cell.x, (int) cell.y);
        TiledMapTile  tile = tileCell.getTile();

        tileCell.setFlipHorizontally(!tileCell.getFlipHorizontally());

        System.out.println("selectedCell = "+cell.toString());
        c=cell.toString();
        tileCell.setTile(tile);



        lastx=-screenX;
        lasty=screenY;
        Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
        Vector3 position = camera.unproject(clickCoordinates);

        translate=position.x;



        return true;
    }





    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        lastx=0;
        lasty=0;
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {

        if (lastx != 0) {
            camera.translate(-screenX - lastx, screenY - lasty);
            lastx = -screenX;
            lasty = screenY;




        }

        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {

        return false;
    }

    @Override
    public boolean scrolled(int amount) {

        return false;
    }

    @Override
    public boolean keyDown(int p1)
    {
        // TODO: Implement this method
        return false;
    }

    @Override
    public boolean keyUp(int p1)
    {
        // TODO: Implement this method
        return false;
    }

    @Override
    public boolean keyTyped(char p1)
    {
        // TODO: Implement this method
        return false;
    }

}

And now I want to pick the tile on touching it, so I get the tile (x, y) , I tried many ways to do this but I couldn't find a good solution, and I tried this

LibGDX: How to make tiled map tiles clickable?

But it didn't work, so what can I do?


Solution

  • The problem is that you can't put clickable squares as isometric maps are rotated squares

    See this article. Or you can use a rotated matrix...

    In create init the Matrixes...

    
        @Override
        public void create () {
            //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);
    
            //... and the inverse matrix
            invIsotransform = new Matrix4(isoTransform);
            invIsotransform.inv();
            ...
            ...
            ...
            ...
    
        }
    

    Add this methods....

    
        public Vector2 worldToCell(float x, float y) {
            float halfTileWidth = TILE_WIDTH * 0.5f;
            float halfTileHeight = TILE_HEIGHT * 0.5f;
    
            float row = (1.0f/2) * (x/halfTileWidth + y/halfTileHeight);
            float col = (1.0f/2) * (x/halfTileWidth - y/halfTileHeight);
    
            return  new Vector2((int)col,(int)row);
        }
    
        public Vector2 screenToWorld(float x, float y){
            Vector3 touch = new Vector3(x,y,0);
            cam.unproject(touch);
            touch.mul(invIsotransform);
            touch.mul(isoTransform);
            return  new Vector2(touch.x,touch.y);
        }
    
    
        public Vector2 screenToCell(float x, float y) {
            Vector2 world = screenToWorld(x,y);
            world.y -= TILE_HEIGHT *0.5f;
            return worldToCell(world.x,world.y);
        }
    

    And finally you would get the tile like this....

    @Override
        public boolean touchDown(int screenX, int screenY, int pointer, int button) {
            Vector2 cell = screenToCell(screenX,screenY);
            System.out.println("selectedCell = "+cell.toString());
    
            //if you want to get the tile and the cell 
            TiledMapTileLayer layer = (TiledMapTileLayer)tiledMap.getLayers().get("Tile Layer 1");
    
            TiledMapTileLayer.Cell tileCell = layer.getCell((int) cell.x, (int) cell.y);
            TiledMapTile           tile     =  tileCell.getTile();
    
            //flip the tile just so you have a visual to make sure your selected the right tile
            tileCell.setFlipHorizontally(!tileCell.getFlipHorizontally());
    
            return true;
        }