游戏场景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
    评论
在C++中,可以使用OpenCV库和相机参数,将世界坐标系中的点转换为图像坐标系中的点。下面是一个简单的示例: 首先,我们需要定义相机参数,包括相机内参和外参: ```cpp cv::Mat camera_matrix = (cv::Mat_<double>(3, 3) << fx, 0, cx, 0, fy, cy, 0, 0, 1); cv::Mat dist_coeffs = (cv::Mat_<double>(1, 5) << k1, k2, p1, p2, k3); cv::Mat rvec = (cv::Mat_<double>(3, 1) << rx, ry, rz); cv::Mat tvec = (cv::Mat_<double>(3, 1) << tx, ty, tz); ``` 其中,fx、fy、cx、cy是相机内参,k1、k2、p1、p2、k3是相机畸变系数,rx、ry、rz是相机旋转向量,tx、ty、tz是相机平移向量。 然后,我们可以将世界坐标系中的点转换为相机坐标系中的点: ```cpp cv::Mat point3d = (cv::Mat_<double>(3, 1) << x, y, z); cv::Mat point3d_cam; cv::projectPoints(point3d, rvec, tvec, camera_matrix, dist_coeffs, point3d_cam); ``` 其中,x、y、z是世界坐标系中的点,point3d是一个3x1的矩阵,存储了点的三维坐标。projectPoints函数将点从世界坐标系中转换为相机坐标系中的点,并进行了相机内参和畸变的校,得到了在相机坐标系中的坐标。 最后,我们可以将相机坐标系中的点转换为图像坐标系中的点: ```cpp cv::Point2d point2d_cam(point3d_cam.at<double>(0, 0), point3d_cam.at<double>(1, 0)); cv::Point2d point2d = cv::Point2d(point2d_cam.x * ratio_x + offset_x, point2d_cam.y * ratio_y + offset_y); ``` 其中,ratio_x、ratio_y、offset_x、offset_y是图像坐标系和相机坐标系之间的转换参数。在实际应用中,可以根据相机标定结果计算得到。 这样,我们就完成了世界坐标系到图像坐标系的转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值