Unity基礎系列(二)——構建一個視圖(可視化數學)

點擊藍字關注我們吧!

目錄

1 創建一排立方體

1.1 預製體

1.2 視圖組件

1.3 實例化組件

1.4 代碼循環

1.5 簡化語法

1.6 改變域

1.7 把向量挪出循環

1.8 用X來定義Y

2 創建更多的立方體

2.1 定義分辨率

2.2 變量實例化

2.3 設置父節點

3 給視圖上色

3.1 創建自定義Shader

3.2 基於世界座標來上色

4 給視圖配置動畫

4.1 保持追蹤點

4.2 更新點

4.3 顯示正弦波

本篇內容概括:

1、創建一個prefab

2、實例化一排立方體

3、展示一個數學方法

4、創建一個自定義的Shader

5、讓視圖動起來

在本章教程中,我們將使用遊戲對象來構建一個圖形,這樣我們就可以把數學公式用圖像展示出來。然後再把函數和時間關聯起來,從而產生一個運動的圖像。

本教程假設您已經完成了「遊戲對象和腳本」相關教程,對Unity有了基礎的瞭解,並且Unity的版本至少要在2017.1。

(使用一排立方體來展示 正弦 波)

1 創建一排立方體

在編程的時候,充分地理解數學是非常非常有必要的事情。

從最基礎的層次理解,數學就是操作一堆表示數字的符號。比如,解一個方程可以理解爲重寫一組符號,這樣它就變成了另一組符號集(一般來說會比原始的簡單)。而數學的規則決定了如何對它們進行重寫。

比如我們有一個函數 f(x)=x+1 ,我們可以用一個數字來代替x,比如說3。這就會產生f(3)=3+1=4的結果。

我們提供了3作爲輸入,並在最後以4作爲輸出。我們可以說函數映射了 3到4,寫這個的一個更短的方法是把它們作爲一個輸入-輸出對,比如(3,4)。我們可以創建許多對形式(x,f(X))。例如(5,6)和(8,9)和(1,2)和(6,7)等等。但是,當我們按輸入編號排序對時,會更容易理解該函數,例如:(1,2)和(2,3)和(3,4)等。

函數f(X)=x+1f(X)=x+1很容易理解。但

就比較難了。當然可以和之前一樣寫下一些輸入-輸出對,但這樣的方式不能讓我們很好地掌握它所代表的映射究竟是什麼。如果要做到輕鬆的理解,就需要非常非常多的點,緊密排列在一起,但這無疑會導致巨量的數據,不但難以列舉還難以分析。

相反,我們可以將對解釋爲形式[x,f(X)]的二維座標。這是一個2D向量,上面的數字代表水平座標,在X軸上,底部的數字代表Y軸上的垂直座標。換句話說,y=f(X)。然後在一個座標軸的表面上畫出這些點。只要有足夠多的點,就會得到一條線。這個結果是一個視圖(graph)。

(用視圖表示 x在(-2,2)的區間)

查看一個視圖可以讓我們快速瞭解一個函數是如何工作的。既然這麼方便的話,那麼我們就看下如何在Unity裏創建吧。

通過File / New Scene啓動一個新場景,或者使用新項目的默認場景。

1.1 預製體

視圖是通過在適當的座標處放置點來創建的。

要做到這一點的話,就需要把每個點的變成三維可視化的。我們將簡單地使用Unity默認立方體的遊戲對象。

在場景中添加一個立方體,並移除其碰撞組件,因爲這個示例中不會使用到物理。

我們會使用腳本來創建這個立方體的諸多實例並正確定位它們。要做到這一點的話,就需要把這些立方圖做成模板。把立方體從層次結構窗口(hierarchy window)拖到項目窗口(project window)中。

這會創建一個新的Asset,一個具有藍色立方體圖標,我們稱爲預製體。它是一個預先製作好的遊戲對象,存在於項目中,但不在場景中。

(一個立方體的預製件)

預製體(Prefabs )是配置遊戲對象的一種方便的方法。如果你更改了預置體資源,那麼它在任何場景中的所有實例都會以相同的方式進行變更。例如,更改預製體的Scale會改變仍然在場景中的立方體的Scale。

但是要注意的是,每個實例只會使用自己的位置和旋轉。也就是說,預製體不會統一修改場景示例對象的位置和旋轉。此外,遊戲對象也可以修改相關屬性,並覆蓋預置值。而如果進行了很大的更改,比如添加或刪除了組件,則Prefab和實例之間的關係將被打破,需要重新保存或者應用修改。

