Unity基礎教程-物體運動(十)——環境交互(Movement with Consequences)

目錄編程

1 加速區域數組

1.1 Zone 組件微信

1.2 阻止檢測地面編輯器

1.3 持續加速ide

1.4 任意方向函數

2 意識到存在flex

2.1 檢測區域優化

2.2 材質選擇動畫

2.3 最開始進入和最後退出ui

2.4 檢測忽然出現和消失的物體

2.5 熱重載

2.6 更復雜的行爲

3 簡單運動

3.1 自動滑動條

3.2 位置插值

3.3 自動倒置

3.4 平滑步長

3.5 更多控制

3.6 壓碎的碰撞體

3.7 局部插值

本文重點內容:

一、經過加速區域建立跳板和浮空

二、製做一個多功能區域

三、不一樣材質的交互以及關閉或者激活對象

四、經過事件觸發簡單對象插值運動


這是關於控制角色移動的教程系列的第十期。它讓環境能夠以各類方式和對象運動產生交互。

本教程是CatLikeCoding系列的一部分, 原文地址見文章底部。


本教程使用Unity 2019.4.4f1製做。它還使用ProBuilder軟件包。


(和環境交互)


1 加速區域


一個活躍的環境比一個靜態的環境更有意思,特別是它們還能對正在發生的行爲作出反應的時候。這個行爲表示能夠對任何事情作出反應,也能夠作任何事情,可是一個簡單的例子是相似於跳板的東西:每當有東西落在跳板上時,它就會向上彈起。這能夠是咱們運動的球體,也能夠是其餘掉落或被推到跳板上的物體。所以,該行爲在邏輯上屬於跳板。其餘物體不須要意識到它的存在,它們只是忽然被彈飛起來了。


1.1 Zone 組件


描述跳板行爲的最通用方法是,它是一個區域,可加速進入區域的任何物體。所以,咱們將建立AccelerationZone組件類型,其可配置的速度不能爲負。


區域能夠經過添加一個帶有觸發器碰撞器的對象到場景中來建立,而後將 zone behavior 附加到它上。你也能夠添加可視化的跳板對象,可是我只是用半透明的黃色材質使區域可見。


(Acceleration zone 組件)


當具備剛體的物體進入區域時,咱們應該對其進行加速。爲此添加一個OnTriggerEnter方法,該方法將觸發並調用新的Accelerate方法。進入該區域的全部物體都被執行,可是若是須要的話,可使用Layer來防止檢測到不須要的處理的物體。



(在區域中的物體被推開)


1.2 阻止檢測地面


這種簡單的方法在發射常規物體時效果很好,可是咱們的球體卻沒有正確發射。相反,它進入該區域時彷佛得到了很大的前進速度。發生這種狀況是由於咱們將其壓在了地面上。在這種狀況下,能夠經過下降「Max Snap Speed 」來解決,但這種方法不適用於設置爲低速的加速區域。一般,爲了防止被地面捕捉,咱們必須指示MovingSphere暫時不要執行捕捉。爲此,咱們能夠向其添加一個公共的PreventSnapToGround方法,該方法將stepsSinceLastJump設置爲-1。


如今,若是物體具備MovingSphere組件,則AccelerationZone.Accelerate能夠調用此方法,咱們能夠經過使用Sphere做爲輸出參數調用TryGetComponent來進行檢查和檢索。


(發射)


請注意,這種方法不會重置跳躍階段,所以在沒有着陸的狀況下彈跳跳板不會刷新空氣跳躍。


1.3 持續加速


瞬時速度變化對於跳板很合適,可是咱們也可使用該區域建立其餘連續的加速度現象,例如懸浮區域。咱們能夠經過簡單地添加一個與OnTriggerEnter相同的OnTriggerStay方法來支持這個特性。


若是效果持續時間較長,那麼經過適當的加速度來實現速度變化會更好一些,所以讓咱們向該區域添加一個可配置的加速度,最小仍是爲零。若是將其設置爲零,咱們將當即進行更改,不然將應用加速。



(升空區域 air加速度爲1)


也能夠施加力,這樣質量較大的物體最終加速得較慢,可是固定的加速度使關卡設計變得更容易,所以我使用這個方式。


1.4 任意方向


最後,爲了使其能夠在任何方向上加速,請在「Accelerate」開始時將體速度轉換爲區域的局部空間,並在應用時將其轉換回世界空間。使用InverseTransformDirection和TransformDirection進行此操做,以便區域的比例不會對其產生影響。如今能夠經過旋轉區域來控制加速度方向。


(跳躍區域之間的彈跳)


