【问题标题】:Designing a lookup map设计查找地图
【发布时间】:2016-05-11 09:58:54
【问题描述】:

我在设计正确的映射时遇到了问题。

我正在开发一个自上而下的 rpg 游戏,在该游戏中,您只能在运行时看到地图的部分(块)——当从一个块移动到另一个块时,会加载新的块数据。

根据我的位置加载不同的数据。

目前我的代码看起来像这样(但肯定更长)

public static final int getChunkIdForPos(int x, int y){
    switch (x) {                    
    case 1248:  
        switch (y) {
        case 1247: return R.raw.overworld_512;
        case 1248: return R.raw.overworld_528;
        case 1249: return R.raw.overworld_544;
        default: break;}break;          
    case 1249:  
        switch (y) {
        case 1247: return R.raw.overworld_513;
        case 1248: return R.raw.overworld_529;
        case 1249: return R.raw.overworld_545;
        default: break;}break;
    }
    return R.raw.overworld_161;
}

问题是这段代码不容易阅读并且不容易维护。关于如何做得更好的任何想法?

我正在考虑使用Map<Integer,<Map<Integer,Integer>> 来映射数据,但这需要先创建地图。这也意味着您携带大量“死”对象作为钥匙(SparseArray<SparseArray<>> 也是如此)

注意:您不能按照answer - Implemention a Lookup Table 中的建议通过任何数学来确定返回值

选择这种设计的原因是在不创建任何对象的情况下获得答案 - 查找只是通过两个 switch/case 语句运行并即时提供答案!

【问题讨论】:

  • 如果您使用地图,您希望有多少“死”键?
  • 在每个地图块中封装可达地图的块怎么样?
  • 我在想一张至少有 10.000 x 10.000 块的地图
  • 这个想法是地图块可以由其他玩家提供并帮助创建世界的一部分

标签: java dictionary


【解决方案1】:

如果您使用实现了equalshashcodePoint 类,则可以将它们用作映射中的键。

现在实际的查找机制折叠为:

static final Map<Point, Integer> lookup = new HashMap<>();

static {
    // Looks a lot like your case statement eh?
    lookup.put(new Point(1248, 1247), 512);
    lookup.put(new Point(1248, 1248), 528);
    lookup.put(new Point(1248, 1249), 544);
    lookup.put(new Point(1249, 1247), 513);
    lookup.put(new Point(1249, 1248), 529);
    lookup.put(new Point(1249, 1249), 545);
    // Could also be populated by other statics.
}

public static final int getChunkIdForPos(int x, int y) {
    Integer got = lookup.get(new Point(x, y));
    return got != null ? got : 161;
}

这样做的优雅之处在于地图只构建一次。它是在可预测的时间构建的(第一次引用类),它可以被其他模块添加(通过一些调整)。

这是一个正确实现equalshashcodePoint 类示例。

public static class Point {

    final int x;
    final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "{" + x + "," + y + '}';
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + this.x;
        hash = 97 * hash + this.y;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Point other = (Point) obj;
        if (this.x != other.x) {
            return false;
        }
        if (this.y != other.y) {
            return false;
        }
        return true;
    }

}

【讨论】:

  • 如果我使用我的问题中描述的 switch 语句 no objects 被创建,结果被即时抓取 - 我想不出任何方法来实现这样更好,但这肯定会清理我的代码。
  • @MartinFrank - Java 旨在有效地处理对象,因此除非您在某些嵌入式环境或内存不足的情况下工作,否则成本可以忽略不计。其价值在于以少量内存为代价节省了程序员的时间。注意:static 初始化只发生一次。
  • @MartinFrank - case 方法确实接近最小的时间成本,但 HashMap 查找是 O(1) 并且速度非常接近。
  • 这是一个安卓应用程序,我设计用于旧设备(Android2.3)......这就是我什至考虑的原因
  • 我认为问题 clean codemaintainance 将是选择您的解决方案的原因,即使我不确定是否有更好的解决方案方式...(给我一天左右,我会接受)
【解决方案2】:

好的,第一件事:我将地图从 10.000x10.000 限制为 256x256

  • 代码仍然不可维护!
  • 代码仍然不可扩展!
  • 最好的性能仍然是使用 switch/case

处理此问题的正确方法是使用代码生成器。在一个单独的项目中,我创建了一个世界地图编辑器,这让我可以看到所有块的组合。地图完成后(提示:您现在可以保存/加载地图)代码发射器会为您生成一个类。

魔术是从 codeEmitter 类完成的:

int[][] worldMap = new int[256][256]; //generated via GUI
File file = new File(fileName);
BufferedWriter br = new BufferedWriter(new FileWriter(file));

//writing header

for (int dy = 0; dy < 256; dy++) {
    br.write("    public static int row_"+dy+"(int x){\n");
    br.write("        switch (x) {\n");
    for (int dx = 0; dx < 256; dx++) {
        br.write("        case " + dx + ": return " + worldMap[dx][dy] + ";\n");
    }
    br.write("        default: return 0;\n");
    br.write("        }\n");
    br.write("    }\n");
    br.write("\n");
}

//writing trailer

结果与问题中请求的代码非常相似

 //this part of genCode is not explained above
 public static final int getIdForPos(int x, int y, int area){
    switch (y) {
    case 0: return row_0(x);
    case 1: return row_1(x);
    //.. lots of rows
    default: return 0;
    }
 }

 //but this part is generated with the code above
 public static int row_0(int x){
    switch (x) {
    case 0: return 4711;
    case 1: return 815;
    //.. lots of columns
    default: return 0;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多