Java 如果我的玩家可以在回合制游戏中以x个动作结束,我应该如何计算每个可能的终点?

Java 如果我的玩家可以在回合制游戏中以x个动作结束,我应该如何计算每个可能的终点?,java,processing,Java,Processing,我目前正在制作一个文明游戏。我计划实现一个功能,在一个给定的单位可以看到每一个可能的移动,它可以与一个给定数量的六边形,它是允许移动。所有可能的端点都用红色圆圈标记。但是,部队不能穿过山脉或水体。我试图通过找出每一个可能的动作组合来实现这一点,而不需要单位进入山或水体,但我不知道如何确定每一个组合 任何单位都可以进入6个方向,东北、北、西北、东南、南、西南。我分配给任何单位的最大移动次数可能会增加到6。再高一点,我担心每次移动一个单元时,处理速度都会变慢 我正在尝试重新创建这个: 我希望在没有黑

我目前正在制作一个文明游戏。我计划实现一个功能,在一个给定的单位可以看到每一个可能的移动,它可以与一个给定数量的六边形,它是允许移动。所有可能的端点都用红色圆圈标记。但是,部队不能穿过山脉或水体。我试图通过找出每一个可能的动作组合来实现这一点,而不需要单位进入山或水体,但我不知道如何确定每一个组合

任何单位都可以进入6个方向,东北、北、西北、东南、南、西南。我分配给任何单位的最大移动次数可能会增加到6。再高一点,我担心每次移动一个单元时,处理速度都会变慢

我正在尝试重新创建这个:

我希望在没有黑色箭头的情况下,结果会是两种可能的运动:

该图像的原始版本:

下面是我用来绘制十六进制网格的代码。在绘制每个六边形之后,其中心的x坐标和y坐标分别存储在XHEX和YHEX中。此外,在生成瓷砖类型(例如草地、海滩)后,瓷砖类型也立即存储在名为hexTypes的数组中。因此,我可以通过引用它的索引,在地图上得到我想要的任何十六进制的x和y坐标以及十六进制的类型

用于绘制单个六边形的代码:

beginShape();
for (float a = PI/6; a < TWO_PI; a += TWO_PI/6) {
  float vx = x + cos(a) * gs*2;
  float vy = y + sin(a) * gs*2;
  vertex(vx, vy);
}
x是六角体中心的x坐标 y是六边形中心的y坐标 gs=六边形的半径

用于在窗口上镶嵌十六进制以创建十六进制网格的代码:

void redrawMap() {
  float xChange = 1.7;
  float yChange = 6;
  for (int y = 0; y < ySize/hexSize; y++) {
    for (int x = 0; x < xSize/hexSize; x++) {
      if (x % 2 == 1) {
        // if any part of this hexagon being formed will be visible on the window and not off the window.
        if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
          drawHex(x*hexSize*xChange, y*hexSize*yChange, hexSize);
        }
// only record and allow player to react with it if the entire tile is visible on the window
        if (x*hexSize*xChange < width && int(y*hexSize*yChange) < height) {
          xHexes.add(int(x*hexSize*xChange));
          yHexes.add(int(y*hexSize*yChange));
        }
      } else {
        if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
          drawHex(x*hexSize*xChange, y*hexSize*yChange+(hexSize*3), hexSize);
        }
        if (x*hexSize*xChange < width && int(y*hexSize*yChange+(hexSize*3)) < height) {
          xHexes.add(int(x*hexSize*xChange));
          yHexes.add(int(y*hexSize*yChange+(hexSize*3)));
        }
      }
    }
  }
}

hexSize是用户为每个六边形指定的大小,确定屏幕上六边形的数量。

您必须使用我们在寻路时使用的类似算法。创建一个堆栈或队列,该堆栈或队列将保存一个类,该类存储十六进制的位置和从该点开始的移动次数,首先插入起始位置和移动次数,并将该十六进制标记为已完成,以避免重复使用已在的位置,然后弹出一个元素,然后插入该十六进制的每一个相邻元素,移动次数为-1。插入零移动的六角体时,这些是端点。在插入任何十六进制之前,请检查是否尚未执行

