溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

發(fā)布時間:2022-09-05 11:24:13 來源:億速云 閱讀:188 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)”,在日常操作中,相信很多人在C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

    一、前言

    嚴格來說,瓦片的角度并不是45度。因為為了美術(shù)作圖方便,圖片的寬高比一般為2:1,如下圖所示,它的實際角度為arctan(1/2),不過這個數(shù)值對我們不重要。正如魚香肉絲沒有魚一般,叫它45度瓦片也無妨,由于它是一個菱形,所以這里我們稱它為菱形瓦片。

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    寬高比為2:1的菱形瓦片

    或許有人認為任意角度的瓦片都是可以的,其實不然,因為我們要考慮線條鋸齒的畫法,如果采用非整數(shù)比,則線條不是規(guī)律的(非像素游戲或許可以試試)。所以最常見的比例為2:1,其次是1:1。

    還有一個問題,我們觀察菱形的四分之一部分,它將一個矩形一分為二。我們當(dāng)然期望它是平分的,然而這根本做不到,因為它不是理論的對角線。對于正方形瓦片來說,邊緣是不會重疊的。而菱形瓦片不可避免的邊緣存在重疊。

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    邊緣必然重疊

    二、定義

    我們定義地圖上的一個點為世界(World)坐標(biāo),它是連續(xù)的,用浮點數(shù)表示。然后格子的索引叫地圖(Map)坐標(biāo),它是離散的,用有符號整數(shù)表示。不過這里地圖坐標(biāo)的取值未考慮負數(shù),如要使用負數(shù)的地圖坐標(biāo)則需要對代碼略微修改。

    比如下圖的p點,我們假設(shè)格子寬10像素。則其世界坐標(biāo)為(54,67),而地圖坐標(biāo)為(5,6)。

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    矩形瓦片示例

    三、矩形瓦片

    矩形瓦片的代碼很簡單,如下:

    //! 矩形瓦片地圖
    template<Vector2 TILE_SIZE>
    class Rectangle
    {
    public:
    	/**
    	* @brief 地圖坐標(biāo) -> 世界坐標(biāo)
    	*/
    	constexpr Vector2 Map2World(const Point& xy)
    	{
    		return toVector2(xy) * TILE_SIZE;
    	}
    	/**
    	* @brief 世界坐標(biāo) -> 地圖坐標(biāo)
    	*/
    	constexpr Point World2Map(const Vector2& pos)
    	{
    		return toPoint(pos / TILE_SIZE);
    	}
    };

    四、菱形瓦片

    1.斜菱形瓦片

    這里的斜指的是,整個地圖拼出來是斜著的,也是一個菱形,如下圖所示(這是常用的算法):

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    斜菱形瓦片

    我們令x'y'為地圖(格子)坐標(biāo),xy為世界(像素)坐標(biāo),其中wh為瓦片寬高,則有如下關(guān)系:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    上面這個式子通過簡單的變換,就可以得出:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    轉(zhuǎn)換代碼如下,這里就體現(xiàn)出了將瓦片大?。═ILE_SIZE)作為模板的好處了,其中除2的操作會自動合并為常量表達式,世界坐標(biāo)到地圖坐標(biāo)的轉(zhuǎn)換其中加了0.5,是為了四舍五入。

    //! 斜45度瓦片地圖
    template<Vector2 TILE_SIZE>
    class DiamondSlant
    {
    public:
    	/**
    	* @brief 地圖坐標(biāo) -> 世界坐標(biāo)
    	*/
    	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 世界坐標(biāo) -> 地圖坐標(biāo)
    	*/
    	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 });
    	}
    };

    2.正菱形瓦片

    下面這種整體也是一個矩形,它的特點是x軸移動瓦片寬度,y軸只移動半個瓦片高度,當(dāng)y為奇數(shù)時,x再往右移動半個瓦片寬度。

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    正菱形瓦片

    容易得到,從格子坐標(biāo)到世界坐標(biāo),如下:

    當(dāng)y為偶數(shù)時:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    當(dāng)y為奇數(shù)時:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    這里出現(xiàn)和上面不一樣的事了,無法簡單的逆推公式來表示x'y'。因為通過世界(像素)坐標(biāo)無法輕松得到它的地圖(格子)坐標(biāo)的y是奇數(shù)還是偶數(shù)。

    從格子坐標(biāo)到世界坐標(biāo)的代碼如下:

    /**
    * @brief 地圖坐標(biāo) -> 世界坐標(biāo)
    */
    constexpr Vector2 Map2World(const Point& xy)
    {
    	Vector2 pos = { TILE_SIZE[0] * xy[0] , TILE_SIZE[1] / 2 * xy[1] };
    	if (xy[1] % 2 != 0)
    	{//奇數(shù)行向右偏移 w / 2
    		pos[0] += TILE_SIZE[0] / 2;
    	}
    	return pos;
    }

    而從世界坐標(biāo)到格子坐標(biāo)則比較麻煩了,如下,我們劃分網(wǎng)格:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    劃分網(wǎng)格

    明顯格子大小為(w,h),記世界坐標(biāo)pos所在的格子為p,則有:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    來看單個劃分網(wǎng)格內(nèi),如下:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    單個劃分格子

    設(shè)瓦片格子坐標(biāo)為xy,則當(dāng) pos在菱形內(nèi)時,有:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    當(dāng) pos在菱形外時,四個角則分別判斷:右下角偏移(0,1);左下角偏移(-1,1);左上角偏移(-1,-1);右上角偏移(0,-1)。

    所以最終實現(xiàn)代碼如下:

    //! 平菱形瓦片地圖
    template<Vector2 TILE_SIZE>
    class DiamondFlat
    {
    public:
    	/**
    	* @brief 地圖坐標(biāo) -> 世界坐標(biāo)
    	*/
    	constexpr Vector2 Map2World(const Point& xy)
    	{
    		Vector2 pos = { TILE_SIZE[0] * xy[0] , TILE_SIZE[1] / 2 * xy[1] };
    		if (xy[1] % 2 != 0)
    		{//奇數(shù)行向右偏移 w / 2
    			pos[0] += TILE_SIZE[0] / 2;
    		}
    		return pos;
    	}
    	/**
    	* @brief 世界坐標(biāo) -> 地圖坐標(biāo)
    	*/
    	constexpr Point World2Map(const Vector2& pos)
    	{
    		constexpr Vector2 TILE_SIZE_HALF = TILE_SIZE / 2.0;
    		//四分之一矩形面積
    		constexpr real s = Each::AccumulateMul(TILE_SIZE_HALF);
    		//先計算矩形下標(biāo)
    		Point p = toPoint(pos / TILE_SIZE);
    		//在矩形內(nèi)坐標(biāo)
    		Vector2 p1 = pos - toVector2(p) * TILE_SIZE - TILE_SIZE_HALF;
    		//點圍成矩形面積
    		real sp = abs(p1[0] * TILE_SIZE_HALF[1]) + abs(p1[1] * TILE_SIZE_HALF[0]);
    		p[1] *= 2;
    		if (s < sp)
    		{
    			if (p1[0] > 0 && p1[1] > 0)
    				return p + Point{ 0, 1 };
    			else if (p1[0] < 0 && p1[1] > 0)
    				return p + Point{ -1, 1 };
    			else if (p1[0] < 0 && p1[1] < 0)
    				return p + Point{ -1, -1 };
    			else if (p1[0] > 0 && p1[1] < 0)
    				return  p + Point{ 0, -1 };
    			else
    				return p;
    		}
    		else
    		{
    			return p;
    		}
    	}
    };

    五、點在菱形內(nèi)判斷

    如下圖所示,以菱形中心為原點建立坐標(biāo)系:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    p在對角線上時

    當(dāng)p點在菱形上時,紅綠區(qū)域面積相等(對角線平分面積),所以:

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    (紅色區(qū)域加了兩次,將其中變成一個綠色區(qū)域)

    則當(dāng)p點在菱形外時,

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    ;在菱形內(nèi)時

    C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)

    到此,關(guān)于“C++瓦片地圖坐標(biāo)轉(zhuǎn)換如何實現(xiàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    c++
    AI