游戏场景offset坐标系关联正六边形cube坐标系

        MMO中,游戏场景即游戏地图,服务器要做的就是将地图上所有的玩家信息同步。为了减轻服务器压力,根据游戏本身的设定,如能见度,服务器只需根据不同的玩家返回相应的数据即可。

        假设将地图切成了N个矩形块,在 (0, 0) 这个坐标上的玩家,我们只需要给玩家返回 (-1, 1) (0, 1) (1, 1) (-1, 0) (0, 0) (1, 0) (-1, -1) (0, -1) (1, -1) 矩形块上的数据即可。

        但是,使用矩形切片,每次需要计算的场景为 9。如果使用正六边形切片,每次需要计算的场景则为 7。使用正六边形,并不会减少总的数据量,但能减少周围场景服务器的访问次数。而且,相比矩形,正六边形的计算更为复杂。

 

六边形cube坐标系:

六边形区域计算:

将cube坐标系转换成offset坐标系(此处假定step=0.5):

初始化将地图的某个点的相对 cube 坐标传入,并设置步长,计算出 radius 的值。

xy::utils::hexagon::Hexagon::Hexagon(double x, double y, double z, long double step) : cube{x, y, z}, step(step) {
	this->offset = {static_cast<double>((y - x) * step), static_cast<double>(-std::sqrt(3) * z * step)};
	this->radius = (2.0 * std::sqrt(3) / 3.0) * step;
}

根据玩家的绝对坐标,判断玩家是否跨越了此区域。

double xy::utils::hexagon::Hexagon::compute_distance(double x, double y) {
	return std::sqrt(std::pow(x - this->offset.x, 2) + std::pow(y - this->offset.y, 2));
}

bool xy::utils::hexagon::Hexagon::has_in_radius(double x, double y) {
	return this->compute_distance(x, y) <= this->radius ? true : false;
}

如果玩家跨越了此区域,获取玩家当前所在的区域,然后将玩家的数据复制到玩家所在区域去。

这里需要用到 offset 转 cube 的公式。

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube(double x, double y) {
	x -= this->offset.x;
	y -= this->offset.y;
	return this->offset2cube_absolutely(x, y);
}

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube_absolutely(double x, double y) {
	/*
		ax + by = m
		cx + dy = n
		x = (md - bn) / (ad - bc)
		y = (mc - an) / (bc - ad)
	*/
	double a = 1.0, b = -1.0, c = 1.0, d = 1.0;
	double z = -std::sqrt(3) / 3.0 * y / this->step;
	double m = -x / this->step, n = -z;
	x = (d * m - b * n) / (a * d - b * c);
	y = (c * m - a * n) / (b * c - a * d);
	Cube cube {x, y, z};
	return cube;
}

 

如果用 python 的 numpy 结合线性代数解方程组就简单了,可惜 python 运算效率实在太慢,用来做服务器将大大降低承载量。

import numpy
import math

def offset2cube_absolutely(offset_x, offset_y, step=0.5):
    a = numpy.asarray([
        [1, -1],
        [1, 1]
    ])
    z = -(math.sqrt(3) / 3) * offset_y / step
    b = numpy.asarray([-offset_x / step, -z])
    x, y = numpy.linalg.solve(a, b)
    return x, y, z

 

完整代码:

hexagon.h

#ifndef XY_HEXAGON
#define XY_HEXAGON


namespace xy {
	namespace utils {
		namespace hexagon {
			class Hexagon {
			public:
				struct Cube {
					double x, y, z;
				};
			public:
				struct Offset {
					double x, y;
				};
			public:
				Hexagon(double, double, double, long double=0.5);
				~Hexagon();
			private:
				Cube cube;
				Offset offset;
				long double step;
				long double radius;
			public:
				Cube& get_cube();
				Offset& get_offset();
				long double get_step() const;
				long double get_radius() const;
			public:
				double compute_distance(Offset&);
				double compute_distance(double, double);
				bool has_in_radius(Offset&);
				bool has_in_radius(double, double);
				Cube offset2cube(Offset&);
				Cube offset2cube(double, double);
				Cube offset2cube_absolutely(Offset&);
				Cube offset2cube_absolutely(double, double);
			};
		}
	}
}


#endif

hexagon.cpp

#include <cmath>
#include "hexagon.h"


xy::utils::hexagon::Hexagon::Hexagon(double x, double y, double z, long double step) : cube{x, y, z}, step(step) {
	this->offset = {static_cast<double>((y - x) * step), static_cast<double>(-std::sqrt(3) * z * step)};
	this->radius = (2.0 * std::sqrt(3) / 3.0) * step;
}

xy::utils::hexagon::Hexagon::~Hexagon() {}

xy::utils::hexagon::Hexagon::Cube& xy::utils::hexagon::Hexagon::get_cube() {
	return this->cube;
}