我希望我是清楚的,你的问题有点模糊,但我试图让你了解这些解决方案通常是如何实现的,而且我认为你的问题更适合于算法而不是处理


祝您好运

您将不得不使用我们在寻路中使用的类似算法。创建一个堆栈或队列,该堆栈或队列将保存一个类,该类存储十六进制的位置和从该点开始的移动次数,首先插入起始位置和移动次数,并将该十六进制标记为已完成,以避免重复使用已在的位置,然后弹出一个元素,然后插入该十六进制的每一个相邻元素,移动次数为-1。插入零移动的六角体时,这些是端点。在插入任何十六进制之前,请检查是否尚未执行

我希望我是清楚的,你的问题有点模糊,但我试图让你了解这些解决方案通常是如何实现的,而且我认为你的问题更适合于算法而不是处理


祝你好运

这个答案将帮助你到达这个绿色是平原,红色是丘陵,蓝色是水,也请不要点燃我可怕的网格:

请注意,在这个解决方案中没有寻路,只有一些非常简单的数学问题可以解决。我将在最后包含草图的完整代码,这样您就可以复制我所做的并自己测试它。最后一件事:这个答案没有使用任何高级设计模式,但它假设您可以熟悉基础知识和面向对象编程。如果我做了一些你不确定你是否理解的事情,你可以也应该问一下

另外:这是一个概念验证,而不是复制粘贴解决方案。我没有你的代码,所以无论如何这不可能是第二件事,但由于你的问题可以以无数种方式解决,这是唯一一个我故意让它尽可能简单和直观的方法,这样你就可以得到这个想法并用它运行

首先,我强烈建议您将瓷砖制作成对象。首先,因为他们需要携带大量的信息,每个瓷砖上都有什么,它们有多难跨越,可能是资源或产量之类的东西。。。我不知道,但是会有很多东西

基础知识 我将全局变量组织为:

// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it

// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;

ArrayList<Tile> _tiles = new ArrayList<Tile>(); // all the tiles
ArrayList<Tile> _canTravel = new ArrayList<Tile>(); // tiles you can currently travel to
瓷砖将更加精致。它将有更多的基本信息:行、列、当前是否选择了为什么不选择、平原、丘陵或水之类的类型、一堆相邻的瓷砖、一种绘制自身的方法以及一种了解单元是否可以通过它的方法:

class Tile extends Drawable {
  int row, column;
  boolean selected = false;
  TileType type;

  ArrayList<Tile> neighbors = new ArrayList<Tile>();

  Tile(int row, int column, TileType type) {
    super(); // this calls the parent class' constructor

    this.row = row;
    this.column = column;
    this.type = type;

    // the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
    position.x = (_tileSize * 1.5) * (column + 1);
    position.y = (_tileSize * 0.5) * (row + 1);
    // this part checks if this is an offset row to adjust the spatial coordinates
    if (row % 2 != 0) {
      position.x += _tileSize * 0.75;
    }
  }

  // this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
  void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
    if (travelPoints >= type.travelCost) {
      // if the unit has enough travel points, we add the tile to the "the unit can get there" list
      if (!_canTravel.contains(this)) {
        // well, only if it's not already in the list
        _canTravel.add(this);
      }
      
      // then we check if the unit can go further
      for (Tile t : neighbors) {
        if (originalTile) {
          t.FillCanTravelArrayList(travelPoints, false);
        } else {
          t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
        }
      }
    }
  }

  void Render() {
    if (isVisible) {
      // the type knows which colors to use, so we're letting the type draw the tile
      type.Render(this);
    }
  }
}
选管会 现在,h瓷砖类型可以自定义,但每个瓷砖都是。。。只要一块瓷砖,随便什么都可以。以下是我在本演示中使用的TileType:

// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
  Plains() {
    this.fill = color(0, 125, 0);
    this.travelCost = 10;
  }
}

class Water extends TileType {
  // here I'm adding a random variable just to show that you can custom those types with whatever you need
  int numberOfFishes = 10;
  
