【SLG游戏服务器开发手册】(ROK Like)沙盘地图篇一,菱形瓦片与笛卡尔坐标系的转换(附调试工具)

在这里插入图片描述
在ROK、SOS、WOS等SLG游戏中,都是使用菱形瓦片(也称为斜方格轴对称网格)进行地图布局。

它的实际角度为arctan(1/2),由于它是一个菱形,所以这里我们称它为菱形瓦片。

在这里插入图片描述
但是我们在屏幕里的视野,就是一个标准正交坐标系,所以很多场景下我们需要进行做一个转换

一、坐标系定义

1. 菱形网格坐标系

  • 轴向坐标:以菱形网格对角方向为基准轴,记作 (q, r)
  • 特征参数:菱形瓦片宽度 w=2a,高度 h=a(宽高比2:1)

2. 笛卡尔坐标系

  • 标准正交坐标系,记作 (x, y)
  • 菱形网格中心点对齐笛卡尔坐标原点
    在这里插入图片描述

二、正向转换(菱形→笛卡尔)

公式:

在这里插入图片描述

几何解释:

  • 横向位移由 q 和 r 共同驱动,纵向位移由两者差值决定
  • 参数 a 为基本单位长度(例如像素尺寸)

示例:

若 a=1,菱形坐标 (3,2) 对应笛卡尔坐标:

在这里插入图片描述


三、逆向转换(笛卡尔→菱形)

公式:

在这里插入图片描述

运算步骤:

在这里插入图片描述

转换代码

//! 瓦片地图
template<Vector2 TILE_SIZE>
class DiamondSlant
{
public:
	/**
	* @brief 地图坐标 -> 世界坐标
	*/
	constexpr Vector2 Map2World(const Point& xy)
	{
		return { (xy[1] + xy[0]) * TILE_SIZE[0] / 2.0,  (xy[1] - xy[0]) * TILE_SIZE[1] / 2.0};
	}
 
	/**
	* @brief 世界坐标 -> 地图坐标
	*/
	constexpr Point World2Map(const Vector2& pos)
	{
		Vector2 xy_div = pos / TILE_SIZE;
		return toPoint(Vector2{ xy_div[0] - xy_div[1] + 0.5, xy_div[0] + xy_div[1] - 0.5 });
	}
};

调试工具

在这里插入图片描述

import tkinter as tk
from math import sqrt

class DiamondGridDebugger:
    def __init__(self, master, cell_size=40, rows=15, cols=15):
        self.master = master
        self.cell_size = cell_size  # 对应参数a
        self.rows = rows
        self.cols = cols

        # Canvas尺寸自适应
        canvas_width = cell_size * 2 * cols
        canvas_height = cell_size * rows
        self.canvas = tk.Canvas(master,
                                width=canvas_width,
                                height=canvas_height,
                                bg='white')
        self.canvas.pack(fill=tk.BOTH, expand=True)

        # 坐标系转换参数
        self.origin_x = canvas_width // 2
        self.origin_y = canvas_height // 2

        # 交互元素
        self.selected_cell = None
        self.canvas.bind("<Motion>", self.highlight_cell)
        self.canvas.bind("<Button-1>", self.select_cell)

        self.draw_grid()

    def qr_to_xy(self, q, r):
        """菱形坐标转笛卡尔坐标(原点在画布中心)"""
        x = self.cell_size * (q + r)
        y = self.cell_size * (r - q) / 2
        return x + self.origin_x, -y + self.origin_y  # Y轴翻转

    def draw_diamond(self, q, r, color='lightgray'):
        """绘制单个菱形单元"""
        center_x, center_y = self.qr_to_xy(q, r)

        # 计算四个顶点(宽高比2:1)
        points = [
            (center_x + self.cell_size, center_y),
            (center_x, center_y + self.cell_size//2),
            (center_x - self.cell_size, center_y),
            (center_x, center_y - self.cell_size//2)
        ]

        # 绘制图形
        cell = self.canvas.create_polygon(
            points,
            outline='gray',
            fill=color,
            tags=('cell', f'cell_{q}_{r}')
        )

        # 添加坐标标签
        label = self.canvas.create_text(
            center_x, center_y,
            text=f'({q},{r})',
            font=('Arial', 8),
            tags=('label', f'label_{q}_{r}')
        )
        return cell

    def draw_grid(self):
        """绘制完整网格"""
        for q in range(-self.cols//2, self.cols//2):
            for r in range(-self.rows//2, self.rows//2):
                self.draw_diamond(q, r)

    def highlight_cell(self, event):
        """鼠标悬停高亮"""
        x = event.x - self.origin_x
        y = self.origin_y - event.y

        # 逆向坐标转换
        q = round((x - 2*y) / (2*self.cell_size))
        r = round((x + 2*y) / (2*self.cell_size))

        if self.selected_cell != (q, r):
            self.canvas.delete('highlight')
            self.draw_diamond(q, r, color='#e0f0ff')
            self.canvas.addtag_withtag('highlight', f'cell_{q}_{r}')
            self.canvas.addtag_withtag('highlight', f'label_{q}_{r}')

    def select_cell(self, event):
        """点击选中单元格"""
        x = event.x - self.origin_x
        y = self.origin_y - event.y

        q = round((x - 2*y) / (2*self.cell_size))
        r = round((x + 2*y) / (2*self.cell_size))

        # 显示详细信息
        info = f"Selected Cell: ({q}, {r})\nCartesian: ({q + r}, {(r - q)/2:.1f})"
        self.canvas.itemconfig('info', text=info)

        # 绘制选中框
        self.canvas.delete('selection')
        center_x, center_y = self.qr_to_xy(q, r)
        self.canvas.create_rectangle(
            center_x - self.cell_size,
            center_y - self.cell_size//2,
            center_x + self.cell_size,
            center_y + self.cell_size//2,
            outline='red',
            tags='selection'
        )

        self.selected_cell = (q, r)

if __name__ == '__main__':
    root = tk.Tk()
    root.title("Diamond Grid Debugger (2:1)")
    debugger = DiamondGridDebugger(root, cell_size=40, rows=15, cols=15)
    root.mainloop()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值