2 意識到存在


加速區域只是如何建立具備特定行爲的觸發區域的一個示例。若是你須要一個作其餘事情的區域,你將不得不爲它編寫新的代碼。可是,檢測和響應某個地方出現的某些東西的簡單行爲是如此廣泛,咱們理想狀況下只想編寫一次。有不少行爲很是簡單,好比只是激活一個對象,就爲它建立一個專用的組件類型可能就有些設計過渡了。更復雜的行爲一般只是幾個簡單動做的組合。若是關卡設計師能夠經過簡單的對象來建立它,那會是很是方便的。


2.1 檢測區域


讓咱們首先建立一個DetectionZone組件,該組件檢測其區域中是否存在某些東西,並在有物體進入或退出時通知感興趣的模塊。咱們經過從UnityEngine.Events命名空間爲它提供類型爲UnityEvent的onEnter和onExit字段進行配置來實現。


只需讓它在OnTriggerEnter和OnTriggerExit中的適當事件上調用Invoke方法。這將觸發對事件註冊的全部內容的方法調用。


檢查器會將組件的事件做爲名爲On Enter()和On Exit()的列表公開,這些列表最初是空的。名稱後面的括號中沒有任何內容,表示這些事件沒有參數。


(沒有事件)


2.2 材質選擇


爲了演示其工做原理,咱們將建立一個簡單的MaterialSelector組件類型,該組件類型具備可配置的材質數組和MeshRenderer參考。它具備一個帶有索引參數的公共Select方法,該方法將有效的材質分配給渲染器(若是有效的話)。


建立一個帶有紅色非活動區域和綠色活動區域的材質選擇器組件,這將用於更改檢測區域的可視化。雖然不須要將其添加到受影響的遊戲對象中,但這仍然是有意義的。


(材質選擇器)


如今,經過按項目的+按鈕將其添加到檢測區域組件的輸入事件列表中。經過材質選擇器的左下角字段將遊戲對象連接到該項目。以後,能夠選擇MaterialSelector.Select方法。因爲此方法具備整數參數,所以其值將顯示在方法名稱下方。默認狀況下,它設置爲零,表示不活動狀態,所以將其設置爲1。而後對退出事件執行相同的操做,此次將參數保留爲零。


(設置材質)


區域對象默認使用不活動的紅色材質。只要有物體進入區域,將切換材質到綠色。當有東西離開這個區域時,它又會變成紅色。

(和檢測區域的交互)


2.3 最開始進入和最後退出


該檢測區域能夠工做,並確實能夠完成其編程的目的,即每次進入時調用一次進入,每次離開時調用一次退出。所以,咱們能夠混合使用enter和exit事件(例如enter,enter,exit,enter,exit,exit),而且當其中仍然有東西時,最終會出現視覺上無效的區域。在區域中保持活動狀態時,使區域保持活動狀態更加直觀。使用保證進入和退出事件將嚴格交替的區域進行設計也更加容易。所以,它僅應在第一件東西進入時和最後一件東西離開時發出信號。重構事件重命名爲onFirstEnter和onLastExit可使這一點變得清晰,這將須要再次鏈接事件。


(重命名事件)


爲了使這種行爲成爲可能,咱們必須跟蹤區域中當前的碰撞體。經過爲DetectionZone提供一個List   字段(從System.Collections.Generic命名空間初始化爲新列表)來完成此操做。


該列表如何工做?

請參閱「對象管理」系列的「持久對象」教程。


在OnTriggerEnter中,只有在列表爲空時才調用enter事件,而後始終將碰撞器添加到列表中以跟蹤它。


在OnTriggerExit中,咱們從列表中移除碰撞器,而且只有在列表爲空時才調用退出事件 列表的Remove方法返回刪除是否成功 這應該老是這樣的,由於不然咱們就沒法追蹤碰撞器。


(只要有物體在區域就保持激活狀態)


2.4 檢測忽然出現和消失的物體


不幸的是,OnTriggerExit不可靠,由於在停用,禁用或銷燬遊戲對象或其碰撞器時便不會再調用它。不該該單獨禁用碰撞器,由於那樣會致使物體掉落到幾何體中,所以咱們將不支持這種方法。可是咱們應該可以處理整個遊戲對象在區域內時被禁用或銷燬的狀況。


在每個物理步長中,咱們都要檢查區域內的碰撞器是否仍然有效。添加一個在碰撞器列表中循環的FixedUpdate方法。若是一個碰撞器計算爲false,這意味着它或它的遊戲對象已經被銷燬。若是不是的話,咱們就須要檢查它的遊戲對象是否被禁用了,這一點咱們能夠經過它的遊戲對象的active屬性來發現。若是碰撞器再也不有效,則將其從列表中刪除並遞減循環迭代器。若是列表爲空,則調用退出事件。