  Water() {
    this.fill = color(0, 0, 125);
    this.travelCost = 1000;
  }
}

class Hill extends TileType {
  Hill() {
    this.fill = color(125, 50, 50);
    this.travelCost = 15;
  }
}
非类方法 我添加了一个鼠标点击的方法,这样我们可以选择一个十六进制来检查装置可以移动多远。在你的游戏中,当你选择一个单位时,所有这些东西都会到位,但这只是一个概念证明,这个单位是虚构的,它的位置在你点击的任何地方

void mouseClicked() {
  // clearing the array which contains tiles where the unit can travel as we're changing those
  _canTravel.clear();

  for (Tile t : _tiles) {
    // select the tile we're clicking on (and nothing else)
    t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
    if (t.selected) {
      // if a tile is selected, check how far the imaginary unit can travel
      t.FillCanTravelArrayList(unitTravelPoints, true);
    }
  }
}
最后,我添加了两个帮助器方法,使事情变得更简单:

// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
  // simple math, but with a twist: I'm not using the square root because it's costly
  // we don't need to know the distance between the center and the point, so there's nothing lost here
  return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}

// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
  float angle = TWO_PI / npoints;
  beginShape();
  for (float a = 0; a < TWO_PI; a += angle) {
    float sx = x + cos(a) * radius;
    float sy = y + sin(a) * radius;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}
旅行是如何计算的 在幕后,程序就是这样知道装置可以移动到哪里的:在本例中,装置有30个移动点。平原10英镑,丘陵15英镑。如果设备剩余的点足够多,则瓷砖会标记为可以移动到那里。每次一块瓷砖在移动距离内,我们也会检查装置是否能离这块瓷砖更远

现在,如果你仍在跟踪我,你可能会问:瓷砖如何知道其他瓷砖是他们的邻居?这是一个很好的问题。我认为检查其坐标的算法是处理此问题的最佳方法,但由于此操作仅在我们创建地图时发生一次,因此我决定采用简单路线,并检查哪些瓷砖在空间上足够接近:

void setup() {
  // create the grid
  for (int i=0; i<_gridWidth; i++) {
    for (int j=0; j<_gridHeight; j++) {
      int rand = (int)random(100);
      if (rand < 20) {
        _tiles.add(new Tile(j, i, new Water()));
      } else if (rand < 50) {
        _tiles.add(new Tile(j, i, new Hill()));
      } else {
        _tiles.add(new Tile(j, i, new Plains()));
      }
    }
  }

  // detect and save neighbor tiles for every Tile
  for (Tile currentTile : _tiles) {
    for (Tile t : _tiles) {
      if (t != currentTile) {
        if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
          currentTile.neighbors.add(t);
        }
      }
    }
  }
}
复制粘贴的完整代码 这是一个地方的全部内容,因此您可以轻松地将其复制并粘贴到处理IDE中,并对其进行操作,其中包括我如何绘制糟糕的网格:

// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it

// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;

ArrayList<Tile> _tiles = new ArrayList<Tile>();
ArrayList<Tile> _canTravel = new ArrayList<Tile>();

void settings() {
  // this is how to make a window size's dynamic
  size((int)(((_gridWidth+1) * 1.5) * _tileSize), (int)(((_gridHeight+1) * 0.5) * _tileSize));
}

void setup() {
  // create the grid
  for (int i=0; i<_gridWidth; i++) {
    for (int j=0; j<_gridHeight; j++) {
      int rand = (int)random(100);
      if (rand < 20) {
        _tiles.add(new Tile(j, i, new Water()));
      } else if (rand < 50) {
        _tiles.add(new Tile(j, i, new Hill()));
      } else {
        _tiles.add(new Tile(j, i, new Plains()));
      }
    }
  }

  // detect and save neighbor tiles for every Tile
  for (Tile currentTile : _tiles) {
    for (Tile t : _tiles) {
      if (t != currentTile) {
        if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
          currentTile.neighbors.add(t);
        }
      }
    }
  }
}

