javascript - 在像系统这样的网格中,得到最接近的方格

  显示原文与译文双语对照的内容
0 0

现在,我有一个类似格子系统的象棋,我想建立一个算法来得到给定范围内的正方形,假设距离应该在一个的交叉点上计算,如

假设圆是中心点,下面是一个示例图像:

Grid system with cross-like distance

我已经找到了这个解决方案( 我正在使用 javascript ):

function findRange(tile, range){
 var tiles = [];
 for(row = 0; row <rows; row++){
 for(col = 0; col <cols; col++){
 if( (Math.abs(row - tile.y) + Math.abs(col - tile.x)) <= range )
 tiles.push([col,row]);
 }
 }
 return tiles;
 }

基本上,我遍历所有的瓦片,然后比较坐标的绝对值差与我的范围之和。 i,但对于某些原因,gimmicky gimmicky gimmicky gimmicky gimmicky gimmicky gimmicky gimmicky gimmicky loop loop tile tile tile tile tile 虽然我正在使用小型网格,但我不认为循环是一个昂贵的操作。

从有利的一面来说,代码是很小的。

我要求我的一个朋友在游戏开发中找到解决这个问题的解决方案,他建议这个( 在 C++ 中):

Node *GetNodeAt(float x, float y)
{ 
 float width = m_nodeSize * m_columns;
 float height = m_nodeSize * m_rows;
 if( x <0.0f || y <0.0f || 
 x> = width || y> = height)
 return nullptr;
 int r = y/m_nodeSize;
 int c = x/m_nodeSize;
 int target = (m_columns*r + c);
 return &m_nodesArray[target];
}
std::list<Node*> GetCrossArea(Node *origin, int range, bool addOriginNode)
{
 std::list<Node*> area;
 Node *n;
 for(int k = range; k> = -range; k--) 
 {
 n = GetNodeAt(origin->GetPosition().x + m_nodeSize * k, origin->GetPosition().y);
 if(k == range || k == -range)
 area.push_back(n);
 else
 {
 if(n!= origin)
 area.push_back(n);
 else if(addOriginNode)
 area.push_back(n);
 Node *nVert;
 int verticalSteps = (range - abs(k));
 for(int q = verticalSteps; q> 0; q--)
 {
 nVert = GetNodeAt(n->GetPosition().x, n->GetPosition().y + m_nodeSize * verticalSteps);
 area.push_back(nVert);
 nVert = GetNodeAt(n->GetPosition().x, n->GetPosition().y + (1 - m_nodeSize) * verticalSteps);
 area.push_back(nVert);
 verticalSteps--;
 }
 } 
 }
 return area;
}

问题

是否有更好的已知算法来解决这个问题? 如果没有,那么所提出的解决方案的? 我是不是在我的方法中遗漏了一些完全明显的东西?

时间:原作者:8个回答

0 0

这是一个答案的提纲。

首先要仔细看一下图表中的模式。 注意,它是垂直的垂直和水平线通过 (0,0) 。 ( 它也是对称的对角线通过同一点,但我会忽略这个时间) 。 当然,我在 relative 中使用 (0,0)

第二,只考虑( relative ) 中心的网络的象限。 要查找距离 1处的单元格,请依次将 1添加到每个坐标中。 距离原点 2处的单元格是距离距离 1距离距离 1的单元格距离的距离。 距离 3处的单元格。 到现在你应该知道。 它是一种递归。

一旦你找到了,中感兴趣细胞的坐标,反映通过( relative ) (0,0)的垂直线和通过同一细胞的水平线。

我忽略了对角线的对称性,因为旋转细胞指数比通过正交轴反射的细胞数更为困难。

我将把它留给一个更好的Javascript程序员,而不是( 或者想成为) 来把它变成代码。

编辑

如果不丢失通用性,则感兴趣区域的原点处于单元格 (0,0),感兴趣的半径为 R 。 然后循环

do r = 0, R
 do s = 0, r
 pushTile([s,r-s])
 end do
end do

如果我做了正确的计算,就按顺序

`(0,0),(0,1),(1,0),(0,2),(1,1),(2,0),(0,3),.. .`

这些是距离 0,1,2,3,... 距离原点的细胞的坐标。