大多數狀況下,檢測區域中沒有物體。爲了不沒必要要地連續調用FixedUpdate,咱們能夠在組件喚醒時和最後一個碰撞器退出後禁用該組件。而後咱們只有在有東西進入後才啓用它。之因此這樣有效,是由於不管是否啓用行爲,老是會觸發觸發器方法。


接下來,咱們還應該處理區域對象自身被停用或銷燬的狀況,由於當事件仍在區域中時發生時,調用退出事件是有意義的。咱們均可以經過添加一個OnDisable方法來完成這兩項工做,該方法清除列表並在列表不爲空時調用exit事件。


請注意,檢測區的組件不該由其餘代碼禁用,由於它能夠管理本身的狀態。通常規則是不要禁用檢測區域組件,也不要禁用任何可能影響該區域的碰撞器。這些遊戲對象應所有停用或銷燬。


2.5 熱重載


由於熱重載(在編輯器播放模式下從新編譯)將調用OnDisable,因此它違反了咱們剛剛聲明的規則。這將致使退出事件被調用以響應熱重載,此後已經在區域中的對象會被忽略。幸運的是,咱們能夠在OnDisable中檢測到熱重載。若是同時啓用了該組件而且遊戲對象處於活動狀態,則咱們將進行熱重載,而且什麼也不作。當遊戲對象沒有被銷燬而組件被銷燬時,狀況也是如此,可是咱們仍然什麼都不作。


咱們只須要在編輯器中播放時進行檢查,就能夠將代碼包裝在#if UNITY_EDITOR和#endif中。


OnDisable中有哪些相關狀態組合?

若是禁用了該組件,僅僅是禁用或反激活遊戲對象,則應該繼續進行。不然,若是遊戲對象未處於活動狀態,則該遊戲對象將被停用或銷燬,應該繼續。不然,要麼是熱重載,要麼是僅組件被銷燬,則將其忽略。


2.6 更復雜的行爲


這只是經過事件能夠完成的簡單演示。你能夠經過將更多條目添加到事件列表來建立更復雜的行爲。甚至沒必要爲此建立新方法,直接使用現有方法。而限制則是它必須是與事件的參數列表匹配的無效方法或屬性設置器,或者最多具備一個可序列化的參數。例如,我進行了一些設置,以便在更改檢測區域自己的可視化效果的同時,在檢測區域內有東西時關閉懸浮區域。


(切換懸浮區域)


您必老是對全部事件都響應。有時候可能只有在進入或退出時才觸發某些事件。例如,在進入區域時激活某些內容。而後退出並不會取消激活它,而從新進入則會再次激活它,雖然二級激活實際上沒有任何用處。


這種基於事件的方法能夠用於整個遊戲嗎?

從理論上講,是的,它對於快速原型製做很是有用,可是卻很麻煩。一旦發現本身重複了複雜的模式,便有必要爲其建立專用的方法或行爲,這種方法或方法應該更容易使用,並在之後必要時進行優化。


3 簡單運動


咱們將在本教程中介紹的最後一種狀況是移動環境對象。複雜的運動能夠經過動畫來完成,能夠經過檢測區域觸發。可是一般兩點之間的簡單線性插值就足夠了,例如,對於門,電梯或浮動平臺。如今,讓咱們添加對此的支持。


3.1 自動滑動條


不管插值什麼,它在概念上都由從0到1的滑塊控制。如何更改值是與插值自己不一樣的問題。保持滑塊分離還能夠將其用於多個插值。所以,咱們將建立一個專用於該值的AutomaticSlider組件。它的可配置持續時間必須爲正。當咱們使用它爲物理對象設置動畫時,咱們將使其在FixedUpdate方法中增長其值,並確保它不會溢出。一旦值達到1,咱們就能夠完成並能夠禁用滑塊。


再一次,咱們將使用Unity事件使它可以附加行爲到滑動條。在本例中,咱們須要一個隨值變化的事件,咱們將使用它來傳遞滑塊的當前值。因此咱們的事件須要一個浮點參數,可使用UnityEvent 類型。在FixedUpdate結束時調用事件。


可是,Unity沒法序列化通用事件類型,所以該事件不會顯示在檢查器中。咱們必須建立本身的具體可序列化事件類型,該事件類型只是擴展UnityEvent 。此類型特定於咱們的滑塊,所以能夠經過在類內部以及事件字段自己進行聲明來使其成爲嵌套類型。


