映射分支切片路径

Suk*_*asa 2 c++ algorithm pathing tile-engine

我正在做一个游戏(并且已经对它提出了几个问题),现在我还有一个问题要问你们.

这个游戏中的关卡格式被设置为Uint16的平铺图(我正在使用SDL),这是tilemapData结构数组的索引.tilemapData结构的一个位是isConductive位/布尔值.

这个位的使用基本上是创建将各种对象连接成一个"powerNet"的路径.我在下面有一些关于当前方法的代码(可行,但我会介绍为什么我真的讨厌它)

void findSetPoweredObjects(unsigned long x, unsigned long y, powerNetInfo * powerNet) {
  //Look for poweredObjs on this tile and set their powerNet to the given powernet
  for (int i = 0; i < level->numChunks[CHUNKTYPE_POWEREDDEF]; i++)
    if (level->poweredObjects[i]->position[0] == x && level->poweredObjects[i]->position[1] == y)
      level->poweredObjects[i]->powerNet = powerNet, powerNet->objectsInNet++;
}

void recursiveCheckTile(bool * isWalked, powerNetInfo * powerNet, unsigned long x, unsigned long y, tilemapData * levelMap) {
  //If out of bounds, return
  if (x < 0 || y < 0 || x >= level->mapDimensions[0] || y >= level->mapDimensions[1]) return;
  //If tile already walked, return
  if (isWalked[x + (y * level->mapDimensions[0])]) return;
  //If tile is nonconductive, return
  if (!(level->tiles[levelMap->map[x + (y * level->mapDimensions[0])]]->flags & TILETYPE_CONDUCTIVE)) return;

  //Valid tile to check, see if there's a poweredobj on the tile (link it to the net if it is) and check the adjacent tiles.
  isWalked[x + (y * level->mapDimensions[0])] = true;

  findSetPoweredObjects(x,y,powerNet);

  recursiveCheckTile(isWalked, powerNet, x - 1, y, levelMap);
  recursiveCheckTile(isWalked, powerNet, x + 1, y, levelMap);
  recursiveCheckTile(isWalked, powerNet, x, y - 1, levelMap);
  recursiveCheckTile(isWalked, powerNet, x, y + 1, levelMap);
}

bool buildPowerNets(void) {
  //Build the powernets used by the powered objects
  //TODO: Rewrite buildPowerNets() & recursiveCheckTile() to avoid stack overflows and make it easier to backtrace powernets in-game
  bool * isWalked;
  isWalked = new bool[(level->mapDimensions[0] * level->mapDimensions[1])];
  unsigned long x, y;
  tilemapData * levelMap = level->layers[level->activeMap];
  for (y = 0; y < level->mapDimensions[1]; y++) {
    for (x = 0; x < level->mapDimensions[0]; x++) {
      if (isWalked[x + (y * level->mapDimensions[0])]) continue;
      isWalked[x + (y * level->mapDimensions[0])] = true;
      if (level->tiles[levelMap->map[x + (y * level->mapDimensions[0])]]->flags & TILETYPE_CONDUCTIVE) {
        //it's conductive, find out what it's connected to.

        //But first, create a new powernet
        powerNetInfo * powerNet = new powerNetInfo;
        powerNet->objectsInNet = 0;
        powerNet->producerId = -1;
        powerNet->supplyType = POWER_OFF;
        powerNet->prevSupplyType = POWER_OFF;
        powerNet->powerFor = 0;

        //Find adjacent tiles to this one, add them to it's powernet, and then mark them walked.  Then repeat until the net is done.
        recursiveCheckTile(isWalked, powerNet, x, y, levelMap);
      }
    }
  }
  delete isWalked;
  for (int i = 0; i < level->numChunks[CHUNKTYPE_POWEREDDEF]; i++)
      if (level->poweredObjects[i]->powerNet == NULL) return false;
  return true;
}
Run Code Online (Sandbox Code Playgroud)

请注意,返回false意味着函数失败(在这种情况下,它没有正确链接所有对象).

我担心的是,由于堆栈溢出,导致导电瓦片走路的功能将在更复杂的地图上失效.如何通过这些功能减轻这种风险有哪些想法?如果需要,我可以提供有关结构的更多信息.

我已经考虑过修改代码,这样recursiveCheckTile只有当它到达一个连接点时才进行递归调用,并且只是按照它的导通路径进行迭代,但是这似乎只是一个部分解决方案,因为我无法提前知道如何扭曲或分支路径.

如果它有所不同,速度在这里完全不重要,因为此功能仅在使用之前处理地图时运行一次,因此使用一点额外时间不会受到伤害.

Joh*_*ica 5

洪水填充

看起来你基本上正在对你的网格进行填充.您可以通过使用需要检查的队列或一堆方块来消除递归.有关伪代码,请参阅维基百科文章的"备用实现"部分.

自己维护队列/堆栈的好处是,当您访问它们时,您将从列表中删除方块,而在递归解决方案中,即使您访问它们之后,方块仍保留在堆栈中.

以下是适用于您的问题的维基百科文章中的"简单"替代实现:

1. Set Q to the empty queue.
2. Add node to the end of Q.
3. While Q is not empty: 
4.     Set n equal to the first element of Q
5.     Remove first element from Q
6.     If n has already been visited:
7.         Go back to step 3.
8.     Mark n as visited.
9.     Add the node to the west to the end of Q.
10.    Add the node to the east to the end of Q.
11.    Add the node to the north to the end of Q.
12.    Add the node to the south to the end of Q.
13. Return.
Run Code Online (Sandbox Code Playgroud)

请注意,您可以使用堆栈或队列来执行此操作.这里有一些很酷且令人着迷的动画,可以直观地显示出差异:

基于队列的洪水填充

洪水填充队列

基于堆栈的洪水填充

洪水填充堆栈

连接组件标签

如果最终在同一网格上有多个电源网,您可能还会发现连接组件标签页很有趣.它基本上可以帮助您弄清楚您是否有多个断开连接的电源网络,当您这样做时,它会告诉您每个方块属于哪个.

连接组件标签示例