一、Unity3D中有哪些坐標(biāo)系屿聋?
坐標(biāo)系這個(gè)概念最早是由法國數(shù)學(xué)家笛卡爾提出的咧欣,并由此創(chuàng)造了用代數(shù)方法來研究幾何圖形的數(shù)學(xué)分支——解析幾何。解析幾何的基本思想是將幾何圖形抽象成點(diǎn)的運(yùn)動(dòng)軌跡,從而點(diǎn)可以作為組成圖形的基本元素皮假,而描述一個(gè)點(diǎn)的位置首先需要建立合適的坐標(biāo)系鞋拟。所以,首先我們來了解下Unity3D中都有哪些坐標(biāo)系吧惹资!Unity3D中的坐標(biāo)系目前可以分為以下四類:世界坐標(biāo)贺纲、屏幕坐標(biāo)、視圖坐標(biāo)和GUI坐標(biāo)褪测。下面我們來對(duì)這5類坐標(biāo)進(jìn)行詳細(xì)說明:
世界坐標(biāo)
世界坐標(biāo)按照笛卡爾坐標(biāo)系定義出來的絕對(duì)坐標(biāo)系猴誊,下面的各種坐標(biāo)系都建立在世界坐標(biāo)的基礎(chǔ)上。我們知道二維平面內(nèi)任意一個(gè)點(diǎn)可以用二維坐標(biāo)(x,y)來表示侮措,如果將這個(gè)概念延伸到三維空間內(nèi)懈叹,那么三維空間內(nèi)任意一個(gè)點(diǎn)都可以用三維坐標(biāo)(x,y,z)來表示。這就是世界坐標(biāo)的概念啦分扎,坐標(biāo)系通吵纬桑可以分為左手坐標(biāo)系和右手坐標(biāo)系,而Unity3D采用的是左手坐標(biāo)系畏吓。在Unity3D中我們可以使用transform.position來獲取場(chǎng)景中一個(gè)物體的世界坐標(biāo)墨状,通常情況下編輯器中的Inspector窗口是以世界坐標(biāo)來描述一個(gè)3D物體的位置的,除非當(dāng)一個(gè)3D物體存在父物體的時(shí)候菲饼,它會(huì)以相對(duì)坐標(biāo)來描述其位置歉胶。
屏幕坐標(biāo)
屏幕坐標(biāo)是以像素來定義的,它的范圍是以左下角為(0,0)巴粪,右上角為(Screen.width,Screen.height)定義的這樣一個(gè)矩形通今。屏幕坐標(biāo)是一個(gè)3D坐標(biāo),Z軸是以相機(jī)的世界單位來衡量的肛根。屏幕坐標(biāo)和相機(jī)之間滿足:Screen.width=Camera.pixelWidth和Screen.height=Camera.pixelHeight這兩個(gè)條件辫塌。例如我們將相機(jī)正對(duì)著場(chǎng)景中的原點(diǎn)(0,0,0),相機(jī)的Z軸分量為-10,按照屏幕坐標(biāo)的定義派哲,假設(shè)屏幕為800X640的大小臼氨,則此時(shí)原點(diǎn)轉(zhuǎn)化為屏幕坐標(biāo)后應(yīng)該是(400,320,10)。在Unity3D中我們可以使用WorldToScreenPoint來將一個(gè)世界坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)芭届。
視口坐標(biāo)
視口坐標(biāo)是標(biāo)準(zhǔn)化后的屏幕坐標(biāo)储矩。標(biāo)準(zhǔn)化的概念我們可以引申到向量的標(biāo)準(zhǔn)化中,比如一個(gè)向量(x,y)將過標(biāo)準(zhǔn)化后可以得到一個(gè)單位向量(x’,y’)褂乍。類似地持隧,視口坐標(biāo)是以0到1間的數(shù)字來表示的,它的范圍是以左下角為(0,0),右上角為(1,1)定義的這樣一個(gè)矩形逃片。視口坐標(biāo)是一個(gè)3D坐標(biāo)屡拨,Z軸是以相機(jī)的世界單位來衡量的。通過對(duì)比可以發(fā)現(xiàn)視口坐標(biāo)和屏幕坐標(biāo)特別的相似,所以這里大家可以對(duì)比著來學(xué)習(xí)呀狼。同樣以屏幕坐標(biāo)中的例子來將這里的轉(zhuǎn)換裂允,例如我們將相機(jī)正對(duì)著場(chǎng)景中的原點(diǎn)(0,0,0),相機(jī)的Z軸分量為-10,按照屏幕坐標(biāo)的定義哥艇,假設(shè)屏幕為800X640的大小绝编,則此時(shí)原點(diǎn)轉(zhuǎn)化為屏幕坐標(biāo)后應(yīng)該是(0.5,0.5,10)。在Unity3D中我們可以使用WorldToViewportPoint來將一個(gè)世界坐標(biāo)轉(zhuǎn)換為視口坐標(biāo)貌踏。
GUI坐標(biāo)
GUI坐標(biāo)是指通過OnGUI方法繪制UI時(shí)使用的坐標(biāo)十饥。這個(gè)坐標(biāo)系和屏幕坐標(biāo)類似,它同樣是以像素來定義的哩俭,它的范圍是以左上角為(0,0),右下角為(Screen.width,Screen.height)定義的這樣一個(gè)矩形,GUI坐標(biāo)是一個(gè)[2D]坐標(biāo)(絕對(duì)坐標(biāo))拳恋。因?yàn)镚UI在早期的文章中曾有所設(shè)計(jì)凡资,屬于Unity3D中較為基礎(chǔ)的內(nèi)容,此外我們知道使用絕對(duì)坐標(biāo)來進(jìn)行布局的話是沒有辦法做自適應(yīng)的谬运,所以這部分內(nèi)容我們就不展開講了隙赁,UI自適應(yīng)的一個(gè)主要觀點(diǎn)就是不要使用絕對(duì)坐標(biāo)!不要使用絕對(duì)坐標(biāo)梆暖!不要使用絕對(duì)坐標(biāo)伞访!重要的事情要說三遍的嘛!這里想特別說說Unity4.6以后推出的全新UI支持:uGUI轰驳。在uGUI的Screen Space模式下厚掷,Unity3D的編輯器以屏幕坐標(biāo)來顯示UI元素的位置;在World Space模式下级解,Unity3D的編輯器以世界坐標(biāo)來顯示UI元素的位置冒黑。相信這一點(diǎn)大家在使用的時(shí)候都沒有注意到,最近在看一個(gè)論壇帖子的時(shí)候發(fā)現(xiàn)對(duì)uGUI坐標(biāo)轉(zhuǎn)換的相關(guān)知識(shí)一片茫然勤哗,所以對(duì)這部分內(nèi)容進(jìn)行了深入的研究抡爹,最終得出的結(jié)論是:uGUI的坐標(biāo)本質(zhì)上是特殊的屏幕坐標(biāo),因?yàn)閡GUI的Anchor決定了該坐標(biāo)系的原點(diǎn)芒划,pivot決定了元素本身坐標(biāo)系的原點(diǎn)冬竟,正是這兩個(gè)屬性讓uGUI的坐標(biāo)看起來顯得撲朔迷離。RectTransform組件繼承自Transform民逼,所以它們的position和localPosition是等價(jià)的泵殴,都是世界坐標(biāo);anchoredPosition是UI元素的屏幕坐標(biāo)拼苍,在對(duì)UI元素進(jìn)行操作的時(shí)候應(yīng)該考慮使用這個(gè)坐標(biāo)袋狞。
二、Unity3D各種坐標(biāo)系間的轉(zhuǎn)換
雖然Unity3D里提供了各種坐標(biāo)系間的轉(zhuǎn)換API,可是我們這是在寫博客不是在說API不是苟鸯?所以從實(shí)用性的角度出發(fā)同蜻,我們這里提供了一個(gè)Unity3D各種坐標(biāo)系間轉(zhuǎn)換的FAQ!
1、屏幕坐標(biāo)如何轉(zhuǎn)換為世界坐標(biāo)早处?
在Unity3D中通過camera.ScreenToWorldPoint(Vector3 v)方法可以將一個(gè)屏幕坐標(biāo)轉(zhuǎn)化為世界坐標(biāo)湾蔓。其中,camera是游戲場(chǎng)景中的場(chǎng)景相機(jī)砌梆。通過Input.mousePosition或者 Input.touches[0].position可以獲得鼠標(biāo)或者單根手指的屏幕坐標(biāo)默责。典型的應(yīng)用場(chǎng)景是第一人稱射擊游戲中子彈的發(fā)射和RPG游戲中點(diǎn)擊地面移動(dòng)角色
2、世界坐標(biāo)如何轉(zhuǎn)換為屏幕坐標(biāo)咸包?
在Unity3D中通過camera.WorldToScreenPoint(Vector3 v)方法可以將一個(gè)世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)桃序。其中,camera是游戲場(chǎng)景中的UI相機(jī)烂瘫。比如我們需要將一個(gè)世界坐標(biāo)轉(zhuǎn)換到NGUI坐標(biāo)的時(shí)候媒熊,可以使用場(chǎng)景相機(jī)將世界坐標(biāo)轉(zhuǎn)為屏幕坐標(biāo),然后再利用UI相機(jī)將屏幕坐標(biāo)轉(zhuǎn)換為世界坐標(biāo)坟比,最后再賦值給NGUI控件芦鳍。典型的應(yīng)用場(chǎng)景是NPC頭頂?shù)奈淖诛@示和怪物頭頂?shù)难獥l顯示。
3葛账、屏幕坐標(biāo)如何轉(zhuǎn)換為視口坐標(biāo)柠衅?
在Unity3D中可以通過camera.ScreenToViewportPoint()來將一個(gè)屏幕坐標(biāo)轉(zhuǎn)換為視口坐標(biāo),其中camera是游戲場(chǎng)景中的場(chǎng)景相機(jī)籍琳。當(dāng)我們了解了視口坐標(biāo)本質(zhì)就是屏幕坐標(biāo)的標(biāo)準(zhǔn)化坐標(biāo)以后菲宴,這個(gè)真心沒有什么可說的趋急。
4裙顽、世界坐標(biāo)如何轉(zhuǎn)換為視口坐標(biāo)?
在Unity3D中可以通過camera.WorldToViewportPoint()來將一個(gè)世界坐標(biāo)轉(zhuǎn)換為視口坐標(biāo)宣谈,其中camera是游戲場(chǎng)景中的場(chǎng)景相機(jī)愈犹。當(dāng)我們了解了視口坐標(biāo)本質(zhì)就是屏幕坐標(biāo)的標(biāo)準(zhǔn)化坐標(biāo)以后,這個(gè)真心沒有什么可說的闻丑。
5漩怎、屏幕坐標(biāo)如何轉(zhuǎn)化為GUI坐標(biāo)?
我們注意到屏幕坐標(biāo)和GUI坐標(biāo)的根本差異在于坐標(biāo)系原點(diǎn)選定不同嗦嗡,即屏幕坐標(biāo)的原點(diǎn)在左下角勋锤,而GUI坐標(biāo)的原點(diǎn)在左下角。因此我們可以定義一個(gè)ScreenToGUIPoint方法:
private Vector2 ScreenToGUIPoint(Vector2 v)
{
return new Vector2(v.x,Screen.height-v.y)
}
6侥祭、GUI坐標(biāo)如何轉(zhuǎn)換為屏幕坐標(biāo)叁执?
一個(gè)更為神奇的事情是你可以繼續(xù)調(diào)用ScreenToGUIPoint方法茄厘。
7、屏幕坐標(biāo)如何轉(zhuǎn)換為uGUI坐標(biāo)谈宛?
當(dāng)我們接觸到uGUI以后常常會(huì)遇到類似NGUI需要為uGUI控件坐標(biāo)賦值的[問題]次哈,此時(shí)我們我們有以下兩種思路處理:直接把屏幕坐標(biāo)賦值給transform.position,這種方式簡(jiǎn)單粗暴在ScreenSpace-Overlay模式下沒有問題吆录,然而在ScreenSpace-Camera模式下會(huì)出現(xiàn)因?yàn)樯疃葦?shù)值不正確而引發(fā)的問題窑滞,需要注意(并且需要確保被賦值的物體在Canvas的根節(jié)點(diǎn)下),前段時(shí)間有人在群里問我position和localPosition的區(qū)別恢筝,我就呵呵噠哀卫,現(xiàn)在人都是一般的浮躁啊,寧肯整天鉆研某某插件的用法都不愿意鉆研下API文檔撬槽、計(jì)算機(jī)圖形學(xué)此改、3D數(shù)學(xué)這些東西。通過RectTransformUtility.ScreenPointToWorldPointInRectangle方法將屏幕坐標(biāo)轉(zhuǎn)化為世界坐標(biāo)然后再賦值給transform.position侄柔。兩種方法看起來大同小異共啃,然而這種方法是更為保險(xiǎn)的,因?yàn)樗谶@兩種模式下都能穩(wěn)定地運(yùn)行勋拟。更重要的是它可以和uGUI的事件系統(tǒng)完美地融合勋磕。我猜測(cè)它是直接改變UI元素的世界坐標(biāo)妈候,因?yàn)樗琴x值給transform.position敢靡。可是話說回來苦银,以前給怪物頭頂顯示血條的時(shí)候都是直接把世界坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)再賦值給transform.position啸胧,這樣看來當(dāng)初的想法可能是錯(cuò)誤的呀,如果要給UI控件屏幕坐標(biāo)那不是應(yīng)該給anchoredPosition賦值嗎幔虏?好吧纺念,這塊兒暫時(shí)還沒有搞明白,如果有朋友知道怎么回事想括,可以告訴我陷谱,謝謝!