進入播放模式時,滑塊將當即開始增長。若是你不但願這樣作,請在默認狀況下將其禁用。而後,你能夠將其鏈接到檢??測區域,以在之後啓用它。


(禁用具備值更改事件的滑塊)


請注意,在這種狀況下,事件的名稱後跟(Single),表示它具備一個參數。單精度是指浮點類型,它是單精度浮點數。


3.2 位置插值


接下來,建立一個PositionInterpolator組件類型,該類型經過帶有float參數的公共Interpolate方法在兩個可配置位置之間插值可配置剛體的位置。使用Vector3.LerpUnclamped,以使提供的值不會被鉗位,而是由調用者決定。咱們須要經過其MovePosition方法更改身體的位置,以便將其解釋爲運動,不然將成爲閃現。



(位置插值和滑塊相鏈接)


經過將sider和interpolator都添加到同一平臺對象,我建立了一個簡單的移動平臺。插值器的Interpolate方法的動態版本綁定到滑塊的事件,這就是爲何其值沒有字段的緣由。而後,我將滑塊鏈接到檢測區域,以便在有物體進入該區域時激活平臺。請注意,插值點在世界空間中。


(激活移動的平臺)


3.3 自動倒置


咱們能夠經過向AutomaticSlider添加可配置的自動反向切換來使插值來回移動。這須要咱們跟蹤它是否反轉,並在FixedUpdate中加倍代碼,同時必須支持雙向。一樣,當自動反轉激活時,咱們必須跳動而不是鉗制該值。在持續時間極短的狀況下,這可能會致使溢出,所以反彈後咱們仍然會鉗住。



(自動升降的平臺)


3.4 平滑步長


線性插值的運動是剛性的,反轉時速度會忽然變化。經過將值的平滑變體傳遞給事件,可使其加速和減速。經過對其應用smoothstep函數來實現。並使它成爲可配置的選項。



(線性VS平滑)



(開啓了平滑步長的平臺)


3.5 更多控制


能夠經過檢測區域事件,並禁用滑塊組件來暫停動畫,但讓咱們也能夠控制其方向。最簡單的方法是經過公共屬性提供其反轉狀態。將反向字段替換爲自動反向屬性,調整其餘代碼的大小寫以使其匹配。


讓咱們對自動反轉選項執行相同的操做。在這種狀況下,咱們必須保留序列化字段,所以添加一個顯式屬性。



(更復雜的平臺控制)


請注意,方向反轉是忽然的,由於它仍然是簡單的插值。若是要在任什麼時候候平穩中止和反轉,則須要建立使用加速度和速度的更復雜的邏輯。


3.6 壓碎的碰撞體


移動場景的危險在於,物體最終可能會陷入兩個接近的碰撞器之間。當碰撞器之間的縫隙關閉時,身體要麼被彈出,要麼最終被壓入碰撞器或穿過碰撞器。若是碰撞表面成必定角度,則存在清晰的逃生路徑,物體將朝該方向被推進。若是不是這樣,或者若是沒有足夠的時間逃脫,則物體最終會被壓碎,穿透碰撞體。若是一個物體卡在兩個足夠厚的簡單碰撞器之間,那麼它能夠留在它們內部,一旦有一條清晰的道路就彈出。不然會掉下去。


(物體被壓入地表內了)


若是碰撞表面成必定角度,則物體會被推到一邊,而且頗有可能逃脫。所以,經過在表面之間留出足夠的空間或經過引入傾斜的碰撞器(不管是否可見)來設計這樣的配置是一個好主意。此外,將box碰撞器隱藏在地板上可使它更牢固,以避免物體被推入。或者,添加一個區域,在適當的時候觸發該區域的銷燬,表示它被壓碎了。


(帶有角度的碰撞器,而且地表下面隱藏了盒碰撞器)


3.7 局部插值


世界空間中的配置可能會帶來不便,由於它沒法在多個位置用於同一動畫。所以,讓咱們經過在PositionInterpolator中添加一個局部空間選項進行總結。爲此,咱們添加了一個可選的可配置的Transform,該插值相對於應該發生的插值。一般用插值器引用對象,但這不是必需的。



(相對插值讓複用成爲可能)


下一章節,滾動。


歡迎掃描二維碼,查看更多精彩內容。點擊 閱讀原文 能夠跳轉原教程。










本文翻譯自 Jasper Flick的系列教程

原文地址:

https://catlikecoding.com/unity/tutorials








本文分享自微信公衆號 - 壹種念頭(OneDay1Idea)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索