【轉載】Quick 中的觸摸事件

原文地址 http://cn.cocos2d-x.org/article/index?type=quick_doc&url=/doc/cocos-docs-master/manual/framework/quick/V3/touch-events/zh.mdnode

 

Quick 中的觸摸事件

Cocos2d-x 本來的觸摸機制存在一些限制,在使用中須要開發者作很多額外的處理。因此 Quick-Cocos2d-x 提出了本身的一套觸摸機制。本文詳細介紹了這套新機制的用法。函數

顯示層級

在 Cocos2d-x 裏,整個遊戲的畫面是由一系列的 Scene, Node, Sprite, Layer 等對象構成的。而全部這些對象都是從 Node 這個類繼承而來。咱們能夠將 Node 稱爲顯示節點ui

一個遊戲畫面就是許多顯示節點構成的一棵樹:url

1
2
3
4
5
6
7
8
9
10
11
12
/|\
  | 顯示層級
  |
  |         [Node]  [Node]  [Node]
  |           |       |       |
  |           +---+---+       |
  |               |           |
  |             [Node]      [Node]
  |               |           |
  |               +-----+-----+
  |                     |
  |                   [Node]

在這棵樹裏,Node 所處的垂直位置就是它們的顯示層級。越往上的 Node,其顯示層級就越高。從畫面表現上來講,下面的 Node 是背景,上面的 Node 是建築,那麼建築就會擋住一部分背景。spa

觸摸區域

在 Cocos2d-x 裏,只有 Layer 對象才能接受觸摸事件。而 Layer 老是響應整個屏幕範圍內的觸摸,這就要求開發者在拿到觸摸事件後,再作進一步的處理。code

例若有一個需求是在玩家觸摸屏幕上的方塊時,人物角色作一個動做。那麼使用 Layer 接受到觸摸事件後,開發者須要自行判斷觸摸位置是否在方塊以內。當屏幕上有不少東西須要響應玩家交互時,程序結構就開始變得複雜了。對象

因此 Quick-Cocos2d-x 容許開發者將任何一個 Node 設置爲接受觸摸事件。而且觸摸事件一開始只會出如今這個 Node 的觸摸區域內。繼承

所謂觸摸區域,就是一個 Node 及其全部子 Node 顯示內容佔據的屏幕空間。要注意的是這個屏幕空間包含了圖片的透明部分。下圖中,節點 A 是一個 Sprite 對象,它的觸摸區域就是圖片大小;而節點 B 是一個 Node 對象,其中包含了三個 Sprite 對象,那麼節點 B 的觸摸區域就是三個 Sprite 對象觸摸區域的合集。遊戲

爲了簡化實現,觸摸區域都是一個矩形,因此節點 B 的觸摸區域其實是一個「包含三個 Sprite 對象觸摸區域合集的矩形」,能夠參考上圖中的紅色邊框線。事件

用法示例

下面列出觸摸事件的用法示例,更詳細的示例請參考 samples/touch 示例。

單點觸摸事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 容許 node 接受觸摸事件
node:setTouchEnabled( true )
 
-- 註冊觸摸事件
node:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event)
     -- event.name 是觸摸事件的狀態:began, moved, ended, cancelled
     -- event.x, event.y 是觸摸點當前位置
     -- event.prevX, event.prevY 是觸摸點以前的位置
     printf ( "sprite: %s x,y: %0.2f, %0.2f" ,
            event.name, event.x, event.y)
 
     -- 在 began 狀態時,若是要讓 Node 繼續接收該觸摸事件的狀態變化
     -- 則必須返回 true
     if event.name == "began" then
         return true
     end
end)

觸摸事件的 event.name 指示了事件的狀態:

  • began: 手指開始觸摸屏幕。在 began 狀態時,若是要繼續接收該觸摸事件的狀態變化,事件處理函數必須返回 true
  • moved: 手指在屏幕上移動。
  • ended: 手指離開屏幕。
  • cancelled: 由於其餘緣由取消觸摸操做。

多點觸摸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 容許 node 接受觸摸事件
node:setTouchEnabled( true )
 
-- 設置觸摸模式
node:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE) -- 多點
-- node:setTouchMode(cc.TOUCH_MODE_ONE_BY_ONE) -- 單點(默認模式)
 
-- 註冊觸摸事件
node:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event)
     -- event.name 是觸摸事件的狀態:began, moved, ended, cancelled
     -- 多點觸摸增長了 added 和 removed 狀態
     -- event.points 包含全部觸摸點
     -- 按照 events.point[id] = {x = ?, y = ?} 的結構組織
     for id, point in pairs(event.points) do
         printf ( "event [%s] %s = %0.2f, %0.2f" ,
                event.name, id, point.x, point.y)
     end
 
     if event.name == "began" then
         return true
     end
end)