這個章節,我們會使用腳本來創建預製體的實例,因此我們不再需要當前場景中的立方體實例。所以可以刪除它了。

1.2 視圖組件

我們需要一個C#腳本來生成我們的視圖,所以創建一個並將其命名爲Graph。從一個簡單的擴展MonoBehaviour的類開始,這樣它就可以成爲game objects對象的組件。給它設置一個公共字段來保存預置體文件的引用,以便一會用來創建視圖上的「點」,命名爲pointPrefab。由於我們需要訪問Transform組件來定位這些點,所以要使其成爲字段的類型。

將一個空的遊戲對象添加到場景中,通過GameObject/Create Empty,並將其放置在原點,命名爲Graph。通拖動或通過其Add Component按鈕將Graph組件添加到此對象。然後將預置體文件拖到Graph的PointPrefab字段上。現在它保存了對預置Transform組件的引用。

(視圖對象和引用的Prefab)

1.3 實例化組件

實例化遊戲對象是通過Instantiate方法完成的。

這是Unity Object類型的一個可公開使用的方法,它通過擴展MonoBehaviour間接繼承。Instantiate方法會克隆作爲參數提供的任何Unity類型的Object。在針對預製體的時候,它會實例一個對象並添加到當前場景中。我們就在Graph組件Awake時,去實例化。

(實例化的Prefab)

點擊Play之後,會將在原點生成一個立方體,不過,前提是預置體的原始位置設置的就爲零。要將這一點放到其他地方,就需要調整實例後的位置。

實例化方法爲我們提供了對它創建的任何內容的引用。因此,我們給它創建一個 Transform 組件的臨時變量來持有函數的返回值。

現在我們可以通過給它分配3D向量來調整點的位置。如我們在上一篇教程中中調整時鐘指針的Local rotation 一樣,但這次我們將通過修改它的localPosition屬性而不是position來調整點的位置。

三維向量是用[Vector3]的結構創建的。因爲它是一個struct,它的作用就像一個值,就像是一個數字一樣,而不是對象。例如,讓我們將點的X座標設爲1,將其Y和Z座標設爲零。Vector 3可以用right屬性來表示。

現在進入運行模式,我們得到的仍然一個立方體,但是位置已經發生了變化。下面繼續實例化第二個點,並將其放在第一個的增量迭代位置,比如將right向量乘以2來實現。重複實例化和定位代碼,然後將乘法添加到新代碼中。

可以把結構體和數字相乘嗎?

通常是不能的,但是可以定義這樣的功能。這需要通過創建一個具有特殊語法的方法來完成的,因此就可以像調用乘法一樣調用它。在這種情況下,看似簡單的乘法實際上是一種方法調用,類似於Vector3.Multiply(Vector3.right,2f)。它能夠像使用簡單的操作符一樣來調用方法,然後更快更容易地編寫代碼。

但它不是必要的,有的話就會更加便捷,就像能夠隱式使用名稱空間一樣。這種方便的語法被稱爲句法糖。但話雖如此,大部分時候,方法只有在嚴格符合操作員的意願的時候才能被操作人員使用。而對於向量而言,一些數學算式已經定義得很好了,所以沒必要擴展。

這兩行代碼會導致編譯錯誤,因爲我們嘗試兩次定義Point變量。如果我們想使用另一個變量,我們必須給它一個不同的名稱。或者,我們重用我們已經擁有的變量。但其實第一種方式並不好,第二種只需去掉變量的定義,將新的點賦值給同一個變量即可。

(兩個實例,X座標分別爲1和2)

1.4 代碼循環

如果要展示一排的話,就需要更多的點,現在創建10個看看。其實可以再重複相同的代碼八次,但這種是非常非常低效率的編程。理想情況下,我們只編寫一個點的代碼,並指示程序多次執行它,稍有變化即可。

while語句可用於代碼塊循環。將其應用於方法的前兩行,並刪除其他行。

就像if語句一樣,但後面必須有方括號中的表達式。與if一樣,只有當表達式計算爲true時,纔會執行後面的代碼塊。之後,程序將循環回while語句。如果此時表達式再次計算爲true,則代碼塊將再次執行。這會重複執行,直到表達式的計算結果爲false爲止。

