【问题标题】:How to Generate a Map in Java?如何在 Java 中生成地图?
【发布时间】:2009-06-22 12:38:19
【问题描述】:

我试图为一个项目完成一个 RPG 游戏,但不知道如何制作游戏地图。 它不需要是图形的,但整个地图和每个图块的代码必须是正确的。

到目前为止,我考虑使用包含所有链接图块的 ArrayList 来制作非矩阵地图(应教授的要求)。

public abstract class Casella {
/** 
 * @uml.property name="tabellone"
 * @uml.associationEnd multiplicity="(1 1)" inverse="casella:Tabellone"
 * @uml.association name="contains"
 */

private int id;
private boolean free = true;
private List adjacent;
private List items;
private Tabellone tabellone = null;

public void in(){
    free = false;
}

public void out(){
    free = true;
}

}

这是单个图块的代码(它有 3 个扩展它的类)。我仍然不知道如何组合和生成地图。

感谢您的宝贵时间。

【问题讨论】:

  • 您能否详细介绍一下这款游戏是什么以及您所说的地图类型?包含有趣地点的世界地图?
  • 每场比赛都会生成地图。它只有一个级别,每个单元格都可以是 3 种不同类型(城市、山丘、平原)中的一种。每个单元格还可以包含一个项目(项目类),它也可以是 4 种不同类型之一。角色每回合移动 1 格。

标签: java map tile


【解决方案1】:

不要从实现开始,从您希望如何使用地图开始。这会给你一些限制如何实现它。例如:

在绘制地图时,如何访问它?通过坐标?还是“从 X 格向西走”?

[编辑] 我建议从 RPG 游戏的主循环开始。您需要将字符/标记放置在某处(即它需要与图块有某种关系)。

然后你需要移动角色。角色必须能够检查当前图块(对手、物品、类型)。它需要一种知道它如何移动的方法(即右边有墙吗?)

这为您的地图提供了一个界面:它为游戏中的其他对象呈现的服务。当您了解接口需要提供什么时,您应该会了解如何实现映射(数据结构)。

至于生成地图,使用随机数生成器加上一些“常识”。看看相邻的瓷砖:当它们都是城市时,这个瓷砖可能也是城市。平原也一样。 Hills 是单数项,而且它们的出现频率最低。

运行此代码,将地图打印为 ASCII(“C”ity、“P”lain、“H”ill),看看它是否有效。

【讨论】:

  • 我对此一无所知。我认为我这样做的方式不需要坐标,因为每个图块都有一个唯一的 ID。但也许使用坐标更好,我真的不知道... =/ 你的建议?
  • 这就是你不知道如何开始的原因。你真的必须坐下来使用你的课程,即使它还不存在。
  • 无论如何您都需要坐标,否则您将拥有一堆彼此没有任何关系的游戏图块。想象一个棋盘:每一个棋子(正方形)都有一个特定的坐标,它们之间的关系是众所周知的并且是固定的。
  • 不正确。旧的冒险游戏只保留了您可以移动的方向列表:北、东、南、西、上等。玩家将有一个指向当前图块的指针并沿着指针移动。坐标仅对显示地图有用,但即便如此,您也可以使用循环图形渲染器。
【解决方案2】:

要在不使用矩阵的情况下生成这样的地图,我建议从中心图块开始,然后使用修改后的广度优先搜索算法向外填充地图。首先,我们需要一些比相邻图块列表更好的东西。您可以简单地有四个变量,一个用于存储下一个图块的每个方向,如下所示:

private Tabellone up = null;
private Tabellone down = null;
private Tabellone left = null;
private Tabellone right = null;

假设我们从最中心的图块开始。你现在要做的就是弄清楚有多少方向是空的,并为每个方向创建一个新的 Tablellone 对象,确保在当前对象中设置每个变量,并在创建的对象中设置适当的相反变量.

Tabellone adj = new Tabellone();
up = adj;
adj.setDown(this);

