简体   繁体   English

使用 Libgdx 的 Java 瓷砖地图:在鼠标位置找到瓷砖

[英]Java tile map using Libgdx: finding tile at mouse position

I've looked at several examples of people creating tile maps, and I am unable to get the tile position where my mouse is pointed at.我看过几个人创建瓷砖地图的例子,但我无法获得鼠标指向的瓷砖位置。

I am using a spritebatch and GameTile[][] to create the map.我正在使用 spritebatch 和 GameTile[][] 来创建地图。 Keep in mind that the tiles themselves are isometric and not actually a square.请记住,瓷砖本身是等距的,实际上并不是正方形。

The method renderMap() is where the map is actually is being rendered. renderMap() 方法是实际渲染地图的地方。 createMap() just sets the initial GameTiles for an empty map. createMap() 只是为空地图设置初始 GameTiles。

The map is able to be dragged and zoomed in and out using Ortho camera.可以使用 Ortho 相机拖动和放大和缩小地图。

Zooming out gives me an issue as well, the tiles seem to be shifted over on click缩小也给我带来了一个问题,瓷砖似乎在点击时移动了

public class MapEditor implements GameScene {
    private GameContext context;
    private SpriteBatch batch;
    private OrthographicCamera camera;
    public static GameTile[][] tiles; //GameTile.WIDTH = 64 & GameTile.HEIGHT =48

    public static final int MAP_WIDTH = 20;
    public static final int MAP_HEIGHT = 36;

    public MapEditor(GameContext context) {
        this.context = context;
        tiles = new GameTile[MAP_WIDTH][MAP_HEIGHT];
    }
    @Override
    public void create() {
        renderer = new ShapeRenderer();
        this.batch = new SpriteBatch();
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    }
    public void createMap() {
        // Create the sea tiles
        for (int x = 0; x < MAP_WIDTH; x++) {
            for (int y = 0; y < MAP_HEIGHT; y++) {
                if (y < 3 || y > 32) {
                    if(tiles[x][y] == null) {
                        tiles[x][y] = safezone;
                    }
                }
                else {
                    if(tiles[x][y] == null) {
                        tiles[x][y] = cell;
                    }
                }

            }
        }

    }

   @Override
    public void update(){
        // update the camera
        camera.update();
    }