所以我們必須在When之後添加一個表達式。一定一定要小心確保循環不會進入無限循環。無限循環會導致程序卡住,需要用戶手動終止。編譯的最安全的表達式就是false。

是否可以在循環裏面定義point?

可以。雖然代碼會重複,但我們只會定義了一次變量。循環的每一次迭代都會重用它,就像我們之前手動做的那樣。

當然其實還可以在循環之前定義point。這也允許你在循環之外使用變量。否則,其作用域僅限於while循環的塊。

限制循環可以通過追蹤重複代碼的次數來完成,使用一個整數變量來跟蹤即可。它用來記錄循環的迭代次數,我們將其命名爲i先。爲了能夠在while表達式中使用它,必須在循環之前定義它。

每次迭代,i增長1。

代碼寫到這,會產生一個編譯錯誤,因爲在給i賦值之前,正在嘗試使用i。所以必須先明確地將零賦值給i,直接在定義的時候賦值即可。

現在i在第一次迭代開始時變成1,在第二次迭代開始時變成2,依此類推。但是while表達式是在每次迭代之前計算的。所以在第一次迭代之前,i是0,在第二次迭代之前是1,依此類推。所以在第十次迭代之後,i是10。此時需要終止循環,因此它的表達式應該被計算爲false。

換句話說,只要i不到10,循環就應該繼續下去。從數學上講,用i<10來表示。代碼也是這樣寫。

運行之後,會得到10個立方體。但他們都在同一個位置。若要將它們沿X軸排成一行,需要用right向量乘以i。

注意,目前第一個立方體的X座標爲1,最後一個立方體爲10。理想情況下,我們從0開始,將第一個立方體定位在原點。我們可以把所有的點左移動一個單位,用right乘(i-1)代替i。然而,在塊的末尾增加i就可以跳過這次減法所帶來的一次額外計算。

1.5 簡化語法

循環是代碼編程裏最常見的格式之一,所以用盡可能短的語法會更加簡潔。C#提供一些語法糖來幫助我們。首先,讓我們考慮增加迭代次數。當執行x=x*y形式的操作時,可以將其縮短爲x*=y,這適用於對兩個相同類型的操作數進行操作的所有操作符。

更進一步,當將一個數字增加或減少1時,可以將其縮短爲++x或--x。

賦值語句的一個屬性是它們也可以用作表達式。這意味着您可以編寫類似於y=(x+=3)的東西。這將使x增加3,並將其結果分配給y。這表明我們可以在while表達式中增加i,從而縮短代碼塊。

然而,現在我們在比較之前就增加i,而不是事後,這將導致少一個迭代的執行。在這種情況下,增量和遞減運算符也可以放在變量之後,而不是在變量之前。該表達式的結果是更改前的原始值。

儘管while語句適用於所有類型的循環,但還有一種特別適合於遍歷範圍的替代語法。這是for循環。除了迭代器變量聲明和它的比較都包含在圓括號中,用分號隔開之外,它的工作方式類似於while。

這將產生編譯錯誤,因爲實際上有三個部分。第三種方法是遞增迭代器,使其與比較保持分離。

1.6 改變域

到現在爲止,我們的立方體被賦予了X座標從0到9。但這在處理函數時並不方便。通常,0-1的範圍用於X,或者當使用圍繞着0的函數時,範圍爲?1~1。現在重新定位立方體。

把我們的十個立方體沿一條線段,兩個單位長,會導致他們重疊。爲了防止這種情況,我們將減少他們的縮放。默認情況下,每個立方體在每個維中都有1的大小,因此爲了使它們適合,我們必須將它們的比例尺縮小到2/10=1/5。我們可以通過將每個點的local scale 設置爲Vector3.one屬性除以5來實現這一點。

(變小後的立方體)

要使立方體再次聚在一起,把它們的位置也除以5即可。

這使得他們涵蓋了0-2的範圍。若要將其轉換爲?1~1範圍,要在Scale矢量之前減去1。

現在,第一個立方體X座標爲?1,而最後一個立方體爲0.8個。然而,立方體大小爲0.2。由於立方體以其位置爲中心,第一個立方體的左側位於?1.1的位置,而最後一個立方體的右側爲0.9。爲了整齊地用我們的立方體填充?1-1範圍,我們必須將它們移到右邊的半個立方體。這可以通過在除以前將i加0.5來完成。

1.7 把向量挪出循環