void draw() {
  background(0);

  // show the tiles
  for (Tile t : _tiles) {
    t.Render();
  }

  // show how far you can go
  for (Tile t : _canTravel) {
    fill(0, 0, 0, 0);
    if (t.selected) {
      stroke(255);
    } else {
      stroke(0, 255, 0);
    }
    strokeWeight(5);
    DrawPolygon(t.position.x, t.position.y, _tileSize/2, 6);
  }
}

class Drawable {
  PVector position;
  boolean isVisible;

  public Drawable() {
    position = new PVector(0, 0);
    isVisible = true;
  }

  public void Render() {
    // If you forget to overshadow the Render() method you'll see this error message in your console
    println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
  }
}

class Tile extends Drawable {
  int row, column;
  boolean selected = false;
  TileType type;

  ArrayList<Tile> neighbors = new ArrayList<Tile>();

  Tile(int row, int column, TileType type) {
    super(); // this calls the parent class' constructor

    this.row = row;
    this.column = column;
    this.type = type;

    // the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
    position.x = (_tileSize * 1.5) * (column + 1);
    position.y = (_tileSize * 0.5) * (row + 1);
    // this part checks if this is an offset row to adjust the spatial coordinates
    if (row % 2 != 0) {
      position.x += _tileSize * 0.75;
    }
  }

      // this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
      void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
        if (travelPoints >= type.travelCost) {
          // if the unit has enough travel points, we add the tile to the "the unit can get there" list
          if (!_canTravel.contains(this)) {
            // well, only if it's not already in the list
            _canTravel.add(this);
          }
          
          // then we check if the unit can go further
          for (Tile t : neighbors) {
            if (originalTile) {
              t.FillCanTravelArrayList(travelPoints, false);
            } else {
              t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
            }
          }
        }
      }

  void Render() {
    if (isVisible) {
      // the type knows which colors to use, so we're letting the type draw the tile
      type.Render(this);
    }
  }
}

class TileType {
  // cosmetics
  color fill = color(255, 255, 255);
  color stroke = color(0);
  float strokeWeight = 2;
  // every tile has a "travelCost" variable, how much it cost to travel through it
  int travelCost = 10;

  // while I put this method here, it could have been contained in many other places
  // I just though that it made sense here
  void Render(Tile tile) {
    fill(fill);
    if (tile.selected) {
      stroke(255);
    } else {
      stroke(stroke);
    }
    strokeWeight(strokeWeight);
    DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
    textAlign(CENTER, CENTER);
    fill(255);
    text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
  }
}

// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
  Plains() {
    this.fill = color(0, 125, 0);
    this.travelCost = 10;
  }
}

class Water extends TileType {
  // here I'm adding a random variable just to show that you can custom those types with whatever you need
  int numberOfFishes = 10;

  Water() {
    this.fill = color(0, 0, 125);
    this.travelCost = 1000;
  }
}

class Hill extends TileType {
  Hill() {
    this.fill = color(125, 50, 50);
    this.travelCost = 15;
  }
}


void mouseClicked() {
  // clearing the array which contains tiles where the unit can travel as we're changing those
  _canTravel.clear();

  for (Tile t : _tiles) {
    // select the tile we're clicking on (and nothing else)
    t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
    if (t.selected) {
      // if a tile is selected, check how far the imaginary unit can travel
      t.FillCanTravelArrayList(unitTravelPoints, true);
    }
  }
}

// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
  // simple math, but with a twist: I'm not using the square root because it's costly
  // we don't need to know the distance between the center and the point, so there's nothing lost here
  return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}

// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
  float angle = TWO_PI / npoints;
  beginShape();
  for (float a = 0; a < TWO_PI; a += angle) {
    float sx = x + cos(a) * radius;
    float sy = y + sin(a) * radius;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

希望能有帮助。玩得开心

这个答案将帮助你到达这个绿色是平原,红色是丘陵,蓝色是水,也请不要点燃我可怕的网格:

请注意,在这个解决方案中没有寻路,只有一些非常简单的数学问题可以解决。我将在最后包含草图的完整代码,这样您就可以复制我所做的并自己测试它。最后一件事:这个答案没有使用任何高级设计模式,但它假设您可以熟悉基础知识和面向对象编程。如果我做了一些你不确定你是否理解的事情,你可以也应该问一下

另外:这是一个概念验证,而不是复制粘贴解决方案。我没有你的代码,所以无论如何这不可能是第二件事,但由于你的问题可以以无数种方式解决,这是唯一一个我故意让它尽可能简单和直观的方法,这样你就可以得到这个想法并用它运行

首先,我强烈建议您将瓷砖制作成对象。首先,因为他们需要携带大量的信息,每个瓷砖上都有什么,它们有多难跨越,可能是资源或产量之类的东西。。。我不知道,但是会有很多东西

基础知识 我将全局变量组织为:

// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it

// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;

ArrayList<Tile> _tiles = new ArrayList<Tile>(); // all the tiles
ArrayList<Tile> _canTravel = new ArrayList<Tile>(); // tiles you can currently travel to
瓷砖将更加精致。它将有更多的基本信息:行、列、当前是否选择了为什么不选择、平原、丘陵或水之类的类型、一堆相邻的瓷砖、一种绘制自身的方法以及一种了解单元是否可以通过它的方法:

class Tile extends Drawable {
  int row, column;
  boolean selected = false;
  TileType type;

  ArrayList<Tile> neighbors = new ArrayList<Tile>();

  Tile(int row, int column, TileType type) {
    super(); // this calls the parent class' constructor

    this.row = row;
    this.column = column;
    this.type = type;

    // the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
    position.x = (_tileSize * 1.5) * (column + 1);
    position.y = (_tileSize * 0.5) * (row + 1);
    // this part checks if this is an offset row to adjust the spatial coordinates
    if (row % 2 != 0) {
      position.x += _tileSize * 0.75;
    }
  }

  // this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
  void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
    if (travelPoints >= type.travelCost) {
      // if the unit has enough travel points, we add the tile to the "the unit can get there" list
      if (!_canTravel.contains(this)) {
        // well, only if it's not already in the list
        _canTravel.add(this);
      }
      
      // then we check if the unit can go further
      for (Tile t : neighbors) {
        if (originalTile) {
          t.FillCanTravelArrayList(travelPoints, false);
        } else {
          t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
        }
      }
    }
  }

  void Render() {
    if (isVisible) {
      // the type knows which colors to use, so we're letting the type draw the tile
      type.Render(this);
    }
  }
}
现在,每个瓷砖类型都可以自定义,但每个瓷砖都是。。。只要一块瓷砖,随便什么都可以。以下是我在本演示中使用的TileType:

// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
  Plains() {
    this.fill = color(0, 125, 0);
    this.travelCost = 10;
  }
}

class Water extends TileType {
  // here I'm adding a random variable just to show that you can custom those types with whatever you need
  int numberOfFishes = 10;
  
  Water() {
    this.fill = color(0, 0, 125);
    this.travelCost = 1000;
  }
}

class Hill extends TileType {
  Hill() {
    this.fill = color(125, 50, 50);
    this.travelCost = 15;
  }
}
非类方法 我添加了一个鼠标点击的方法,这样我们可以选择一个十六进制来检查装置可以移动多远。在你的游戏中,当你选择一个单位时,所有这些东西都会到位,但这只是一个概念证明,这个单位是虚构的,它的位置在你点击的任何地方

void mouseClicked() {
  // clearing the array which contains tiles where the unit can travel as we're changing those
  _canTravel.clear();

  for (Tile t : _tiles) {
    // select the tile we're clicking on (and nothing else)
    t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
    if (t.selected) {
      // if a tile is selected, check how far the imaginary unit can travel
      t.FillCanTravelArrayList(unitTravelPoints, true);
    }
  }
}
最后,我添加了两个帮助器方法,使事情变得更简单:

// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
  // simple math, but with a twist: I'm not using the square root because it's costly
  // we don't need to know the distance between the center and the point, so there's nothing lost here
  return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}

// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
  float angle = TWO_PI / npoints;
  beginShape();
  for (float a = 0; a < TWO_PI; a += angle) {
    float sx = x + cos(a) * radius;
    float sy = y + sin(a) * radius;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}
旅行是如何计算的 在幕后,程序就是这样知道装置可以移动到哪里的:在本例中,装置有30个移动点。平原10英镑,丘陵15英镑。如果设备剩余的点足够多,则瓷砖会标记为可以移动到那里。每次一块瓷砖在移动距离内,我们也会检查装置是否能离这块瓷砖更远

现在,如果你仍在跟踪我,你可能会问:瓷砖如何知道其他瓷砖是他们的邻居?这是一个很好的问题。我认为检查其坐标的算法是处理此问题的最佳方法,但由于此操作仅在我们创建地图时发生一次,因此我决定采用简单路线,并检查哪些瓷砖在空间上足够接近:

void setup() {
  // create the grid
  for (int i=0; i<_gridWidth; i++) {
    for (int j=0; j<_gridHeight; j++) {
      int rand = (int)random(100);
      if (rand < 20) {
        _tiles.add(new Tile(j, i, new Water()));
      } else if (rand < 50) {
        _tiles.add(new Tile(j, i, new Hill()));
      } else {
        _tiles.add(new Tile(j, i, new Plains()));
      }
    }
  }

  // detect and save neighbor tiles for every Tile
  for (Tile currentTile : _tiles) {
    for (Tile t : _tiles) {
      if (t != currentTile) {
        if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
          currentTile.neighbors.add(t);
        }
      }
    }
  }
}
复制粘贴的完整代码 这是一个地方的全部内容,因此您可以轻松地将其复制并粘贴到处理IDE中,并对其进行操作,其中包括我如何绘制糟糕的网格:

// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it

// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;

ArrayList<Tile> _tiles = new ArrayList<Tile>();
ArrayList<Tile> _canTravel = new ArrayList<Tile>();

void settings() {
  // this is how to make a window size's dynamic
  size((int)(((_gridWidth+1) * 1.5) * _tileSize), (int)(((_gridHeight+1) * 0.5) * _tileSize));
}

void setup() {
  // create the grid
  for (int i=0; i<_gridWidth; i++) {
    for (int j=0; j<_gridHeight; j++) {
      int rand = (int)random(100);
      if (rand < 20) {
        _tiles.add(new Tile(j, i, new Water()));
      } else if (rand < 50) {
        _tiles.add(new Tile(j, i, new Hill()));
      } else {
        _tiles.add(new Tile(j, i, new Plains()));
      }
    }
  }

  // detect and save neighbor tiles for every Tile
  for (Tile currentTile : _tiles) {
    for (Tile t : _tiles) {
      if (t != currentTile) {
        if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
          currentTile.neighbors.add(t);
        }
      }
    }
  }
}

void draw() {
  background(0);

  // show the tiles
  for (Tile t : _tiles) {
    t.Render();
  }

  // show how far you can go
  for (Tile t : _canTravel) {
    fill(0, 0, 0, 0);
    if (t.selected) {
      stroke(255);
    } else {
      stroke(0, 255, 0);
    }
    strokeWeight(5);
    DrawPolygon(t.position.x, t.position.y, _tileSize/2, 6);
  }
}

class Drawable {
  PVector position;
  boolean isVisible;

  public Drawable() {
    position = new PVector(0, 0);
    isVisible = true;
  }

  public void Render() {
    // If you forget to overshadow the Render() method you'll see this error message in your console
    println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
  }
}

class Tile extends Drawable {
  int row, column;
  boolean selected = false;
  TileType type;

  ArrayList<Tile> neighbors = new ArrayList<Tile>();

  Tile(int row, int column, TileType type) {
    super(); // this calls the parent class' constructor

    this.row = row;
    this.column = column;
    this.type = type;

    // the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
    position.x = (_tileSize * 1.5) * (column + 1);
    position.y = (_tileSize * 0.5) * (row + 1);
    // this part checks if this is an offset row to adjust the spatial coordinates
    if (row % 2 != 0) {
      position.x += _tileSize * 0.75;
    }
  }