    @Override
    public void render() {
        batch.setProjectionMatrix(camera.combined);
        batch.begin();
        Gdx.gl.glViewport(0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        renderMap();
        batch.end();
    }
    public int getTileX(float x, float y) {
        /*
         * getRegionWidth() = TILE_WIDTH_HALF 
         * getRegionHeight() = TILE_HEIGHT_HALF
         * these are the ones being added to worldCoords.x/y
        */
        Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
        return (int)((TILE_WIDTH_HALF * ((-TILE_HEIGHT_HALF + (worldCoords.y + TILE_HEIGHT_HALF)) / 
                TILE_HEIGHT_HALF) + (worldCoords.x + TILE_WIDTH_HALF)) / TILE_WIDTH_HALF) / 2;
    }
    
    public int getTileY(float x, float y) {
        /*
         * getRegionWidth() = TILE_WIDTH_HALF 
         * getRegionHeight() = TILE_HEIGHT_HALF
         * these are the ones being added to worldCoords.x/y
        */
        Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
        return (int)(((-TILE_HEIGHT_HALF * (TILE_WIDTH_HALF + (worldCoords.x + TILE_WIDTH_HALF)) / 
                TILE_WIDTH_HALF) + (worldCoords.y + TILE_HEIGHT_HALF)) / TILE_HEIGHT_HALF) / 2;
    }
    
    @Override
    public boolean handleClick(float x, float y, int button) {
        int tileX = getTileX(x,y);
        int tileY = getTileY(x,y);

        System.out.println("Tile:"+tileX + ","+tileY);
    }

    private void renderMap() {
        for (int i = 0; i < tiles.length; i++) {
            for(int j = 0; j < tiles[i].length; j++) {
                TextureRegion region = tiles[i][j].getRegion();
                int x = (i * GameTile.TILE_WIDTH / 2) - (j * GameTile.TILE_WIDTH / 2) - region.getRegionWidth() / 2;
                int y = (i * GameTile.TILE_HEIGHT / 2) + (j * GameTile.TILE_HEIGHT / 2) - region.getRegionHeight() / 2;
                if (canDraw(x, y, GameTile.TILE_WIDTH, GameTile.TILE_HEIGHT)) {
                    batch.draw(region, x, y);
                }
            }
        }
    }

Actual tile before doing anything to it;在做任何事情之前的实际瓷砖;

在此处输入图片说明

Actual:实际的:

在此处输入图片说明 Desired:期望:

在此处输入图片说明

Converting Cartesian coordinates to isometric is (sort of) done like this:将笛卡尔坐标转换为等距坐标(有点)是这样完成的:

float isometricX = cartesianX - cartesianY;
float isometricY = (cartesianX + cartesianY) * 0.5f;

The formula needs to be scaled by the height-to-width ratio of the tiles as well and I think that is where it's going wrong in your code.该公式还需要按图块的高宽比进行缩放,我认为这就是您的代码出错的地方。

Given an unprojected worldMousePosition you can get the coordinates and tile coordinates like this:给定一个未worldMousePosition您可以获得坐标和平铺坐标,如下所示:

    float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;

    float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
    float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
    worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image

    int tileX = (int) worldPosition.x;
    int tileY = (int) worldPosition.y;

在此处输入图片说明

Full source code for the example above:上面例子的完整源代码:

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;

public class SandboxGame extends Game {
    public static final int TILE_NONE = -1;
    public static final int MAP_WIDTH = 20;
    public static final int MAP_HEIGHT = 36;
    public static final int TILE_WIDTH = 64;
    public static final int TILE_HEIGHT = 48;

    private SpriteBatch batch;
    private OrthographicCamera camera;
    private BitmapFont font;
    private Vector3 unprojectVector = new Vector3();
    private Vector2 worldMousePosition = new Vector2();
    private Vector2 worldPosition = new Vector2();
    private Texture[] textures;

    private int[][] tiles = new int[MAP_WIDTH][MAP_HEIGHT];

    @Override
    public void create() {
        batch = new SpriteBatch();
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

        font =  new BitmapFont(Gdx.files.internal("default.fnt"), Gdx.files.internal("default.png"), false);
        textures = new Texture[] {
            new Texture(Gdx.files.internal("tile.png"))
        };

        for(int x = 0; x < MAP_WIDTH; ++x) {
            for(int y = 0; y < MAP_HEIGHT; ++y) {
                int rnd = MathUtils.random(10);
                if (rnd < 1)
                    tiles[x][y] = TILE_NONE;
                else
                    tiles[x][y] = 0;
            }
        }
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 0);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        float scrollSpeed = 64;
        float zoomSpeed = 2;

        float delta = Gdx.graphics.getDeltaTime();
        if (Gdx.input.isKeyPressed(Input.Keys.A))
            camera.position.x -= delta * scrollSpeed;
        if (Gdx.input.isKeyPressed(Input.Keys.D))
            camera.position.x += delta * scrollSpeed;
        if (Gdx.input.isKeyPressed(Input.Keys.W))
            camera.position.y += delta * scrollSpeed;
        if (Gdx.input.isKeyPressed(Input.Keys.S))
            camera.position.y -= delta * scrollSpeed;

        if (Gdx.input.isKeyPressed(Input.Keys.Q))
            camera.zoom = Math.min(camera.zoom + zoomSpeed * delta, 8.0f);
        if (Gdx.input.isKeyPressed(Input.Keys.E))
            camera.zoom = Math.max(camera.zoom - zoomSpeed * delta, 0.5f);

        camera.update();

        int mx = Gdx.input.getX();
        int my = Gdx.input.getY();

        camera.unproject(unprojectVector.set(mx, my, 0.0f));
        worldMousePosition.set(unprojectVector.x, unprojectVector.y);
        float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;

        float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
        float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
        worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image

        int tileX = (int) worldPosition.x;
        int tileY = (int) worldPosition.y;

        batch.setProjectionMatrix(camera.combined);
        batch.begin();

        for (int col = MAP_WIDTH - 1; col >= 0; --col) {
            for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
                if (tiles[col][row] != TILE_NONE) {
                    Texture texture = textures[tiles[col][row]];
                    int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
                    int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
                    batch.setColor(col == tileX && row == tileY ? Color.GRAY : Color.WHITE);
                    batch.draw(texture, x, y);

                }
            }
        }

        if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
            for (int col = MAP_WIDTH - 1; col >= 0; --col) {
                for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
                    int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
                    int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
                    font.draw(batch, String.format("(%d, %d)", col, row), x, y);
                }
            }
        }

        String str = String.format("World position (%.2f, %.2f), Tile (%d, %d)", worldPosition.x, worldPosition.y, (int)worldPosition.x, (int)worldPosition.y);
        font.draw(batch, str, worldMousePosition.x, worldMousePosition.y);
        batch.end();
    }
}

I cant respond to bornander's post, but my tweak would be at我无法回应博纳德的帖子,但我的调整将在

    int tileX = (int) Math.Floor(worldPosition.x);
    int tileY = (int) Math.Floor(worldPosition.y);

Where simple (int) cast will provide wrong position around 0 with negative values, if there are tiles, while using Math.Floor will work as intended.如果简单的 (int) cast 将在 0 附近提供错误的负值位置,如果有瓷砖,而使用 Math.Floor 将按预期工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM