游戏地图是矩形的,将矩形分割成一个个紧邻的大小相等的方形格子,地图中每个对象必然归属于一个格子,每个格子维护了在这个格子里的所有对象。
将每个格子都放入从左下角到右上角的坐标系,设横轴是x,纵轴是y。
假设地图中所有格子共有row行,column列,那左下角格子的坐标是(0,0),右上角格子的坐标是(column-1,row-1)。
现在有1个对象,要搜集它视野中(也就是与之在一定距离内)的所有对象。
因为对象是用格子管理的,可以先搜集其所在格子周围的格子,然后再判断这些格子里的对象与之的距离是不是在视野距离内。
每个格子周围的格子与其的偏移量都是相同的,可以先算出周围所有格子的偏移量来,确定某个格子后就可以快速计算出周围所有格子的坐标了。
可以用这段程序来计算偏移:
struct offset_node
{int offx;int offy;offset_node(int x, int y):offx(x), offy(y){}
};// vision表示视野有几个格子(的边长)
// offsets中依次是由内而外视野内所有格子相对于当前格子的偏移
void build_offsets(std::vector<offset_node> offsets, const int vision)
{assert(vision > 0);offsets.clear();for(int i=1; i<=vision; ++i){for(int j=-i; j<i; ++j){offsets.emplace_back(j, -i);offsets.emplace_back(i, j);offsets.emplace_back(-j, i);offsets.emplace_back(-i, -j);}}
}
这段程序可以直观的展示计算过程:
void show_neighbors(const int vision)
{constexpr int N = 11; // 地图是NxN的assert(N>0 && N%2 ==1);assert(vision > 0 && vision <= N/2);char **p = new char*[N];for(int i=0; i<N; ++i){char *tmp = new char[N];for(int j=0; j<N; ++j)tmp[j] = '-';p[i] = tmp + N/2;}p += N/2;p[0][0] = 'X'; // 中心位置auto show = [p, N]{int half = N/2;for(int y=half; y>=-half; --y){for(int x=-half; x<=half; ++x)cout << p[x][y] << ' ';cout << endl;}cout << endl;};for(int i=1; i<=vision; ++i){for(int j=-i; j<i; ++j){char c = j + i + '1';p[j][-i] = c;show();p[i][j] = c;show();p[-j][i] = c;show();p[-i][-j] = c;show();}}
}
获得周围所有格子的坐标后,还要检查一下每个格子是不是超过了地图的范围:
// (from_x,from_y)是起始格子的坐标
bool check_grid_valid(int rows, int columns, int from_x, int from_y, const offset_node &offset)
{int x = from_x + offset.offx;if(x < 0 || x >= columns) return false;int y = from_y + offset.offy;if(y < 0 || y >= rows) return false;return true;
}
有时候地图格子数量比较少比如3x2(3行2列),视野距离是3的话,完整视野应是7x7的方形,但实际上x方向视野1,y方向视野2就够了,总视野5x3就可以了。可以对build_offsets函数进行一点改动:
void build_offsets(std::vector<offset_node> offsets, const int rows, const int columns, const int vision)
{assert(vision > 0);offsets.clear();auto insert = [&offsets,rows,columns](int x, int y){if(std::abs(x)<columns && std::abs(y)<rows){offsets.emplace_back(x, y);return true;}return false;};for(int i=1; i<=vision; ++i){if(i >= std::max(rows, columns)) break;for(int j=-i; j<i; ++j){insert(j, -i);insert(i, j);insert(-j, i);insert(-i, -j);}}
}
这个过程同样可以比较直观的展示出来:
void show_neighbors(const int rows, const int columns, const int vision)
{constexpr int N = 21;assert(N>0 && N%2 ==1);assert(vision > 0 && vision <= N/2);assert(rows > 0 && rows <= N-2 && columns > 0 && columns <= N-2);char **p = new char*[N];for(int i=0; i<N; ++i){char *tmp = new char[N];for(int j=0; j<N; ++j)tmp[j] = '.';p[i] = tmp + N/2;}p += N/2;p[0][0] = 'X'; // 中心位置auto show = [p, N]{int half = N/2;for(int y=half; y>=-half; --y){for(int x=-half; x<=half; ++x)cout << p[x][y] << ' ';cout << endl;}cout << endl;};auto set = [p,rows,columns](int x, int y){if(std::abs(x)<columns && std::abs(y)<rows){p[x][y] = '*';}else p[x][y] = '-';};for(int i=1; i<=vision; ++i){if(i >= std::max(rows, columns)) break;for(int j=-i; j<i; ++j){set(j, -i);show();set(i, j);show();set(-j, i);show();set(-i, -j);show();}}
}