使用Libgdx创建Java瓦片地图:查找鼠标位置处的瓦片

5

我看了几个人创建图块映射的示例,但是我无法获得鼠标指向的图块位置。

我正在使用spritebatch和GameTile[][]创建地图。请记住,这些瓷砖本身是等角线的,而不是实际的正方形。

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

通过正交摄像机可以拖动和缩放地图。

缩小视图也会出现问题,单击后瓷砖似乎会被移动。

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

在任何操作之前的实际瓷砖:

进入图像描述

实际:

进入图像描述 期望:

进入图像描述


请注意,本网站不是一个调试服务。将大量代码复制粘贴到问题中并要求我们进行调试是不符合主题的。但是,我们可以帮助您解决有关代码小部分(几行)的具体问题,最好附带MCVE。请阅读如何提问,然后编辑您的问题以缩小其焦点并删除所有与直接相关的代码。例如,只留下存在问题的方法。 - Bohemian
你好,也许你已经考虑过这个问题了,但为什么不在游戏瓦片中使用actor呢?每个瓦片都是一个actor,你可以在瓦片上添加监听器。这将使你避免鼠标位置和瓦片位置之间的映射。 - Ruokki
2个回答

3

将笛卡尔坐标转换为等轴投影的方法(在某种程度上)如下:

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

这个公式还需要按照平铺的高宽比进行缩放,我认为这可能是你代码出错的地方。

给定未投影的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();
    }
}

0

我无法回复bornander的帖子,但我的调整将在此处进行

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

如果有瓦片,使用简单的(int)转换将在负值周围提供错误的位置,而使用Math.Floor将按预期工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接