在多點觸摸時,事件狀態的含義有所區別:

  • began: 手指開始觸摸屏幕。用戶可能同時用多個手指接觸屏幕,但由於硬件響應速度極快的緣由,began狀態時,event.points 中可能仍然只有一個觸摸點的數據,其餘觸摸點數據會經過 added 狀態提供。
  • added: 開始觸摸後,若是有更多觸摸點出現,則出現 added 狀態。此時 event.points 中包含新加入的觸摸點數據。
  • removed: 若是觸摸結束前有觸摸點消失(接觸屏幕的部分手指離開了屏幕),則出現 removed 狀態。此時event.points 中包含刪除的觸摸點數據。
  • ended: 若是全部觸摸點都消失(全部手指都離開了屏幕),則出現 ended 狀態。此時 event.points 中包含刪除的觸摸點數據。
  • moved: 因爲多點觸摸時,可能只有部分觸摸點移動。因此此時 event.points 中只包含有變化的觸摸點數據。

觸摸事件吞噬

默認狀況下,Node 在響應觸摸後(在 began 狀態返回 true 表示要響應觸摸),就會阻止事件繼續傳遞給 Node 的父對象(更下層的 Node),這稱爲觸摸事件吞噬

若是要改變這個行爲,能夠用:

  • setTouchSwallowEnabled() 是否容許 Node 吞噬觸摸,默認爲 true。若是設置爲 false,則 Node 響應觸摸事件後,仍然會將事件繼續傳遞給父對象。
  • isTouchSwallowEnabled() 檢查 Node 是否容許吞噬觸摸。

禁用觸摸

對於一個 Node,隨時能夠啓用或禁用其觸摸事件:

  • setTouchEnabled() 是否容許 Node 響應觸摸,默認爲 false
  • isTouchEnabled() 檢查 Node 是否容許觸摸。

但即使禁用了 Node 的觸摸事件,也只能阻止這個 Node 響應觸摸,而不能阻止這個 Node 的子 Node 響應觸摸。

假設有一個對話框(Node),咱們須要禁止對話框中的全部 Node 響應觸摸。那麼須要禁止對話框 Node 捕獲事件:

1
dialog:setTouchCaptureEnabled( false )
  • setTouchCaptureEnabled() 是否容許 Node 捕獲觸摸,默認爲 true。當設置爲 false 時,該 Node 及其全部子 Node 都沒法獲得觸摸事件。
  • isTouchCaptureEnabled() 檢查 Node 是否容許捕獲觸摸。

總結而言,setTouchEnabled() 只針對當前 Node,而 setTouchCaptureEnabled() 同時影響當前 Node 及其全部子 Node。

觸摸事件的三個階段

quick 中觸摸事件分爲三個階段:capturing(捕獲)、targeting(觸發)、bubbling(冒泡)。

當用戶的一根手指觸摸到屏幕時,將產生一個觸摸事件:

  1. 遍歷全部響應觸摸的 Node,找出顯示層級最高,而且其觸摸區域包含觸摸位置的那個 Node。這個 Node 被稱爲 TargetNode(目標 Node)。
  2. 檢查 TargetNode 的 isTouchCaptureEnabled() 結果,若是返回 false,則重複 1
    1. 從 TargetNode 的根 Node(一般是 Scene)開始,檢查 cc.NODE_TOUCH_CAPTURE_EVENT 事件的返回結果。任何一個 Node 返回 false 都會阻止事件在 TargetNode 上觸發。並從步驟 1 開始查找其餘符合條件的 Node。
    2. 這個階段被稱爲 capturing
  3. 在 TargetNode 上觸發事件。這個階段被稱爲 targeting
  4. 若是事件返回結果爲 false,表示 TargetNode 不響應該事件,並從步驟 1 開始查找其餘符合條件的 Node。
  5. 在 TargetNode 完成事件的響應後,檢查 TargetNode:isTouchSwallowEnabled() 的返回值。若是是true,則取消 bubbling 階段。
  6. 從 TargetNode 開始往其全部父 Node 觸發事件,直到某個 Node 返回 false 或者事件被吞噬。這個階段稱爲 bubbling

利用事件的三個階段,咱們能夠註冊 capturing 階段的觸摸事件處理函數:

1
2
3
4
5
6
7
-- 在 capturing 階段就捕獲事件
node:addNodeEventListener(cc.NODE_TOUCH_CAPTURE_EVENT, function(event)
     if event.name == "began" then
         -- 在 began 狀態返回 false ,將阻止事件
         return false
     end
end)

關於觸摸機制的靈活運用,能夠參考 cc.ui 中的各個 UI 控件,以及 samples/touch 示例。

API 參考

  • addNodeEventListener() 爲 Node 的特定事件設置處理函數,返回一個 id 表示註冊成功。
  • removeNodeEventListener() 從 Node 上移除指定類型的事件處理函數,須要提供addNodeEventListener() 返回的註冊 id。
  • setTouchEnabled() 是否容許 Node 響應觸摸,默認爲 false
  • isTouchEnabled() 檢查 Node 是否容許觸摸。
  • setTouchMode() 設置觸摸模式,默認爲 cc.TOUCH_MODE_ONE_BY_ONE
  • getTouchMode() 返回 Node 當前的觸摸模式。
  • setTouchCaptureEnabled() 是否容許 Node 捕獲觸摸,默認爲 true
  • isTouchCaptureEnabled() 檢查 Node 是否容許捕獲觸摸。
  • setTouchSwallowEnabled() 是否容許 Node 吞噬觸摸,默認爲 true
  • isTouchSwallowEnabled() 檢查 Node 是否容許吞噬觸摸。
相關文章
相關標籤/搜索