游戏视野计算

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

你可能感兴趣的:(算法,游戏,算法)