1.負責InputModule的切換(由於如今遊戲大部分都只有一個StanaloneInputModule,因此切換這部分能夠先不考慮)。
2.負責InputModule的激活與反激活。
3.負責Tick整個事件系統。
4.更新InputModule,處理失焦和記錄鼠標位置。
5.記錄一個Selected對象。canvas
1.處理輸入的鼠標或觸摸事件,進行事件的分發。
2.激活和反激活時負責初始化(選擇對象,鼠標位置)和清理無效數據(選擇對象、pointerData)。
3.不直接使用Input獲取數據,而使用一個MonoBehaviour進行封裝,提供切換Input的能力(例如遊戲進入了反轉模式,點左下角時但願右上角有反應。那麼重寫一個對應的腳本,在進入這個模式時切換Input腳本就能夠)。緩存
1.找到全部被射線檢測成功的對象,選排序後第一個對象進行事件分發。函數
1.負責獲取和封裝外部的輸入信息,如點擊、重力感應等。
2.BaseInput提供和Input類同樣的能力,是對Input對象的封裝,接口名字都同樣,方便輸入系統的切換。測試
1.Touch類是一個Touch行爲(在屏幕上按下,擡起的過程算一個Touch行爲)某一時刻的數據。
2.Touch類包含的信息。
主要分3部分:
<1>每個Touch行爲從開始到結束,有一個惟一Id
<2>當前這個Touch行爲所處在的階段,一共5個階段3d
public enum TouchPhase { Began = 0,//按下 Moved = 1,//正在移動 Stationary = 2,//靜止,但沒有結束 Ended = 3,//離開 Canceled = 4//黑屏等其餘因素致使的結束 }
<3>當前位置,移動距離等信息。
3.Touch行爲在絕大多數狀況下都是由Began開始,Ended或Canceled結束。
可是咱們並無監聽Touch階段修改的能力(沒找到相關的接口),只能經過Input.GetTouch接口在某一時間點(如update中)來循環獲取Touch信息。而後經過FingerId,phase來還原一個完整的Touch行爲。但這樣會有一個問題,經過GetTouch獲取的Touch信息多是不完整的,如:
1.在一個Touch拖動的過程當中開始循環調用GetTouch,那麼咱們獲得的Touch就會不是由Began開始的,而是由Moved開始的。
2.在幀數很低,且在一幀內連續點擊屢次時,可能出現相同FingerId的Touch沒有經過Ended結束,而後又直接Began的狀況。code
因此在將GetTouch獲取的數據做爲EventSystem的輸入數據時,須要將這些特殊狀況考慮進去。orm
以Touch舉例
tips:
1.PointerEventData能夠理解爲對Touch行爲的進一步封裝,記錄了Touch行爲信息,如開始位置等,且在此基礎上增長了射線檢測結果等信息。每個PointerEventData的生命週期基本上和Touch行爲相同。由pressed開始(對應Touch的Began,如緩存中沒有對應fingerId的PointerEventData,則新建一個),released結束(對應Touch的Ended或Canceled,從緩存中移除該PointerEventData)。固然對於特殊狀況要特殊處理(如上面提到的沒有由Began開始的Touch等)。
2.Process主要的工做就是維護PointerEventData的數據,同時根據PointerEventData發出事件。
3.對事件腳本的查找是向上查找的,如C是B的子節點,B是A的子節點。射線檢測的結果是C。那麼會按C->B->A的順序去查找可響應該事件的對象。對象
這裏簡單說一下GraphicRaycaster做爲舉例。blog
1.GraphicRaycaster是檢測同gameobject下canvas中包含的全部Graphic元素是否被射線擊中的腳本。
2.Graphic在Onenable,OnDisable,OnBeforeTransformParentChanged,OnTransformParentChanged,OnCanvasHierarchyChanged這幾個時間點把本身加入或移除一個以canvas爲鍵值的graphic集合的字典中。
3.具體檢測過程:
<1>先從緩存中獲取該Canvas下全部的Graphic對象。
<2>處理多顯示器問題,先作一波座標轉換。
<3>根據BlockingObjects,對遊戲中的3D或2D對象作一次射線檢測,保存離相機最近的對象的距離,以後用於對結果的過濾。
<4>先經過RectangleContainsScreenPoint判斷射線擊中點是否在Graphic的RectTransform中,再經過Graphic自身的Raycast函數進行進一步的檢測(檢測CanvasGroup,Active狀態等)。
<5>最後再作一些測試,如反轉剔除,遮擋測試等。排序
1.遊戲中全部的Raycaster都進行一次射線檢測,獲取當前射線擊中的全部物體,統一進行排序,選排序後的第一個對象做爲射線檢測的結果。
2.排序規則
不一樣Racaster下:
camera.depth
Raycaster.sortOrderPriority 針對ScreenSpaceOverlay
Raycaster.renderOrderPriority 針對ScreenSpaceOverlay
相同Racaster下:
sortingLayer
sortingOrder
depth
distance
index
在手機上按這個方式操做。
其中A上的腳本繼承了IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler,IPointerUpHandler,IPointerClickHandler,IDragHandler接口。
B上的腳本繼承了IPointerEnterHandler,IPointerExitHandler, IDropHandler接口。
這一系列操做中事件的觸發主要依賴於對這幾個變量的設置和斷定。
①pointerPress:按下時射線擊中對象或向上查找的某一掛有繼承了IPointerDownHandler或IPointerClickHandler腳本的對象。
②pointerDrag:按下時射線擊中對象或向上查找的某一掛有繼承了IDragHandler腳本的對象。
③pointerEnter:當前Touch位置發出的射線擊中的第一個物體。
④pointerCurrentRaycast:當前位置發出射線的計算結果,包括當前擊中的物體等信息。
1.按下
一次完整的touch行爲的開始,新生成一個PointerEventData加入緩存中
<1>記錄pressPosition,用於開始拖動的斷定。
<2>由於當前按下的位置在A上,因此設置pointerEnter爲A。且A上的腳本繼承了IPointerEnterHandler接口,因此執行A上的PointerEnter函數。
<3>由於當前按下的位置在A上,且A上的腳本繼承了IProinterDownHandler、IPointerDragHandler接口,因此設置pointerPress和pointerDrag爲A。用於對後續擡起時的Click等事件作斷定。同時執行PointerDown函數。
2.拖動
在拖動距離超過Threshold以前什麼都不作。超事後開始不停的執行pointerDrag(A)對象上的OnDrag函數。
3.離開A
在離開A時,pointerEnter對象由A變爲了null,因此執行pointerEnter(A)對象上的PointerExit函數。
4.拖動
同2。
5.進入B
在進入B時,pointerEnter對象由null變爲了B,因此執行pointerEnter(B)上的PointerEnter函數。
6.擡起
touch行爲的結束,從緩存中移除這個PointerEventData
<1>執行pointerPress(A)對象上的PointerUp函數。
<2>因爲擡起時射線擊中的對象是B,而不是pointerPress(A)對象。因此不執行pointerPress(A)對象上的OnClick函數,而執行B上的OnDrop函數。
<3>執行B上的PointExit函數。
1.簡單來講EventSysetm的處理過程就是循環獲取Touch數據。根據Touch數據來推測完整的Touch行爲,來維護對應的PointerEventData,在此基礎上進行事件的計算和分發。 2.EventSystem的代碼量比較少但特殊處理的地方還挺多的,畢竟一個完善的系統,全部狀況都得考慮到位。因此閱讀代碼時能夠先看最核心的Process相關的代碼(Touch和Mouse先選一個),像InputModule切換、BaseInput的處理、Touch的特殊狀況處理這些能夠先略過,把握住核心思路以後再看這些部分。