如何在Java中为2D游戏构建平铺地图?

3

不确定如何解决这个问题。

基本上,我希望将400x400窗口的每个坐标点(例如120x300)作为一个瓷砖,并以像素为单位显示。我的最小精灵是4像素,因此我们可以说1个瓷砖=4个像素。玩家和敌人的精灵都是20 x 20,因此每个玩家/坏人将占用5个瓷砖。

然后我想使用这个地图类来:

  • 通过提供瓷砖的索引/ID来检索玩家/怪物精灵的x/y坐标。

  • 知道边界在哪里,以便它不会将精灵移动到400x400以外而被隐藏。

  • 碰撞检测,了解瓷砖是否为空。

如何实现呢?这里特别讨论x,y->瓷砖或瓷砖索引->x,y转换(以适当的方式绘制精灵)。

3个回答

4
首先,将像素的概念与瓷砖分离开来。像素只涉及表示,而瓷砖则是实际的游戏对象,具有其对游戏的限制。
我发现解开这类事情的一个好方法是先草拟出你想要的粗略API,例如:
public class Board {
  public Board (int width, int height){.. }
  public boolean isOccupied(int x, int y){.. }
  public void moveTo(Point from, Point to) { .. 
    (maybe throws an exception for outofbounds )

“在这里,板子的所有内部单元都是以瓷砖而不是像素为单位的。然后,像素信息可以通过一些内部乘法从瓷砖表示中独立地派生出来。”
  public Point getPixelPosition(int xTilePos, int yTilePos, int pixelsPerTile)..

这段话的意思是:瓷砖可以被内部表示为一个二维数组或单个数组,如果是单个数组,你需要使用一些内部表示方案来将数组映射到棋盘方格上,因此需要使用模算术。

给我一些包含 getPixelPosition() 的代码,我会接受你的答案 :) - Ali
天啊,ClickUpvote要有点眼力了。使用屏幕作为模型不是一个好的设计。应该使用内存模型。颜色和位置是独立的属性。您还可以使用内存模型来存储其他属性。http://www.fastgraph.com/qfrip.html 可以作为一个例子。 - jim
不知道x,y属性的情况下,我如何在屏幕上绘制精灵以指示其从一个瓷砖移动到另一个瓷砖?! - Ali
当您执行drawImage操作时,需要提供x和y属性。 - Ali
从一个代表你区域的二维数组开始。该区域中的值可以表示友方、敌方甚至是背景瓷砖。如果t[x,y]=z,则将瓷砖z贴到屏幕位置x,y上。也许使用20x20瓷砖进行缩放。同样的t[x,y]=z,但使用z20并在x20,y20屏幕上绘制。 - jim

1
/** size of a tile in pixel (for one dimension)*/
int TILE_SIZE_IN_PIXEL = 4;
/** size of a piece in tiles (for one dimension)*/
int PIECE_SIZE_IN_TILE = 5;


public int tileToPixel(int positionInTiles){
    return TILE_SIZE_IN_PIXEL * positionInTiles;
}

/** returns the tile coordinate to which the given tile coordinate belongs 

Note: tileToPixel(pixelToTile(x)) only returns x if x is the upper or left edge of a tile
*/
public int pixelToTile(int positionInPixel){
    return positionInPixel / TILE_SIZE_IN_PIXEL;
}

你可能也需要操作两个参数(x和y)的方法。

对于ID->棋子转换和反之,有多种可用的方法。选择哪一种取决于具体要求(速度、游戏大小等)。因此,请确保隐藏实现细节,以便稍后可以更改它们。

我会从一个非常简单的解决方案开始:

public class Piece{
    /** x position measured in tiles */
    private int x;
    /** y position measured in tiles */
    private int y;

    /** I don't think you need this, but you asked for it. I'd pass around Piece instances instead */
    private final  Long id;

    public void getX(){
        return x;
    }
    public void getY(){
        return y;
    }

    public void getID(){
        return id;
    }

}

public class Board(){
    private Set<Long,Piece> pieces = new HashMap<Piece>(pieces);

    public Piece getPieceOnTile(int tileX, int tileY){ 
        for(Piece piece:pieces){
             if (isPieceOnTile(piece, tileX, tileY)) return piece;
        }
    }

    private boolean isPieceOnTile(piece, tileX, tileY){
        if (piece.getX() < tileX) return false;
        if (piece.getX() > tileX + PIECE_SIZE_IN_TILE) return false;

        if (piece.getY() < tileY) return false;
        if (piece.getY() > tileY + PIECE_SIZE_IN_TILE) return false;

        return true;
    }
}

希望这可以帮助你入门。所有的代码都是在没有编译器的情况下编写的,因此可能会包含打字错误和问题,这些问题可以在创意共用许可下分发。

如果棋子不太多,保持棋子的方法应该很有效。只要大多数棋盘区域不包含棋子,它应该比2D数组更好用。整个算法当前假定没有重叠的棋子。如果需要这些,则getPieceOnTile必须返回一个棋子集合。如果顺序无关紧要,则使用集合;如果有关紧要,则使用列表。


谢谢,但你能告诉我如何根据瓦片编号确定x.y坐标吗?这是我一直没有弄清楚的。 - Ali
这就是tileToPixel方法的作用。 - Jens Schauder

1

简短回答:乘法和模运算。

但如果这让你感到困惑,我建议在尝试编写游戏之前进行一次认真的数学复习。

另外,你的陈述

我的最小精灵是4个像素,所以我们可以说1个瓷砖=4个像素。玩家和敌人精灵都是20 x 20,所以每个玩家/坏家伙将占据5个瓷砖。

对于任何合理的几何形状都不适用。如果你的“1个瓷砖=4个像素”意味着瓷砖是2x2,那么一个玩家需要100个像素,而不是5个。如果你的意思是它们是4x4,那么玩家需要25个像素,但仍然不是5个。


瓦片=像素/4。小精灵(4x4)= 4/4 = 1个瓦片。大精灵(20 x 20)= 20/4 = 5。不是吗?让我困惑的是如何根据瓦片索引(例如5)检索x,y坐标。你能给我举个例子吗? - Ali
20像素 x 20像素 = 400平方像素。一个4x4的瓷砖是16平方像素。400/16 = 在一个400平方像素区域内有25个4x4的瓷砖。 - Paul

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