Java 使用 Libgdx 的瓦片地图:在鼠标位置找到瓦片

Java tile map using Libgdx: finding tile at mouse position

我看了几个创建瓦片地图的例子,但我无法获得鼠标所指的瓦片位置。

我正在使用 spritebatch 和 GameTile[][] 来创建地图。请记住,瓷砖本身是等距的,而不是真正的正方形。

renderMap() 方法是实际渲染地图的地方。 createMap() 只是为空地图设置初始 GameTiles。

可以使用 Ortho 相机拖动和放大和缩小地图。

缩小也给了我一个问题,瓷砖似乎在点击时移动了

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

对其进行任何操作之前的实际图块;

实际:

期望:

将笛卡尔坐标转换为等距坐标(有点)是这样完成的:

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

公式也需要按 height-to-width 比例缩放,我认为这就是您的代码出错的地方。

给定一个未投影的 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;

上面例子的完整源代码:

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

我无法回应 bornander 的 post,但我的调整会在

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

如果有方块,简单 (int) 转换将在 0 附近提供错误的负值位置,而使用 Math.Floor 将按预期工作。