这不是一个递归算法,我认为它可以能是早期的,现在我看不到递归的需求。

一旦循环完成,你就有了它的所有细胞的坐标,包括它的轴,IE的所有坐标都是非负的。 首先对垂直轴进行反射,对于每个具有 +ve x 坐标的单元格,将单元格 (-x,y) 插入到列表中。 然后在水平轴周围反射: 对于每个具有 +ve y 坐标的单元格,将单元格 (x,-y) 添加到列表中。

如果感兴趣区域的原点不是 (0,0),那么计算从中心到 (0,0)的偏移量,运行计算出的above,然后在另一个 direction 中应用偏移。

原作者:
0 0

你只需要按下你需要的瓷砖?

对于所有小于或者等于 range 距离的瓷砖:

function findRange(tile, range){
 var tiles = [];
 starty = Math.max(0, (tile.y - range));
 endy = Math.min(rows - 1, (tile.y + range));
 for(row = starty ; row <= endy ; row++){
 xrange = range - Math.abs(row - tile.y);
 startx = Math.max(0, (tile.x - xrange));
 endx = Math.min(cols - 1, (tile.x + xrange));
 for(col = startx ; col <= endx ; col++){
 tiles.push([col,row]);
 }
 }
 return tiles;
}

基本原理:

要查找满足这里条件的所有单元格,请执行以下操作:

 (Math.abs(row - tile.y) + Math.abs(col - tile.x)) <= range

因为 Math.abs(col - tile.x)的结果永远不会是负的,我们知道我们只需要查看那些满足行的单元格。

 Math.abs(row - tile.y) <= range

这相当于

 tile.y - range <= row <= tile.y + range 

为了考虑网格的边缘,我们必须在 0和 rows - 1 上盖上这些范围,因这里它变成了

 Math.max(0, (tile.y - range)) <= row <= Math.min(rows - 1, (tile.y + range))

所以第一个循环是从 Math.max(0, (tile.y - range)) 并在 Math.min(rows - 1, (tile.y + range))

现在,一旦你选择了一行,我们将考虑哪些列将满足第一个条件:

 (Math.abs(row - tile.y) + Math.abs(col - tile.x)) <= range

这相当于

 Math.abs(col - tile.x) <= range - Math.abs(row - tile.y)

还有更多的equvalent

 tile.x - (range - Math.abs(row - tile.y))
 <= col <= 
 tile.x + (range - Math.abs(row - tile.y))

再一次考虑到边缘

 Math.min(0, (tile.x - (range - Math.abs(row - tile.y)))) 
 <= col <= 
 Math.max(cols - 1, (tile.x + (range - Math.abs(row - tile.y))))

让它变得更清晰一些 (range - Math.abs(row - tile.y)) 把它叫做 xrange

 Math.min(0, (tile.x - xrange)) 
 <= col <= 
 Math.max(cols - 1, (tile.x + xrange))
原作者:
0 0

为此,一个算法实际上不是完全必要的。 你需要知道的就是原点的坐标然后从那里得到正方形。 顺便说一下,砖块的array 必须按行顺序排列,然后为这里工作。

function getSurroundingTiles(tiles, originY, originX) {
 var newTiles = [];
 if (tiles[originY + 1][originX - 1]) {
 newTiles.push(tiles[originY + 1][originX - 1]);
 }
 if (tiles[originY + 1][originX]) {
 newTiles.push(tiles[originY + 1][originX]);
 }
 if (tiles[originY + 1][originX + 1]) {
 newTiles.push(tiles[originY + 1][originX + 1]);
 }
 if (tiles[originY][originX + 1]) {
 newTiles.push(tiles[originY][originX + 1]);
 }
 if (tiles[originY - 1][originX + 1]) {
 newTiles.push(tiles[originY - 1][originX + 1]);
 }
 if (tiles[originY - 1][originX]) {
 newTiles.push(tiles[originY - 1][originX]);
 }
 if (tiles[originY - 1][originX - 1]) {
 newTiles.push(tiles[originY - 1][originX - 1]);
 }
 if (tiles[originY][originX - 1]) {
 newTiles.push(tiles[originY][originX - 1]);
 }
 return newTiles;
}
原作者:
...