      // this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
      void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
        if (travelPoints >= type.travelCost) {
          // if the unit has enough travel points, we add the tile to the "the unit can get there" list
          if (!_canTravel.contains(this)) {
            // well, only if it's not already in the list
            _canTravel.add(this);
          }
          
          // then we check if the unit can go further
          for (Tile t : neighbors) {
            if (originalTile) {
              t.FillCanTravelArrayList(travelPoints, false);
            } else {
              t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
            }
          }
        }
      }

  void Render() {
    if (isVisible) {
      // the type knows which colors to use, so we're letting the type draw the tile
      type.Render(this);
    }
  }
}

class TileType {
  // cosmetics
  color fill = color(255, 255, 255);
  color stroke = color(0);
  float strokeWeight = 2;
  // every tile has a "travelCost" variable, how much it cost to travel through it
  int travelCost = 10;

  // while I put this method here, it could have been contained in many other places
  // I just though that it made sense here
  void Render(Tile tile) {
    fill(fill);
    if (tile.selected) {
      stroke(255);
    } else {
      stroke(stroke);
    }
    strokeWeight(strokeWeight);
    DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
    textAlign(CENTER, CENTER);
    fill(255);
    text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
  }
}

// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
  Plains() {
    this.fill = color(0, 125, 0);
    this.travelCost = 10;
  }
}

class Water extends TileType {
  // here I'm adding a random variable just to show that you can custom those types with whatever you need
  int numberOfFishes = 10;

  Water() {
    this.fill = color(0, 0, 125);
    this.travelCost = 1000;
  }
}

class Hill extends TileType {
  Hill() {
    this.fill = color(125, 50, 50);
    this.travelCost = 15;
  }
}


void mouseClicked() {
  // clearing the array which contains tiles where the unit can travel as we're changing those
  _canTravel.clear();

  for (Tile t : _tiles) {
    // select the tile we're clicking on (and nothing else)
    t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
    if (t.selected) {
      // if a tile is selected, check how far the imaginary unit can travel
      t.FillCanTravelArrayList(unitTravelPoints, true);
    }
  }
}

// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
  // simple math, but with a twist: I'm not using the square root because it's costly
  // we don't need to know the distance between the center and the point, so there's nothing lost here
  return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}

// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
  float angle = TWO_PI / npoints;
  beginShape();
  for (float a = 0; a < TWO_PI; a += angle) {
    float sx = x + cos(a) * radius;
    float sy = y + sin(a) * radius;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

希望能有帮助。玩得开心

您的瓷砖之间的关系如何?它是严格的坐标还是它们是物体?严格的坐标我希望我有你的代码来创建一个漂亮的网格。我会帮忙的,但下次请包括这样的东西:对不起,我是新来的堆栈溢出。我已经附上了密码。如果你还需要什么,请告诉我。谢谢,一切都很好,我把我的编码了,和你的相比真是糟透了,这样我就可以克服它了。如果您有一些停机时间,我建议您
有一点,写得好的问题有更多的机会把答案贴出来。我之所以这么做是因为我喜欢你所做的,但是如果你写的方法正确,你的下一个作品会有更多的人阅读并希望提供帮助。你的作品之间的关系如何?它是严格的坐标还是它们是物体?严格的坐标我希望我有你的代码来创建一个漂亮的网格。我会帮忙的,但下次请包括这样的东西:对不起,我是新来的堆栈溢出。我已经附上了密码。如果你还需要什么,请告诉我。谢谢,一切都很好,我把我的编码了,和你的相比真是糟透了,这样我就可以克服它了。如果你有一些休息时间,我建议你提出一些写得好的问题,这样你就有更多的机会发布答案。我之所以这么做是因为我喜欢你所做的,但是如果你写的方法正确的话,你的下一篇文章将会有更多的人阅读并希望提供帮助。