雖然所有的立方體都有相同的縮放了,但我們在循環的每一次迭代中都計算了它,這並沒有必要。相反,我們可以在循環之前計算一次,將其存儲在Vector 3變量中,並在循環中使用。

我們也可以爲循環之前的位置定義一個變量。當我們沿着X軸創建一條線時,我們只需要調整環內位置的X座標而不再需要乘以向量Vector3.right。

是否可以單獨改變向量的分量?

Vector3 結構體有三個浮點字段,x、y和z。這些字段是公共的,因此我們可以修改它們。因爲結構的行爲是簡單的值類型,所以它們應當是不可變的。一旦定義好,就不應該改變。如果要使用不同的值,需要將新的結構賦給字段或變量,就像我們處理數字一樣。如果我們說x=3,然後x=5,我們給x分配了一個不同的數字。我們沒有將數字3本身修改爲5。但是,Unity的向量類型是可變的。這既是爲了方便,也是爲了性能,因爲單個向量組件通常是獨立操作的。要了解如何處理可變向量,可以考慮使用三個單獨的浮點值代替Vector3。這樣既可以獨立地訪問它們,也可以將它們作爲一個組進行復制和分配。

這會導致編譯錯誤,編譯器提示使用未賦值變量。這是因爲我們還沒有設置它的Y座標和Z座標就把位置分配給某個對象。因此,在循環之前顯式地將它們設置爲零。

1.8 用X來定義Y

我們的想法是,把立方體的位置定義爲(x,f(x),0),這樣我們就可以用這些點來展示一個函數了。此時,如果Y座標始終爲零,它表示簡單的函數f(X)=0。爲了顯示不同的函數,我們必須確定循環中的Y座標,而不是之前的做法直接等於X,之前的函數可以表示爲f(X)=x。

(Y等於X)

一個稍微不那麼明顯的函數是

,它定義了一個拋物線,最小值爲零。

(Y 等於X 的平方)

2 創建更多的立方體

雖然現在我們已經有了一個基於函數來排布的點,但是因爲我們只有10個立方體,排出來的圖形比較醜,間距也大。如果我們使用更多更小的立方體,效果就會好很多。

2.1 定義分辨率

解決固定立方體的數量,就是讓它變爲可配置。要實現這個目的,就給Graph增加一個字段用來定義立方體數量。

(自定義數量)

通過改變這個自定義值來調整視圖的分辨率,值可以通過修改inspector面板的值來完成。然而,並非所有整數都是有效或者有意義的。至少,它們都必須是正的。我們可以指示inspector 爲我們的值提供一個可選的範圍。通過在字段定義之前在方括號中寫入 Range 來實現。

Range 是由Unity定義的attribute類型。attribute是一種可以將元數據附加到代碼結構的方法,在本例中是字段。Unity的inspector會檢查字段是否附加了範圍屬性。如果附加了,它將使用一個滑塊而不是數字的默認輸入字段。

然而,要滑動,就需要提供允許的範圍。因此範圍有兩個參數,最小值和最大值。這裏我們用10和100。此外,屬性通常寫在字段上面,而不是前面。

(分辨率滑塊)

這是否意味着這個值只能以10-100爲限?

不是的。它所做的只是在Unity面板上使用滑塊可以得到的範圍。除此之外,它不會以任何其他方式影響分辨率。所以你可以自己寫代碼來修改它,讓它變爲任何其他的值。在本教程中,我們假設分辨率僅通過檢查器面板進行調整,而不是代碼或者其他地方。

2.2 變量實例化

要實際使用分辨率,我們必須更改實例化的立方體數量。不需要在Awake裏循環固定的次數,而是用我們設置的分辨率的值。因此,如果分辨率設置爲50,我們將在運行後後創建50個立方體。

分辨率變化了,必須要同時調整立方體的規模和位置,以便它們仍然保存在?1~1域中。每一次迭代所要做的步長的大小現在是2 /resolution,而不是總是1/5。把這個值存儲在一個變量中,然後用它來計算立方體及其X座標的比例尺。

(使用50的分辨率)

2.3 設置父節點

分辨率設置爲50之後,大量實例化的立方體出現在場景中,而場景的視圖視窗裏也顯示了這麼多。

(很多根節點對象)

這些立方體目前都是根對象,但它們其實可以作爲圖形對象的子對象。通過調用立方體的Transform組件的SetParent方法,就可以在實例化立方體之後建立這種節點關係。

