最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++瓦片地圖坐標轉(zhuǎn)換的實現(xiàn)詳解

 更新時間:2022年09月05日 09:44:33   作者:略游  
常見的瓦片地圖有矩形、菱形、正六邊形幾種。此文章主要討論菱形瓦片,也就是大家常說的2.5D,斜45度瓦片地圖。比如《紅警2》、《帝國時代2》都是采用這種技術(shù)

一、前言

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

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

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

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

邊緣必然重疊

二、定義

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

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

矩形瓦片示例

三、矩形瓦片

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

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

四、菱形瓦片

1.斜菱形瓦片

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

斜菱形瓦片

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

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

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

//! 斜45度瓦片地圖
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 });
	}
};

2.正菱形瓦片

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

正菱形瓦片

容易得到,從格子坐標到世界坐標,如下:

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

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

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

從格子坐標到世界坐標的代碼如下:

/**
* @brief 地圖坐標 -> 世界坐標
*/
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;
}

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

劃分網(wǎng)格

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

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

單個劃分格子

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

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

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

//! 平菱形瓦片地圖
template<Vector2 TILE_SIZE>
class DiamondFlat
{
public:
	/**
	* @brief 地圖坐標 -> 世界坐標
	*/
	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 世界坐標 -> 地圖坐標
	*/
	constexpr Point World2Map(const Vector2& pos)
	{
		constexpr Vector2 TILE_SIZE_HALF = TILE_SIZE / 2.0;
		//四分之一矩形面積
		constexpr real s = Each::AccumulateMul(TILE_SIZE_HALF);
		//先計算矩形下標
		Point p = toPoint(pos / TILE_SIZE);
		//在矩形內(nèi)坐標
		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)判斷

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

p在對角線上時

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

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

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

;在菱形內(nèi)時

源碼位置:傳送門

到此這篇關(guān)于C++瓦片地圖坐標轉(zhuǎn)換的實現(xiàn)詳解的文章就介紹到這了,更多相關(guān)C++坐標轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解C++ STL vector容量(capacity)和大小(size)的區(qū)別

    詳解C++ STL vector容量(capacity)和大小(size)的區(qū)別

    這篇文章主要介紹了詳解C++ STL vector容量(capacity)和大小(size)的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • C++設(shè)計一個簡單內(nèi)存池的全過程

    C++設(shè)計一個簡單內(nèi)存池的全過程

    利用C/C++開發(fā)大型應(yīng)用程序中,內(nèi)存的管理與分配是一個需要認真考慮的部分,下面這篇文章主要給大家介紹了關(guān)于C++設(shè)計一個簡單內(nèi)存池的全過程,需要的朋友可以參考下
    2021-09-09
  • C語言分裝實現(xiàn)三子棋游戲詳解

    C語言分裝實現(xiàn)三子棋游戲詳解

    這篇文章主要為大家詳細介紹了C語言分裝實現(xiàn)三子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • C語言中棧的兩種實現(xiàn)方法

    C語言中棧的兩種實現(xiàn)方法

    棧的實現(xiàn)方式有兩種,一種方法是使用指針,而另一種方法則是使用數(shù)組,這篇文章主要介紹了C語言中棧的兩種實現(xiàn)方法,需要的朋友可以參考下
    2021-08-08
  • 詳解C++之類和對象(2)

    詳解C++之類和對象(2)

    類是創(chuàng)建對象的模板,一個類可以創(chuàng)建多個對象,每個對象都是類類型的一個變量;創(chuàng)建對象的過程也叫類的實例化。每個對象都是類的一個具體實例(Instance),擁有類的成員變量和成員函數(shù)
    2021-11-11
  • c++中new一個結(jié)構(gòu)體初始化過程

    c++中new一個結(jié)構(gòu)體初始化過程

    這篇文章主要介紹了c++中new一個結(jié)構(gòu)體初始化過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • c++中的4種類型轉(zhuǎn)化方式詳細解析

    c++中的4種類型轉(zhuǎn)化方式詳細解析

    const_cast該函數(shù)用于去除指針變量的常量屬性,將它轉(zhuǎn)換為一個對應(yīng)指針類型的普通變量。反過來,也可以將一個非常量的指針變量轉(zhuǎn)換為一個常指針變量
    2013-10-10
  • C語言實現(xiàn)學(xué)生成績管理系統(tǒng)

    C語言實現(xiàn)學(xué)生成績管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)學(xué)生成績管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 詳解C++中的成員訪問運算符和指針到成員運算符

    詳解C++中的成員訪問運算符和指針到成員運算符

    這篇文章主要介紹了C++中的成員訪問運算符和指針到成員運算符,即. 和 ->以及.* 和 ->*的使用方法,需要的朋友可以參考下
    2016-01-01
  • C++類與對象深入之構(gòu)造函數(shù)與析構(gòu)函數(shù)詳解

    C++類與對象深入之構(gòu)造函數(shù)與析構(gòu)函數(shù)詳解

    朋友們好,這篇播客我們繼續(xù)C++的初階學(xué)習(xí),現(xiàn)在對我們對C++非常重要的一個知識點做出總結(jié),整理出來一篇博客供我們一起復(fù)習(xí)和學(xué)習(xí),如果文章中有理解不當(dāng)?shù)牡胤?還希望朋友們在評論區(qū)指出,我們相互學(xué)習(xí),共同進步
    2022-06-06

最新評論

大庆市| 栾川县| 彩票| 大城县| 平远县| 宣威市| 满城县| 梅河口市| 西乡县| 建宁县| 门头沟区| 宁河县| 陇川县| 平度市| 六枝特区| 荣成市| 绥化市| 洛南县| 新平| 靖江市| 九江县| 永顺县| 阿图什市| 石家庄市| 天全县| 普兰店市| 麻栗坡县| 阿图什市| 大宁县| 清水河县| 响水县| 南安市| 大兴区| 喀喇| 喀喇| 灵宝市| 钦州市| 城口县| 涡阳县| 张北县| 高青县|