unity双击打不开脚本_游戏对象和脚本 (创建一个时钟)
該文章是一篇譯文,附上原文鏈接
Game Objects and Scripts?catlikecoding.com在這個(gè)教程中,我們將創(chuàng)建一個(gè)簡(jiǎn)單的時(shí)鐘并編寫(xiě)一個(gè)組件來(lái)顯當(dāng)前時(shí)間。你只需要對(duì)Unity編輯器有最低限度的了解。如果你已經(jīng)使用了幾分鐘并知道如何在場(chǎng)景中導(dǎo)航,那么你就可以開(kāi)始了。
這個(gè)教程假設(shè)你使用的是Unity編輯器版本最低為 2017.1.0。
創(chuàng)建一個(gè)時(shí)鐘打開(kāi)Unity并新建一個(gè)3D工程。你不需要任何額外的資源包也不需要任何分析。如果你還沒(méi)有定制編輯器,你將得到它的默認(rèn)窗口布局。
默認(rèn)窗口布局我使用的是另一種窗口布局,2x3的預(yù)設(shè)布局,你可以從編輯器右上角的下拉列表中選擇。我通過(guò)將項(xiàng)目窗口切換到一個(gè)列布局來(lái)進(jìn)一步定制該視圖,這樣更適合它的垂直方向。你可以通過(guò)工具欄上方窗口右上角的lock圖標(biāo)旁邊的下拉列表更改它。我通常在場(chǎng)景窗口中通過(guò)Gizmos下拉菜單禁用Show Grid。
自定義2x3窗口布局Ps:為何我的游戲窗口中有黑邊框?
當(dāng)使用高分辨率顯示時(shí)就會(huì)發(fā)生這種情況,要使其展開(kāi)并填充整個(gè)游戲窗口,請(qǐng)打開(kāi)游戲窗口上的aspect-ratio下拉菜單并禁用Low Resolution Aspect Ratios選項(xiàng)。
禁用 Low resolution aspect ratios1.1 創(chuàng)建一個(gè)游戲?qū)ο?/p>
默認(rèn)場(chǎng)景包含兩個(gè)游戲?qū)ο蟆K鼈冴惲性贖ierarchy窗口中,你還可以在Scene窗口中看到它們的圖標(biāo)。第一個(gè)是用于渲染場(chǎng)景的Main Camera。Game窗口就是用這個(gè)攝像機(jī)渲染的。第二個(gè)是照亮場(chǎng)景的Directional Light。
使用GameObject->Create Empty菜單創(chuàng)建你自己的游戲?qū)ο蟆D氵€可以通過(guò)Hierarchy窗口中上下文菜單來(lái)完成。這將向場(chǎng)景中添加一個(gè)新對(duì)象,你可以立即為其命名。由于我們將要?jiǎng)?chuàng)建一個(gè)時(shí)鐘,所以命名為Clock。
擁有一個(gè)Clock對(duì)象的Hierarchy窗口Inspector窗口顯示游戲?qū)ο蟮募?xì)節(jié)。當(dāng)我們選中clock對(duì)象時(shí),它將包含一個(gè)有對(duì)象名稱的標(biāo)題以及一些配置選項(xiàng)。默認(rèn)情況下,對(duì)象是可用的,非靜態(tài)的,沒(méi)有tag屬于default 層。這些設(shè)置對(duì)我們是友好的。在下面的列表中,它顯示了游戲?qū)ο蟮乃薪M件。總有一個(gè)Transform組件,這是我們的時(shí)鐘對(duì)象當(dāng)前擁有的所有組件。
Clock對(duì)象選中時(shí)的Inspector窗口Transform組件包含3D空間中的位置,旋轉(zhuǎn)和縮放信息。確保clock對(duì)象的位置和旋轉(zhuǎn)為0,縮放應(yīng)當(dāng)為1.
Ps:那么2D對(duì)象呢?
當(dāng)在2D而不是3D中工作時(shí),你可以忽略其中的一維坐標(biāo)。專門針對(duì)2D對(duì)象(例如UI元素)通常有一個(gè)Rect Transform 組件,這是一個(gè)特殊的Transform組件。
1.2 創(chuàng)建時(shí)鐘的表面
盡管我們已經(jīng)有了時(shí)鐘對(duì)象,但是我們還不能看見(jiàn)任何東西。我們將為它添加3D模型以便它們能夠渲染。Unity包含一些基本對(duì)象,我們可以用它們來(lái)構(gòu)建一個(gè)簡(jiǎn)單的時(shí)鐘。讓我們通過(guò)GameObject->3D Object->Cylinder菜單向場(chǎng)景中添加一個(gè)cylinder對(duì)象。確保它的Transform組件和Clock對(duì)象擁有相同的值。
一個(gè)表示圓柱體的游戲?qū)ο?p>這個(gè)新的對(duì)象比一個(gè)空對(duì)象多了三個(gè)組件。首先,它有一個(gè)包含對(duì)內(nèi)置圓柱體mesh引用的Mesh Filter。其次是用于3D物理的膠囊碰撞體。第三是Mesh Render。這是一個(gè)確保對(duì)象mesh能夠被渲染的組件。它還控制用于渲染的材質(zhì),這是內(nèi)置的默認(rèn)材質(zhì),除非你改變它。該材質(zhì)也顯示在inspector組件列表的下面。盡管對(duì)象表示一個(gè)圓柱體,但是他擁有一個(gè)膠囊碰撞體,因?yàn)閁nity沒(méi)有一個(gè)原始的圓柱體碰撞器。我們不需要它,所以我們可以移除這個(gè)組件。如果你想在你的時(shí)鐘上使用物理屬性,你最好使用Mesh Collider組件。組件可以通過(guò)右上角的齒輪圖標(biāo)的下拉菜單移除。
沒(méi)有碰撞體為了把圓筒變成時(shí)鐘的表面,我們需要壓扁它。這是通過(guò)減小Y分量的縮放來(lái)實(shí)現(xiàn)的。將Y分量的值減小到0.1。由于cylinder mesh的高度是兩個(gè)單元,所以它現(xiàn)在的有效高度是0.2個(gè)單元。我們需要做一個(gè)大時(shí)鐘,所以我們把X和Z分量的縮放值增加到10。
一個(gè)縮放過(guò)的圓柱體由于圓柱體代表時(shí)鐘的表面,修改它名字為Face。它是時(shí)鐘的一部分,所以讓它成為時(shí)鐘對(duì)象的子節(jié)點(diǎn)。你可以通過(guò)將Face拖到Hierarchy窗口中的時(shí)鐘對(duì)象之上來(lái)實(shí)現(xiàn)這一點(diǎn)。
Face成為子節(jié)點(diǎn)子對(duì)象受制于其父對(duì)象的變換。這意味著當(dāng)Clock對(duì)象位置改變時(shí),Face對(duì)象也會(huì)跟著一起改變。就好像它們是一個(gè)單一的實(shí)體。縮放和旋轉(zhuǎn)也是如此。你可以通過(guò)這個(gè)特性來(lái)創(chuàng)建復(fù)雜的對(duì)象層次結(jié)構(gòu)。
1.3 創(chuàng)建時(shí)鐘的外圍
時(shí)鐘表面的外圈通常有標(biāo)記用來(lái)幫助指示時(shí)鐘顯示的時(shí)間。這就是時(shí)鐘的外圍。讓我們用塊來(lái)表示時(shí)鐘的12小時(shí)。
通過(guò)GameObject->3D Object->Cube向場(chǎng)景中添加一個(gè)立方體。將它的縮放設(shè)為(0.5,0.2,1),這樣它就變成了一個(gè)狹長(zhǎng)的扁平塊。它現(xiàn)在位于時(shí)鐘表面的內(nèi)部。將它的位置設(shè)為(0,0.2,4)。將它放在Face的上面和12小時(shí)對(duì)應(yīng)的一邊。將它命名為Hour Indicator。
12小時(shí)指示器指示器很難看見(jiàn),因?yàn)樗蜁r(shí)鐘表面有相同的顏色。讓我們通過(guò)Assets->Create->Material或Project窗口中的上下文菜單為它創(chuàng)建一個(gè)其他的材質(zhì)。這為我們提供了一個(gè)默認(rèn)材質(zhì)副本的材質(zhì)資源。把它的Albedo值改為深一點(diǎn)的,例如rgb設(shè)為73。這就得到了一個(gè)深灰色的材質(zhì)。給它一個(gè)適當(dāng)?shù)拿?#xff0c;例如Clock Dark。
Dark材質(zhì)資源和顏色值Ps:什么是albedo?
Albedo是一個(gè)拉丁詞,意思為白色。它僅僅是材質(zhì)的顏色。
讓Hour Indicator 對(duì)象使用這個(gè)材質(zhì)。你可以通過(guò)將這個(gè)材質(zhì)拖放到場(chǎng)景或者Hierarchy窗口中的對(duì)象上來(lái)實(shí)現(xiàn)這一點(diǎn)。你也可以把它拖拽到Inspector窗口的底部,或者改變Mesh Render材質(zhì)數(shù)組的第一個(gè)元素。
使用Dark材質(zhì)的小時(shí)指示器我們的指示器正確定位到12點(diǎn),但是如果我們想要表示1點(diǎn)呢?因?yàn)橛?2個(gè)小時(shí),一個(gè)完整的圓是360°,所以我們必須把指示器繞Y軸旋轉(zhuǎn)30°。
旋轉(zhuǎn)小時(shí)指示器,位置不正確雖然這給出了正確的方向,但是指示器仍然在12小時(shí)的位置。這是因?yàn)橐粋€(gè)對(duì)象的旋轉(zhuǎn)是相對(duì)于它自己的局部原點(diǎn),也就是它自己的局部坐標(biāo)。
我們必須沿著時(shí)鐘表面的邊緣移動(dòng)指示器使其與1小時(shí)對(duì)齊。我們可以利用對(duì)象層次結(jié)構(gòu)來(lái)代替自己計(jì)算這個(gè)位置。首先把指示器的旋轉(zhuǎn)重置為0.然后創(chuàng)建一個(gè)位置和旋轉(zhuǎn)為0,縮放為1的新空對(duì)象。讓指示器成為該對(duì)象的子對(duì)象。
臨時(shí)父節(jié)點(diǎn)現(xiàn)在設(shè)置這個(gè)父節(jié)點(diǎn)Y軸旋轉(zhuǎn)為30°。指示器也會(huì)有效的圍繞父節(jié)點(diǎn)的原點(diǎn)旋轉(zhuǎn),從而精確的達(dá)到我們想要的位置。
正確定位的小時(shí)指示器使用Ctrl或Cmd D或通過(guò)hierarchy窗口中的上下文菜單復(fù)制臨時(shí)父節(jié)點(diǎn)。將復(fù)制父節(jié)點(diǎn)的Y軸旋轉(zhuǎn)值在增加30°。持續(xù)這樣的操作直到每個(gè)小時(shí)得到一個(gè)指示器。
十二小時(shí)指示器我們不再需要臨時(shí)父節(jié)點(diǎn)。在hierarchy窗口中選擇一個(gè)小時(shí)指示器并將其拖到時(shí)鐘對(duì)象上。它現(xiàn)在變?yōu)闀r(shí)鐘對(duì)象的子節(jié)點(diǎn)。當(dāng)這種情況發(fā)生時(shí),Unity改變了指示器的變換,所以它的位置和旋轉(zhuǎn)在世界空間中并沒(méi)有發(fā)生變化。為十二個(gè)指示器重復(fù)這個(gè)操作然后刪除所有的臨時(shí)父對(duì)象。你可以通過(guò)crl 或 cmd同時(shí)選擇多個(gè)對(duì)象來(lái)加快這一過(guò)程。
時(shí)鐘外圍子節(jié)點(diǎn)Ps:我看到的值是90.00001.這有問(wèn)題嗎?
發(fā)生這種情況是由于位置、旋轉(zhuǎn)和縮放組件是使用的浮點(diǎn)數(shù)。這些數(shù)字的精度有限,有可能與期望的數(shù)值有微小的偏差。不用擔(dān)心這0.00001的偏差,因?yàn)樗鼈兪遣豢筛兄摹?/p>
1.4 創(chuàng)建時(shí)針
我們可以用同樣的方法來(lái)創(chuàng)建時(shí)鐘的時(shí)針。創(chuàng)建另一個(gè)名為Arm的對(duì)象,并給它賦予指示器使用的相同材質(zhì)。設(shè)置它的縮放值為(0.3,0.2,2.5),這樣它就比指示器更窄、更長(zhǎng)。將它的位置設(shè)置為(0,0.2,0.75)這樣它就會(huì)位于表盤頂部并指向12點(diǎn),但是它也會(huì)指向相反的方向。這就使當(dāng)它旋轉(zhuǎn)時(shí)看起來(lái)像一個(gè)有小平衡重量的時(shí)針。
時(shí)針Ps:光照的圖標(biāo)去哪兒了?
我把光線移開(kāi),這樣就不會(huì)使得場(chǎng)景變得雜亂。因?yàn)樗嵌ㄏ蚬?#xff0c;所以它的位置并不重要。
要使Arm圍繞時(shí)鐘的中心旋轉(zhuǎn),我們?yōu)樗鼊?chuàng)建一個(gè)父對(duì)象。確保它的位置和旋轉(zhuǎn)值為0并且縮放值為1.因?yàn)樯院笪覀儠?huì)旋轉(zhuǎn)這個(gè)時(shí)針,讓這個(gè)父節(jié)點(diǎn)成為時(shí)鐘的子對(duì)象并命名為Hours Arm。Arm對(duì)象就成為了Clock對(duì)象的孫子節(jié)點(diǎn)。
擁有時(shí)針的時(shí)鐘層次復(fù)制時(shí)針對(duì)象來(lái)創(chuàng)建分針和秒針。分針對(duì)象應(yīng)當(dāng)比時(shí)針對(duì)象更窄、更長(zhǎng),所以設(shè)置他的縮放為(0.2,0.15,4),設(shè)置位置為(0,0.375,1)。這樣它就在時(shí)針的上面.
對(duì)于Seconds Arms節(jié)點(diǎn),設(shè)置縮放為(0.1,0.1,5),位置為(0,0.5,1.25)。為了使它看起來(lái)不一樣,我為它創(chuàng)建了一個(gè)Clock Red材質(zhì)并設(shè)置它的顏色值為(197,0,0)。
三根指針現(xiàn)在時(shí)鐘已經(jīng)被構(gòu)建好了。如果你還沒(méi)有完成,這是一個(gè)很好的保存場(chǎng)景的時(shí)機(jī)。它將作為資源存儲(chǔ)在項(xiàng)目中。
保存的場(chǎng)景2.讓時(shí)鐘動(dòng)起來(lái)
我們的時(shí)鐘現(xiàn)在還不能告訴我們時(shí)間。它現(xiàn)在僅僅是Unity渲染的一堆Mesh的對(duì)象層次結(jié)構(gòu)。僅此而已。如果有一個(gè)默認(rèn)時(shí)鐘組件,我們就可以用它來(lái)計(jì)時(shí)。由于沒(méi)有現(xiàn)成的,所以我們需要自己創(chuàng)建。組件通過(guò)腳本定義。通過(guò)Assets->Create->C# Script創(chuàng)建一個(gè)新的腳本資源并命名為Clock。
時(shí)鐘腳本資源當(dāng)選中腳本時(shí),檢查器會(huì)顯示它的內(nèi)容,以及一個(gè)在代碼編輯器中打開(kāi)腳本的按鈕。你也可以雙擊腳本資源來(lái)打開(kāi)它。腳本文件會(huì)包含一些默認(rèn)模板代碼,像以下顯示的一樣。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class Clock : MonoBehaviour {// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {} }這是C#代碼。這是在Unity中使用的腳本編程代碼。為了理解代碼是如何工作的,我們將全部刪除從頭開(kāi)始。
Ps:那么JavaScript呢?
Unity還支持另一種編程語(yǔ)言,通常被稱為JavaScript,但它的實(shí)際名稱是UnityScript。Unity2017.1.0仍然支持它,但是創(chuàng)建JavaScript的菜單將在Unity 2017.2.0中移除。預(yù)計(jì)在那以后將完全取消支持。
2.1 定義一個(gè)組建類型
一個(gè)空文件不是一個(gè)合法的腳本。它必須包含時(shí)鐘組件的定義。我們不定義組件的單個(gè)實(shí)例。相反,我們定義一般的類或類型稱為Clock。一旦定義完成,我們就可以在Unity中創(chuàng)建多個(gè)這樣的組件。
在C#中,我們聲明要定義一個(gè)類,后面是類名稱,以此來(lái)定義時(shí)鐘類型。在下面的代碼片段中,改變代碼的背景顏色為黃色。當(dāng)我們從一個(gè)空文件開(kāi)始,它的內(nèi)容應(yīng)該變成class Clock而不是其他的,盡管你可以在單詞之間隨意添加空格和換行。
class ClockPs:從技術(shù)上講,什么是類?
你可以將類看做一個(gè)藍(lán)圖,它可用于創(chuàng)建存儲(chǔ)在計(jì)算機(jī)內(nèi)存中的對(duì)象。藍(lán)圖定義了它包含哪些數(shù)據(jù)以及它所擁有的函數(shù)。
類還可以用于定義不屬于對(duì)象而是屬于類本身的數(shù)據(jù)和功能。這通常用于提供全局可用的功能。
因?yàn)槲覀儾幌胂拗颇男┐a可以訪問(wèn)時(shí)鐘,所以最好在前面加上public訪問(wèn)修飾符。
public class ClockPs:什么是類的默認(rèn)訪問(wèn)修飾符?
如果沒(méi)有訪問(wèn)修飾符,將會(huì)默認(rèn)我們編寫(xiě)的是internal class Clock。這將限制對(duì)來(lái)自同一程序集的訪問(wèn),當(dāng)使用打包在多個(gè)dll文件中的代碼時(shí),這一點(diǎn)就變得很重要。為了確保在任何時(shí)候都能正常工作,修改類默認(rèn)訪問(wèn)修飾符為public。
目前我們還沒(méi)有合法的C#語(yǔ)法。我們指出我們正在定義一個(gè)類型,所以我們必須實(shí)際定義它是什么樣的。這是通過(guò)聲明后面的代碼塊來(lái)完成。代碼塊的邊界用大括號(hào)表示,我們現(xiàn)在使用{}讓它空著。
public class Clock { }我們的代碼現(xiàn)在是有效的。保存文件并切回到Unity。Unity編輯器將檢測(cè)資源是否發(fā)生了變化,并觸發(fā)重新編譯。完成之后,選擇我們的腳本,檢查器將給我們指出資源沒(méi)有包含MonoBehavior腳本。
沒(méi)有組件的腳本這意味著我們不能再Unity中使用這個(gè)腳本創(chuàng)建組件。此時(shí),我們只是定義了一個(gè)通用的C#對(duì)象類型。Unity只能使用Monobehaviour的子類來(lái)創(chuàng)建組件。
Ps:mono-behavior是什么?
我們可以編寫(xiě)自己的組件來(lái)為游戲?qū)ο筇砑幼远x行為。這就是行為所指的意義。它只是碰巧使用了奇怪的英式拼寫(xiě)。Mono部分指的是將對(duì)自定義代碼的支持添加到Unity中的方式。它用到了mono項(xiàng)目,這是一個(gè).net框架實(shí)現(xiàn)的多平臺(tái)項(xiàng)目。因此,MonoBehaviour。這是一個(gè)向后兼容的老名字。
為了將Clock轉(zhuǎn)換為MonoBehaviour的子類型,我們必須更改類型聲明,以便它繼承該類型,這是通過(guò)冒號(hào)實(shí)現(xiàn)的。這使得Clock繼承了MonoBehaviour的所有功能。
public class Clock : MonoBehaviour { }然而,這將導(dǎo)致編譯后出現(xiàn)錯(cuò)誤。編譯器說(shuō)無(wú)法找到MonoBehaviour類型。這是因?yàn)樵擃愋桶赨nityEngine命名空間中。要訪問(wèn)它,我們必須使用它的完全限定名UnityEngine.MonoBehaviour。
public class Clock : UnityEngine.MonoBehaviour { }Ps:什么是命名空間?
命名空間就類似于網(wǎng)站域名,但是它是作用于代碼的。正如域可以有子域一樣,命名空間也可以有子命名空間。最大的不同是它是反過(guò)來(lái)寫(xiě)的。所以http://forum.Unity3d.com將被寫(xiě)成com.unity3d.forum。代碼是Unity自帶的,不需要上網(wǎng)單獨(dú)獲取。命名空間用于組織代碼并防止名稱沖突。
因?yàn)樵谠L問(wèn)Unity類型的時(shí)候總是使用UnityEngine前綴是不方便的,所以我們沒(méi)有顯示的提到任何命名空間時(shí),我們可以告訴編譯器搜索這個(gè)命名空間。這是通過(guò)添加using UnityEngine在文件頂部完成的。命令需要以分號(hào)結(jié)束。
using UnityEngine;public class Clock : MonoBehaviour { }現(xiàn)在我們可以在Unity中為我們的時(shí)鐘游戲?qū)ο筇砑游覀兊慕M件了。這是通過(guò)拖拽腳本資源到對(duì)象上或者通過(guò)對(duì)象檢查器下方的Add Component按鈕完成的。
擁有組件的時(shí)鐘對(duì)象現(xiàn)在已經(jīng)創(chuàng)建了一個(gè)C#對(duì)象實(shí)例,通過(guò)使用我們的Clock類作為模板。它已經(jīng)被添加到時(shí)鐘游戲?qū)ο蟮慕M件列表中。
2.2 獲取指針的引用
為了旋轉(zhuǎn)這些時(shí)鐘指針,Clock對(duì)象需要獲取它們。讓我們以時(shí)針為例。像所有的游戲?qū)ο笠粯?#xff0c;它可以通過(guò)改變變換組件的旋轉(zhuǎn)值,來(lái)達(dá)到旋轉(zhuǎn)目的。所以我們必須把時(shí)針的變換組件加入到Clock中。這可以通過(guò)添加一個(gè)數(shù)據(jù)字段在代碼塊中來(lái)實(shí)現(xiàn)。
Hours transform是一個(gè)合適的名稱。然而,名稱必須是單個(gè)單詞。通常是將名稱字段的第一個(gè)單詞小寫(xiě)并將所有其他單詞大寫(xiě)。然后將它們組合在一起。所以它變成了hoursTransform.
public class Clock : MonoBehaviour {hoursTransform; }Ps:using語(yǔ)句去哪里了?
它仍然在哪里,我僅僅是沒(méi)有展示它。代碼片段將包含足夠的現(xiàn)有代碼,以便您了解更改的上下文。
我們還必須定義字段的類型,在本例中是UnityEngine.Transform。它必須放在名稱字段的前面。
Transform hoursTransform;我們的類現(xiàn)在定義了一個(gè)字段,該字段可以保存對(duì)另一個(gè)對(duì)象的引用。該對(duì)象的類型必須是Transform。我們必須確保它持有的是時(shí)針轉(zhuǎn)換組件的引用。
字段在默認(rèn)情況下是私有的,這意味著它們只能由Clock類的代碼訪問(wèn)。但是類并不知道我們的Unity場(chǎng)景。通過(guò)修改字段為public,以便任何地方都能修改。
public Transform hoursTransform;Ps:公共字段不是很糟嗎?
一般來(lái)說(shuō),在編程時(shí)避免創(chuàng)建公共字段是一種共識(shí)。但是,在Unity編輯器中需要使用公共字段來(lái)連接對(duì)象。雖然你可以解決這個(gè)問(wèn)題,但這會(huì)使代碼變得不那么直觀。
一旦字段變?yōu)閜ublic,它將顯示在inspector窗口中。這是因?yàn)閕nspector自動(dòng)使組件的所有公共字段變?yōu)榭删庉嫛?/p>
時(shí)針域要建立正確的連接,請(qǐng)將Hours Arm從hierarchy中拖拽到Hours Transform字段。或者,使用該字段右側(cè)的圓形按鈕搜索Hours Arm.
建立連接的Hours Transform在拖動(dòng)或者選擇Hours Arm對(duì)象之后,Unity編輯器獲取到它的transform組件,并在我們的字段中放入對(duì)它的引用。
2.3獲取三個(gè)指針的引用
我們對(duì)分針和秒針做同樣的操作。所以給Clock添加另外兩個(gè)合適名字的字段。
public Transform hoursTransform; public Transform minutesTransform; public Transform secondsTransform;可以使這些字段聲明更簡(jiǎn)潔,因?yàn)樗鼈児蚕硐嗤脑L問(wèn)修飾符和類型。它們可以合并到一個(gè)逗號(hào)分隔的字段名稱列表中,該列表位于訪問(wèn)修飾符和類型之后。
public Transform hoursTransform, minutesTransform, secondsTransform; //public Transform minutesTransform; //public Transform secondsTransform;Ps:// 是做什么的?
//表示注釋。它們后面的所有文本直到行尾都被編譯器忽略。如果需要,它用于添加文本以解釋代碼。我還使用它來(lái)表示已刪除的代碼。被刪除的代碼也有一行代碼貫穿其中。
將編輯器中的其它兩個(gè)指針也連接起來(lái)。
連接完成的指針2.4 獲取時(shí)間
現(xiàn)在我們能夠在clock中獲取到指針,下一步就是指示出當(dāng)前的時(shí)間。 因此,我們需要告訴Clock執(zhí)行一些代碼。這是通過(guò)向類添加一個(gè)代碼塊(稱為方法)來(lái)實(shí)現(xiàn)的。塊必須有一個(gè)通常大寫(xiě)的名稱作為前綴。我們將它命名為Awake,這意味著應(yīng)該在組件喚醒的時(shí)候執(zhí)行代碼。
public class Clock : MonoBehaviour {public Transform hoursTransform, minutesTransform, secondsTransform;Awake {} }方法有點(diǎn)類似數(shù)學(xué)函數(shù)。例如f(x)=2x+3.這個(gè)函數(shù)接受一個(gè)數(shù)字,將其乘以2,然后再加上3。它操作單一的數(shù)字,所以它的結(jié)果也是單一的數(shù)字。對(duì)于一個(gè)方法,它更像是f(p)=c,p表示輸入?yún)?shù),c表示代碼執(zhí)行。由于這是相當(dāng)普遍的,那么這樣一個(gè)函數(shù)的返回結(jié)果是什么呢?這一點(diǎn)必須明確提到。在我們的示例中,我們只想執(zhí)行一些代碼,而不提供返回值。換句話說(shuō),該方法的返回結(jié)果是空的,我們用void前綴表示。
void Awake {}我們也不需要任何輸入數(shù)據(jù)。然而,我們?nèi)匀恍枰x方法的參數(shù),在圓括號(hào)之間用逗號(hào)分隔。只是在我們的例子中它是空的。
void Awake() {}現(xiàn)在我們有了一個(gè)合法的方法,盡管它還沒(méi)有做任何事情。像Unity檢測(cè)我們的字段一樣,它也檢測(cè)這個(gè)Awake方法。當(dāng)一個(gè)組件擁有一個(gè)Awake方法時(shí),Unity將在組件喚醒時(shí)調(diào)用該方法。這發(fā)生在創(chuàng)建或加載之后。
Ps:Awake不需要是公開(kāi)的嗎?
Awake和其它的一系列方法被Unity認(rèn)為是特殊的。它將找到它們并在適當(dāng)?shù)臅r(shí)候調(diào)用。無(wú)論我們?nèi)绾温暶魉鼈?#xff0c;我們不應(yīng)該將這些方法公開(kāi)。因?yàn)樗鼈儾淮蛩惚怀齍nity引擎以外的任何東西調(diào)用。
為了測(cè)試這是否有效,讓Awake創(chuàng)建一條調(diào)試信息。UnityEngine.Debug類包含了公開(kāi)可用日志方法用于此目的。我們將傳遞給它一個(gè)要打印的簡(jiǎn)單文本字符串。字符串寫(xiě)在雙引號(hào)之間。同樣,需要分號(hào)來(lái)標(biāo)記表達(dá)式的末尾。
void Awake() {Debug.Log("Test"); }在Unity編輯器中進(jìn)入運(yùn)行模式。將在編輯器底部的狀態(tài)欄中看到顯示的測(cè)試字符串。還可以在控制臺(tái)窗口中看到它。通過(guò)Window->Console打開(kāi)控制臺(tái)窗口。當(dāng)選中某條log文本時(shí),控制臺(tái)提供了一些附加信息,比如生成消息的代碼。
現(xiàn)在我們知道我們的方法是有效的。讓我們來(lái)指出調(diào)用它的當(dāng)前時(shí)間。UnityEngine命名空間包含一個(gè)Time類,這個(gè)類有一個(gè)Time屬性。我們把它打印出來(lái)。
void Awake() {Debug.Log(Time.time); }Ps:什么是屬性?
屬性是偽裝為字段的方法。它可能是只讀的,也可能是只寫(xiě)的。通常是大寫(xiě)屬性,但是Unity沒(méi)有這樣做。
Log顯示的結(jié)果總是為0。那是因?yàn)門ime.time是高告訴我們從我們進(jìn)入Play模式后所流失的時(shí)間。由于Awake方法立即被執(zhí)行,這時(shí)還沒(méi)有時(shí)間流失。所以這不能告訴我們真實(shí)的時(shí)間。
為了獲取我們所使用的計(jì)算機(jī)的系統(tǒng)時(shí)間,我們可以使用DateTime結(jié)構(gòu)體。這不是Unity的類型,我們可以在System命名空間中找到它。它是.net框架的核心功能的一部分,Unity使用它來(lái)支持腳本。
Ps:什么是結(jié)構(gòu)體?
結(jié)構(gòu)體是一個(gè)藍(lán)圖,跟類類似。不同之處在于它創(chuàng)建的任何對(duì)象都被視為一個(gè)簡(jiǎn)單的值,比如整數(shù)、顏色值,而不是對(duì)象。定義自己的結(jié)構(gòu)與定義類的工作原理是一樣的,只是使用struct而不是class。
DateTime有一個(gè)公開(kāi)可訪問(wèn)的Now屬性。它提供了一個(gè)包含當(dāng)前系統(tǒng)日期和時(shí)間DateTime值。讓我們用Log來(lái)打印它。
using System; using UnityEngine;public class Clock : MonoBehaviour {public Transform hoursTransform, minutesTransform, secondsTransform;void Awake(){Debug.Log(DateTime.Now);} }現(xiàn)在,每次進(jìn)入播放模式時(shí),我們都會(huì)得到一個(gè)時(shí)間戳。
2.5 旋轉(zhuǎn)指針
下一步是基于當(dāng)前時(shí)間旋轉(zhuǎn)時(shí)鐘指針。讓我們?cè)俅螐臅r(shí)針開(kāi)始。DateTime有一個(gè)Hour屬性,它為我們獲取到DateTime的Hours值。在當(dāng)前時(shí)間戳上調(diào)用它將得到當(dāng)前的小時(shí)值。
void Awake() {Debug.Log(DateTime.Now.Hour); }我們可以使用這個(gè)小時(shí)值來(lái)創(chuàng)建旋轉(zhuǎn)。旋轉(zhuǎn)以四元素的形式存儲(chǔ)在Unity中。我們可以使用公共可用的Quaternion.Euler方法來(lái)創(chuàng)建一個(gè)。它以X、Y、Z的標(biāo)準(zhǔn)正則角為參數(shù)生成一個(gè)合適的四元素。
void Awake() {// Debug.Log(DateTime.Now.Hour);Quaternion.Euler(0, DateTime.Now.Hour, 0); }Ps:什么是四元素?
四元素是基于復(fù)數(shù)用于表示3D旋轉(zhuǎn)的。雖然它比簡(jiǎn)單的三維向量更難理解,但它們有一些有用的特征。例如,它們不會(huì)受到萬(wàn)向鎖的影響。
UnityEngine.Quaternion被用作一個(gè)簡(jiǎn)單的值。它是一個(gè)結(jié)構(gòu)體而不是類。
三個(gè)參數(shù)都是實(shí)數(shù),在C#用浮點(diǎn)數(shù)表示。為了顯示的聲明我們提供的方法支持哪種類型的數(shù)字,我們?yōu)樗械?加上f后綴。
Quaternion.Euler(0f, DateTime.Now.Hour, 0f);我們的時(shí)鐘有十二個(gè)小時(shí),每個(gè)小時(shí)指示器間隔30°。為了與旋轉(zhuǎn)匹配,我們必須把小時(shí)乘以30.
Quaternion.Euler(0f, DateTime.Now.Hour * 30, 0f);為了明確的表示我們是將小時(shí)轉(zhuǎn)換為度數(shù),我們可以為轉(zhuǎn)換因子定義一個(gè)適當(dāng)?shù)淖侄巍S捎谒歉↑c(diǎn)數(shù)所以它的類型是float。由于我們已經(jīng)知道它的值,所以我們可以在字段定義的時(shí)候立即給它賦值。
private float degreesPerHour = 30f;public Transform hoursTransform, minutesTransform, secondsTransform;void Awake() {Debug.Log(DateTime.Now.Hour);Quaternion.Euler(0f, DateTime.Now.Hour * degreesPerHour, 0f); }每小時(shí)的度數(shù)是不會(huì)改變的。我們可以在聲明中添加const前綴來(lái)實(shí)現(xiàn)這一點(diǎn)。這就把每小時(shí)的度數(shù)變成了常數(shù)。
private const float degreesPerHour = 30f;Ps:常量有什么特殊的地方?
const關(guān)鍵字表示值永遠(yuǎn)不會(huì)改變,并且不需要成為一個(gè)字段。相反,它的值將在編譯期間計(jì)算,并替換所有使用到整個(gè)常量的地方。這只適用于像數(shù)字這樣的基本類型。
現(xiàn)在我們有一個(gè)旋轉(zhuǎn),但是沒(méi)有對(duì)它做任何操作,所以它只是被丟棄了。為了讓它作用到時(shí)針上,我們需要把它賦值給時(shí)針transform組件的localRotation屬性。
void Awake() {hoursTransform.localRotation =Quaternion.Euler(0f, DateTime.Now.Hour * degreesPerHour, 0f); } 時(shí)針指向4點(diǎn)Ps:什么是局部旋轉(zhuǎn)?
localRotation是指變換組件的實(shí)際旋轉(zhuǎn),獨(dú)立于其父節(jié)點(diǎn)的旋轉(zhuǎn)。換句話說(shuō),它是對(duì)象在局部坐標(biāo)系中的旋轉(zhuǎn)。它顯示在檢查器中的變換組件中。所以如果我們要旋轉(zhuǎn)時(shí)鐘根對(duì)象,它的子對(duì)象也會(huì)向我們期望的那樣跟著旋轉(zhuǎn)。
還有一個(gè)rotation屬性。它指的是轉(zhuǎn)換組件在世界坐標(biāo)系中的最終旋轉(zhuǎn),會(huì)計(jì)算上父節(jié)點(diǎn)的變換值。如果我們使用這種方法,當(dāng)我們旋轉(zhuǎn)時(shí)鐘時(shí),子節(jié)點(diǎn)就不會(huì)跟著調(diào)整,因?yàn)闀r(shí)鐘的旋轉(zhuǎn)會(huì)被補(bǔ)償。
當(dāng)進(jìn)入play模式時(shí),時(shí)針現(xiàn)在只想正確的方向。我們用同樣的方法來(lái)處理另外兩個(gè)指針。一分和一秒都占6度。
private const float degreesPerHour = 30f; private const float degreesPerMinute = 6f; private const float degreesPerSecond = 6f;public Transform hoursTransform, minutesTransform, secondsTransform;void Awake() {hoursTransform.localRotation =Quaternion.Euler(0f, DateTime.Now.Hour * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, DateTime.Now.Minute * degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, DateTime.Now.Second * degreesPerSecond, 0f); } 時(shí)鐘顯示16:29:06我們使用DateTime.Now三次來(lái)獲取時(shí)、分、秒。每次我們都要訪問(wèn)屬性,這需要花費(fèi)一些時(shí)間,理論上會(huì)得到不同的時(shí)間值。為了確保不會(huì)發(fā)生這樣的情況,我們應(yīng)該只訪問(wèn)一次時(shí)間。我們可以通過(guò)在方法中聲明一個(gè)變量并把當(dāng)前時(shí)間賦值給它,然后用這個(gè)時(shí)間值來(lái)實(shí)現(xiàn)這一點(diǎn)。
Ps:什么是變量?
變量的作用類似于字段,只是它只在方法執(zhí)行時(shí)存在。它屬于方法,而不屬于類。
void Awake() {DateTime time = DateTime.Now;hoursTransform.localRotation =Quaternion.Euler(0f, time.Hour * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, time.Minute * degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, time.Second * degreesPerSecond, 0f); }2.6 讓指針動(dòng)起來(lái)
當(dāng)我們進(jìn)入播放模式時(shí),我們獲取到當(dāng)前時(shí)間,但在那之后時(shí)鐘仍然保持靜止。要使時(shí)鐘與當(dāng)前時(shí)間保持同步,修改我們的Awake方法名稱為Update。只要我們處于播放模式,這個(gè)Unity會(huì)每幀調(diào)用一次這個(gè)方法,而不是只調(diào)用一次。
void Update() {DateTime time = DateTime.Now;hoursTransform.localRotation =Quaternion.Euler(0f, time.Hour * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, time.Minute * degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, time.Second * degreesPerSecond, 0f); }注意,我們的組件在檢查器中在它的名稱前多了一個(gè)check box。這允許我們禁用組件,從而防止Unity調(diào)用它的Update方法。
擁有check box的組件2.7 持續(xù)旋轉(zhuǎn)
我們時(shí)鐘的指針精確的指示當(dāng)前的小時(shí)、分、秒。它的行為像一個(gè)數(shù)字時(shí)鐘,離散但有指針。許多時(shí)鐘都有緩慢旋轉(zhuǎn)的指針來(lái)模擬時(shí)間的流逝。這兩種方法都可行。所以讓我們通過(guò)向組件中添加一個(gè)toggle來(lái)對(duì)其進(jìn)行配置。
我們?yōu)镃lock添加另一個(gè)名為continuous的公共字段。它可以是on或off,我們可以聲明為bool類型。
public Transform hoursTransform, minutesTransform, secondsTransform;public bool continuous;Bool值可以是true或false,對(duì)應(yīng)于本例中的on和off。默認(rèn)情況下是false,所以一旦字段出現(xiàn)在檢查器中,就打開(kāi)它。
使用continuous開(kāi)關(guān)我們現(xiàn)在支持兩種方式。為此,復(fù)制我們的Update方法,并將它們重命名為UpdateContinuous和UpdateDiscrete。
void UpdateContinuous() {DateTime time = DateTime.Now;hoursTransform.localRotation =Quaternion.Euler(0f, time.Hour * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, time.Minute * degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, time.Second * degreesPerSecond, 0f); }void UpdateDiscrect() {DateTime time = DateTime.Now;hoursTransform.localRotation =Quaternion.Euler(0f, time.Hour * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, time.Minute * degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, time.Second * degreesPerSecond, 0f); }創(chuàng)建一個(gè)新的Update方法。如果continuous為true,它應(yīng)當(dāng)調(diào)用UpdateContinuous。這個(gè)可以通過(guò)if語(yǔ)句來(lái)完成。If關(guān)鍵字后面跟著一個(gè)圓括號(hào)內(nèi)的表達(dá)式。如果表達(dá)式結(jié)果為true,則執(zhí)行它后面的代碼塊。否則,代碼塊將被跳過(guò)。
private void Update() {if (continuous){UpdateContinuous();} }Ps:新的Update方法必須在哪兒定義?
在Clock類內(nèi)部。它相對(duì)于另外兩個(gè)方法的位置無(wú)關(guān)緊要。可以在它們后面或前面。
還可以添加另一個(gè)代碼塊,在表達(dá)式為false時(shí)執(zhí)行。這是通過(guò)關(guān)鍵字else完成的。我們可以使用它來(lái)調(diào)用UpdateDiscrete方法。
private void Update() {if (continuous){UpdateContinuous();}else{UpdateDiscrect();} }現(xiàn)在我們可以在兩種方法之間切換,但是它們?nèi)匀蛔鐾瑯拥氖虑椤N覀儽仨氄{(diào)整UpdateContinuous,這樣它就會(huì)顯示小數(shù)的小時(shí)、分鐘和秒。不幸的是DateTime不包含小數(shù)數(shù)據(jù)。幸運(yùn)的是,它卻又一個(gè)TimeOfDay屬性。這將為我們提供一個(gè)TimeSpan值,其中包含我們所需要的格式的數(shù)據(jù)。特別是TotalHours,TotalMinutes,TotalSeconds。
private void UpdateContinuous() {TimeSpan time = DateTime.Now.TimeOfDay;hoursTransform.localRotation =Quaternion.Euler(0f, time.TotalHours * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, time.TotalMinutes* degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, time.TotalSeconds * degreesPerSecond, 0f); }這將導(dǎo)致編譯錯(cuò)誤,由于新值具有錯(cuò)誤的數(shù)據(jù)類型。它們被定義為雙精度浮點(diǎn)值,稱為double。這些值比浮點(diǎn)值擁有更高的精度, 但是Unity代碼只適用于單精度浮點(diǎn)值。
Ps:單精度值是否夠用?
對(duì)于大多數(shù)游戲來(lái)說(shuō)已經(jīng)夠用。當(dāng)處理非常大的距離或尺度差異時(shí),這將導(dǎo)致一些其他問(wèn)題。然后你將不得不使用類似傳送這樣的技巧來(lái)保持當(dāng)前游戲區(qū)域接近世界起點(diǎn)。雖然使用雙精度可以解決這個(gè)問(wèn)題,但是它也會(huì)使所涉及的數(shù)字大小加倍,從而導(dǎo)致一些其他的性能問(wèn)題。因此,大多數(shù)游戲引擎都使用浮點(diǎn)數(shù)。
我們可以通過(guò)轉(zhuǎn)換double為float解決這個(gè)問(wèn)題。這只是簡(jiǎn)單的丟棄了我們不需要的精確數(shù)據(jù)。這個(gè)過(guò)程被稱為強(qiáng)制轉(zhuǎn)換。通過(guò)在要轉(zhuǎn)換的值前面的圓括號(hào)內(nèi)寫(xiě)入新類型完成。
private void UpdateContinuous() {TimeSpan time = DateTime.Now.TimeOfDay;hoursTransform.localRotation =Quaternion.Euler(0f, (float)time.TotalHours * degreesPerHour, 0f);minutesTransform.localRotation =Quaternion.Euler(0f, (float)time.TotalMinutes* degreesPerMinute, 0f);secondsTransform.localRotation =Quaternion.Euler(0f, (float)time.TotalSeconds * degreesPerSecond, 0f); }現(xiàn)在您已經(jīng)了解了Unity中創(chuàng)建對(duì)象和編寫(xiě)腳本的基本原理。
總結(jié)
以上是生活随笔為你收集整理的unity双击打不开脚本_游戏对象和脚本 (创建一个时钟)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 开启分页机制———《x86汇编语言:从实
- 下一篇: 《x86汇编语言:从实模式到保护模式》读