這裏需要向它提供它的新的父級的 Transform 組件。通過Graph繼承的Transform屬性,可以直接訪問Graph對象的Transform組件。

(Graph的子節點)

當一個新的父對象被設置時,Unity將嘗試將對象保持在它原來的世界位置、旋轉和縮放。而我們現在的情況並不需要。可以直接通過向SetParent提供第二個參數false來決定。

3 給視圖上色

Unity提供的默認的白色不好看,當然可以換成另外一個純色,但這也沒什麼意思。其實我們可以通過點的位置來改變它的顏色。調整立方體顏色的一個簡單方法是設置其材質的顏色屬性,可以在循環裏設置即可。但由於每個立方體都會得到不同的顏色,這意味着最終會變成每一個物體有一個單獨的材質球。雖然這麼做能實現,但效率太低。如果我們有一種材質球能夠根據自己的位置設置不同的顏色就可以了。但其實Unity並沒有這樣的材質球,所以只能我們自己做了。

3.1 創建自定義Shader

GPU運行着色器程序來渲染3D對象。Unity的材質球資源決定使用哪個着色器,並允許配置相關的屬性。這裏需要創建自定義着色器來獲得我們想要的功能了。通過Asset/Create/Shader/StandardSurfaceShader來創建一個Shader,並將其命名爲ColoredPoint。

(自定義Shader文件)

我們現在有一個着色器資源,可以像打開腳本一樣打開它。我們的着色器文件包含了一些定義表面着色器的代碼,它使用的語法與C#語法不同。下面是文件的內容,爲了簡潔起見,刪除了所有註釋行。

表面着色器怎麼工作?

Unity提供了一個框架,可以快速生成用於執行默認照明計算的着色器,你可以通過調整某些值來影響這些計算,這樣的着色器被稱爲表面着色器。如果你想了解更多關於着色器的知識,你可以瀏覽 渲染 教程系列。

我們的新着色器具有自定義的顏色,紋理,以及表面的光澤和金屬的特性。因爲我們將基於一個點的位置,我們不需要自定義的顏色或紋理。下面的代碼已經刪除了所有不必要的位,使反照率變成黑色,並且使用的alpha值爲1。

什麼是Albedo和alpha?

物質的漫反射率的顏色稱爲albedo。albedo是拉丁文表示白色。它描述了多少紅色,綠色和藍色通道是擴散反射。其餘的都被吸收了。阿爾法是用來衡量不透明度的。在α =0的時候,表面是完全透明的,而在alpha 1是完全不透明的。

現在,着色器還無法編譯,因爲表面着色器不能使用空的輸入結構。這裏需要我們自定義數據格式,來支持着色怎麼繪製顏色。在這個例子裏,我們需要拿到一個點的座標。這可以通過在輸入中添加Float3 worldPos來訪問位置。

這是否意味着,如果我們移動Graph的位置會影響它們的顏色?

是的。使用這種方法的話,只有當視圖位於原點的時候,着色纔是正確的。

還要注意,這個位置是每個頂點決定的。在我們的例子中,這是單個立方體的每個角。顏色將被插入到立方體的表面上。立方體越大,顏色轉換就越明顯。

現在我們有了一個滿足功能着色器,爲它創建一個材質,名爲Colored Point。通過下來列表選中Custom / Colored Point 來更換爲我們自定義的Shader。

(Colored Point 材質球)

直接將材料拖到預置資源上來爲它更換材質球。

3.2 基於世界座標來上色

啓動遊戲,我們的圖形現在將實例化出黑色立方體。爲了給它們渲染顏色,必須修改o.albedo。將X座標賦值給紅色分量。

(基於X座標渲染顏色的視圖)

正X座標的立方體現在變得越來越紅。而負X座標保持爲黑色,因爲顏色不能是負的。要得到從?1到1的紅色轉換,我們必須將X座標減半,然後添加0.5。

(正確的轉換)

用Y座標來表示綠色成分,就像X一樣。在着色器中,我們可以在一行中使用IN.worldPos.xy並分配給o.Albedo.rg。

(使用X和Y座標上色)

紅色加綠色變成黃色,所以我們的圖表目前從淺綠色變成黃色。如果Y座標從?1開始,我們也會得到深綠色的顏色。要想看效果的話,請更改Graph.Awake中的代碼,以便它顯示函數。

