游戏地图是矩形的,将矩形分割成一个个紧邻的大小相等的方形格子,地图中每个对象必然归属于一个格子,每个格子维护了在这个格子里的所有对象。
将每个格子都放入从左下角到右上角的坐标系,设横轴是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();
}
}
}