xy::utils::hexagon::Hexagon::Offset& xy::utils::hexagon::Hexagon::get_offset() {
	return this->offset;
}

long double xy::utils::hexagon::Hexagon::get_step() const {
	return this->step;
}

long double xy::utils::hexagon::Hexagon::get_radius() const {
	return this->radius;
}

double xy::utils::hexagon::Hexagon::compute_distance(Offset& offset) {
	return this->compute_distance(offset.x, offset.y);
}

double xy::utils::hexagon::Hexagon::compute_distance(double x, double y) {
	return std::sqrt(std::pow(x - this->offset.x, 2) + std::pow(y - this->offset.y, 2));
}

bool xy::utils::hexagon::Hexagon::has_in_radius(Offset& offset) {
	return this->has_in_radius(offset.x, offset.y);
}

bool xy::utils::hexagon::Hexagon::has_in_radius(double x, double y) {
	return this->compute_distance(x, y) <= this->radius ? true : false;
}

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube(Offset& offset) {
	return this->offset2cube(offset.x, offset.y);
}

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube(double x, double y) {
	x -= this->offset.x;
	y -= this->offset.y;
	return this->offset2cube_absolutely(x, y);
}

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube_absolutely(Offset& offset) {
	return this->offset2cube_absolutely(offset.x, offset.y);
}

xy::utils::hexagon::Hexagon::Cube xy::utils::hexagon::Hexagon::offset2cube_absolutely(double x, double y) {
	/*
		ax + by = m
		cx + dy = n
		x = (md - bn) / (ad - bc)
		y = (mc - an) / (bc - ad)
	*/
	double a = 1.0, b = -1.0, c = 1.0, d = 1.0;
	double z = -std::sqrt(3) / 3.0 * y / this->step;
	double m = -x / this->step, n = -z;
	x = (d * m - b * n) / (a * d - b * c);
	y = (c * m - a * n) / (b * c - a * d);
	Cube cube {x, y, z};
	return cube;
}


main.cpp

#include <iostream>
#include <cmath>
#include "hexagon.h"


using namespace std;
using namespace xy::utils::hexagon;


void relatively_demo() {
	Hexagon* hexagon_12_1 = new Hexagon(-1, 2, -1);
	Hexagon* hexagon_20_2 = new Hexagon(2, 0, -2);

	const Hexagon::Cube cube_tmp_0 = hexagon_12_1->offset2cube(hexagon_20_2->get_offset());
	cout << "(2, 0, -2)相对(-1, 2, -1),其偏移量为: x=" << cube_tmp_0.x << " y=" << cube_tmp_0.y << " z=" << cube_tmp_0.z << endl;

	const Hexagon::Cube cube_tmp_1 = hexagon_20_2->offset2cube(hexagon_12_1->get_offset());
	cout << "(-1, 2, -1)相对(2, 0, -2),其偏移量为: x=" << cube_tmp_1.x << " y=" << cube_tmp_1.y << " z=" << cube_tmp_1.z << endl;

	if (hexagon_12_1) {
		delete hexagon_12_1;
	}
	if (hexagon_20_2) {
		delete hexagon_20_2;
	}
}

void absolutely_demo() {
	Hexagon* hexagon = new Hexagon(2, -1, -1);

	const Hexagon::Cube& cube_tmp_0 = hexagon->offset2cube_absolutely(-1.0 / 2.0, 3.0 * sqrt(3) / 2.0);
	cout << "(-1/2, 3*sqrt(3)/2): " << "x=" << cube_tmp_0.x << " y=" << round(cube_tmp_0.y) << " z=" << cube_tmp_0.z << endl;

	const Hexagon::Cube& cube_tmp_1 = hexagon->offset2cube_absolutely(3.0 / 2.0, sqrt(3) / 2.0);
	cout << "(3/2, sqrt(3)/2): " << "x=" << cube_tmp_1.x << " y=" << round(cube_tmp_1.y) << " z=" << cube_tmp_1.z << endl;

	Hexagon::Offset offset_tmp{ -2, -sqrt(3) };

	const Hexagon::Cube& cube_tmp_2 = hexagon->offset2cube_absolutely(offset_tmp);
	cout << "(-2, -sqrt(3): " << "x=" << cube_tmp_2.x << " y=" << round(cube_tmp_2.y) << " z=" << cube_tmp_2.z << endl;

	if (hexagon) {
		delete hexagon;
	}
}


int main() {
	relatively_demo();
	cout << endl;
	absolutely_demo();
	return 0;
}

运行结果:

 

 

正六边形坐标系参考:正六边形网格化(Hexagonal Grids)原理与实现

二元一次方程组解法参考:怎么用C编程语言求解二元一次方程组的解

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值