填写完此图块上的所有说明后,您可以选择已创建的其他图块之一并执行相同的操作。这就是广度优先搜索算法的用武之地。您可以使用队列来遍历您创建的每个图块并填写方向。要使算法停止,只需对要创建的图块数量设置一个限制,并使用计数器来跟踪已创建的图块数量。

int count = 0;
ArrayList<Tabellone> queue = new ArrayList<Tabellone>()
queue.add(/*center tile*/);
while (count < 100) { //if we want 100 tiles
  //take out the center tile from the beginning of the array list, create a tile for each direction and add those tiles to the array list, then increment count by 1.
}

注意:这个算法将创建一个菱形的地图,如果你想要一个正方形,你还需要为每个对角线方向设置一个变量。

当然,如果这看起来比您想要的复杂一点,我建议您使用坐标系。

【讨论】:

  • 哦,这可能是它!我现在就去看看能不能搞定!
  • 我试过这个,但我还有一个问题。我在生成周期中放入了什么?我一直在考虑它,但无法提出正确的代码。然后,我制定了设置相邻图块的方法,如下所示: public void setUp(Casella tile){ up = tile; tile.setDown(this);所以它会立即设置两个图块之间的链接而不会丢失一个。那么,对地图创建周期有什么帮助吗?
  • 无论你在哪里调用setUp,你还必须把你设置的瓦片放到ArrayList中。然后,您只需要移除 ArrayList 中的第一个 tile 并执行相同的操作即可。
  • 我不明白为什么我必须使用 ArrayList,因为我对每个相邻的图块都有单独的变量。为什么我必须删除列表中的第一个图块?我想我并没有真正明白这一点...抱歉打扰了,你能再解释一下吗?
  • 此算法用于生成地图。如果没有这个,您将不得不手动创建每个图块。这被认为是一种高级算法,因此我无法详细研究。这是 Wiki 的链接:en.wikipedia.org/wiki/Breadth-first_search。当然,您仍然可以只使用 for 循环并根据需要创建图块,这只是一种方便的方式。
【解决方案3】:

墙壁、袋子和区域是特殊的容器,可以容纳游戏中的所有墙壁、袋子和区域。

private String level =
          "    ######\n"
        + "    ##   #\n"
        + "    ##$  #\n"
        + "  ####  $##\n"
        + "  ##  $ $ #\n"
        + "#### # ## #   ######\n"
        + "##   # ## #####  ..#\n"
        + "## $  $          ..#\n"
        + "###### ### #@##  ..#\n"
        + "    ##     #########\n"
        + "    ########\n";

这是游戏的关卡。除空格外,共有五个字符。井号 (#) 代表一堵墙。美元 ($) 代表要移动的框。点 (.) 字符表示我们必须移动框的位置。 at 字符 (@) 是推箱子。最后换行符 (\n) 开始世界的新行。

【讨论】:

    【解决方案4】:

    对于这个项目来说,速度或内存是一个大问题吗?如果没有,为什么不使用二维数组?

    类似

    Casella map [][] = new Casella[xSize][ySize];
    

    从这里很容易概念化,只需将其想象为一个 Excel 电子表格,其中每个单元格都是地图上的一个图块。

    虽然你的方式很好,只是有时有点难以概念化。你有一个相邻瓷砖的列表。因此,在创建地图时,您从某个地方开始,可能是左上角,然后从那里开始。

    您可以使用嵌套的 for 循环来设置地图,并帮助确定边缘项目。

    for(int i = 0; i < XSIZE ; ++i)
        for(int j = 0; j < YSIZE ; ++j)
            if(j==0) //found left edge (i.e. no adjacent ones to the left)
            if(j==(YSIZE)) //found right edge (you get the picture) 
    

    使用循环和检查边缘的要点是,除了边缘之外,您需要为每个图块向后和向前、向上和向下链接,在边缘处,您将有 2 或 3 个链接而不是4.

    【讨论】:

    • 备注:你永远不会得到 j==YSIZE true。你的意思是 j == YSIZE - 1?
    • 速度或内存不是问题,因为它只是一个实验室项目。使用数组肯定是理想和最简单的方法,但我的教授不想要矩形(基于矩阵)地图。无论如何,如果我没有其他方法,我可能会坚持这样做,并希望他不会太在意。
    • 嗯,使用数组并不意味着它必须是矩形的,您可以在矩阵中使用代表“无地形”的“间隙”。
    【解决方案5】:

    代码需要正确实际上不是功能要求,因此如果不了解您的游戏/地图的更多信息,很难准确地说出什么是正确的。

    假设您的地图包含 X 个图块且没有有序邻接,则非矩阵解决方案是最好的,一个常见的解决方案是使用非对称邻接的邻接列表或对称邻接的关联列表将其建模为图形.如果您使用关联列表,则需要包含边缘连接的顶点的边缘对象,如果您使用邻接列表,multimap 可能会很酷。

    如果您想要一个具有有序邻接的非矩阵解决方案,AlbertoPL 可以提供解决方案。

    假设您有一张宽 X 瓦片、高 Y 瓦片的地图,并且彼此相邻的瓦片是相邻的,因此每个瓦片最多有 4 个和最少 2 个相邻瓦片,您可以使用矩阵来访问这些瓦片并且还通过矩阵邻接来表示邻接。这个想法是 map[Y][X] 与 map[Y+1][X] 和 map[Y][X+1] 相邻并且相反。 如果图块 [Y+1][X+1] 与图块 [Y][X] 相邻,则此解决方案也可以满足最大 6 个和最小 3 个图块邻接。 这样做的好处是您可以轻松地解析地图,并且由于它具有 2 个维度,因此很自然地可以像这样对其进行建模。 缺点是一旦设置了图块,您就无法在不更改矩阵的情况下更改其邻接关系。由于这不是您教授建议的,您可能不想这样做,但如果您有(静态)有序邻接,这可能是最简单的方法。

    【讨论】:

    • 最初的想法是使用邻接表。现在我正在努力让阿尔贝托的方式奏效。顺便说一句,我理解你的方法,所以谢谢你! ^_^
    【解决方案6】:

    我通过使用邻接列表来管理它。 每个单元格将有一个包含相邻单元格索引的 ArrayList。这些索引是所述单元格在单元格的地图 ArrayList 中的索引。

    http://en.wikipedia.org/wiki/Adjacency_list

    【讨论】:

      【解决方案7】:

      如果是基于图块的地图,那么每个房间与其相邻房间的关系是固定的。您可能不需要担心坐标(尽管如果瓷砖是方形的可能会更简单),但您需要担心方向。

      如果图块是方形的,您可能只需要关心基本方向(n、s、e、w)。如果它们是十六进制图块,您可以将出口编号为 1-6(可能使用静态最终常量)。然后,每个相邻的图块都可以链接到这个图块及其出口。

      【讨论】:

      • 十六进制瓷砖的想法可能是我一直在寻找的。所以它不会生成一个矩形地图(因为教授不想要这样的地图)。我如何生成这样的东西?
      【解决方案8】:

      正如Aaron 所说,您需要先确定映射坐标的方式。

      但您不受 X-Y-Z 坐标系的限制。例如,每个图块都可以链接到地图上的任何其他图块。这完全取决于您想如何构建它。

      您说您正在构建一个 RPG 游戏,那么您需要对角色周围的地形有一个很好的了解。是多层次的吗?角色如何从一块瓷砖移动到另一块瓷砖?运动是一种方式吗?

      从字面上看,在为游戏设计地图时要问几十个问题。

      在开始编码之前,您需要先做好计划。

      【讨论】:

      • 这是一款非常简单的角色扮演游戏。一张地图,杀死地图上的每一个对手,你就赢了。没有升级,但你可以在途中获得新物品。每回合可以移动 1 格。
      猜你喜欢
      • 2016-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多