(Y軸從-1~1)

4 給視圖配置動畫

顯示靜態的視圖已經完成了,但是運動視圖會更有意思的。因此,讓添加對動畫功能的支持吧。通過將時間作爲附加函數參數來實現,它使用形式爲f(x,t)的函數,而不只是f(X),其中t是時間。

4.1 保持追蹤點

要實現視圖動畫,需要隨着時間來調整點的位置。當然可以通過刪除所有點並創建每個更新的新點來實現,但這種方法的效率太低了。最好是能夠繼續使用相同的點,在每個Update調整他們的位置。簡單點的實現,可以使用一個字段來保持對點的引用。

在Graph裏增加Transform的數組來表示所有的點。

這個字段允許我們引用一個點,但是我們需要訪問所有的點。可以通過在字段類型後面放置空方括號將字段轉換爲數組。

Point字段現在可以引用數組,其元素是Transform類型。數組是對象,而不是簡單的值。我們必須顯式地創建這樣一個對象並使我們的字段引用它。這是通過New和數組類型來完成的,所以在我們的例子中,new Transform[]。在循環之前,在Awake時創建數組,並將其分配給點。

創建數組時,必須指定其大小。這定義了數組支持多少個元素,這些元素在創建之後不能更改。此長度在構造數組時寫入方括號中。在這個例子中,它的長度等於分辨率。

現在,可以在數組中填充對點的引用了。訪問數組元素的方法是將其索引寫入數組字段或變量後面的方括號中。對於第一個元素,數組索引從零開始,就像循環的迭代計數器一樣。因此,可以使用它來訪問適當的數組元素。

現在需要遍歷這所有的點。因爲數組的長度與分辨率相同,所以我們也可以使用它來約束我們的循環。每個數組都有一個Length屬性,可以使用它來進行循環限定。

4.2 更新點

要實際繪製圖形,我們需要在組件的Update方法中設置點的Y座標。因此,我們不能只在Awake時計算它們了。但我們仍然需要顯示的把某些值設置爲0。

添加一個Update方法,它有一個for循環,就像Awake方法一樣,但是它的循環體中還沒有任何代碼。

每次迭代,首先獲得對當前數組元素的引用。然後找到那個點的位置。

現在,我們可以設置位置的Y座標,就像我們之前所做的那樣。只是把它從Awake挪到Update裏來。

因爲向量不是對象,所以只能調整一個局部變量。爲了把它應用到點上,就必須重新設置它的座標。

我們不能直接分配Point.localPosition.y嗎?

如果localPosition是一個字段,那麼這是可以的。可以直接設置點的位置的Y座標。但是,localPosition是一個屬性。它把一個向量傳遞給我們,或者接受我們傳遞的。所以最終調整一個的是一個局部向量值,它根本不影響要修改的點位置。因爲我們沒有首先顯式地將它存儲在變量中,所以操作是沒有意義的,並且還會產生編譯器錯誤。

4.3 顯示正弦波

從現在起,在運行模式下,我們的視圖的點被每一個幀都會被重新定位。至於爲何看不出來,是因爲它們總是處於相同的位置。所以,必須將時間納入到功能中,以使其發生變化。然而,簡單地添加時間只會將讓這些點迅速上升,並消失在視線之外。爲了防止這種情況發生,我們必須使用一個函數,該函數可以更改,但需要保持在固定的範圍內。正弦函數是一個理想的表現,所以我們使用f(X)=sin(X)。我們可以用Unity的Mathf類的Sin函數來計算它。

(X的正弦表現)

正弦波在?1和1之間振盪,它重複每2個π(π)單位,約爲6.28。由於現在的視圖的X座標在?1和1之間,所以目前看到的重複模式還不到三分之一。爲了完整地看到它,用π標識X,所以最終可以得到f(X)=sin(πx)。可以使用Mathf.PI常數作爲π的近似值來計算。

(πx的正弦)

若要讓此函數動起來,可以在計算正弦函數之前將當前遊戲時間添加到X上。如果我們也通過π縮放時間,這個函數將每兩秒重複一次。所以使用f(x,t)=sin(π(x+t)),其中t是經過的遊戲時間。這將推動正弦波隨着時間的推移,向負X方向移動。

(動畫展示)

Unity基礎系列(一)——創建一個時鐘(GameObjects與Scripts)

下一章內容爲:數學表面。

授權聲明