【Android遊戲開發十七】讓玩家自訂手勢玩轉Android遊戲!—Android Gesture之【IME手勢技術】

來源:互聯網
上載者:User

 李華明Himi 原創,轉載務必在明顯處註明:
轉載自【黑米GameDev街區】 原文連結: http://www.himigame.com/android-game/340.html

 

很多童鞋說My Code運行後,點擊home或者back後會程式異常,如果你也這樣遇到過,那麼你肯定沒有仔細讀完Himi的博文,第十九篇Himi專門寫了關於這些錯誤的原因和解決方案,這裡我在部落格都補充說明下,省的童鞋們總疑惑這一塊;請點擊下面聯絡進入閱讀:

【Android遊戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後台等異常處理!

                                     有童鞋問我為什麼不用SDK2.1 ,2.2來進行遊戲開發,那我這裡稍微說兩句:

1.Android SDK 屬於向下相容!那麼低版本可以啟動並執行,高版本基本上更是沒問題!(當然每次SDK的更新也會帶來新功能,或者修改了一些原來的BUG等等,那麼其實對於遊戲開發來說,如果你的遊戲中不需要更高的SDK版本的支援情況下,完全不必去追求最新的SDK!)

2.使用低版本進行遊戲開發這樣能兼顧更多的機型,擷取更多的使用者!

3.大家都知道Android SDK 每次版本的更新,底層代碼也會更健壯和最佳化了!比如我們公司的網遊Android版在G2(SDK1.5)上跑起來稍微有些卡,而在My Phone上(SDK2.2)運行起來流暢的沒說的~各種舒坦~~但是這樣也會帶來一些弊端,比如我們自己遊戲如果上來就用高版本SDK進行開發,那麼對於效能、記憶體上到底如何,我們都不會很容易的看出其效果,如果我們用低版本的SDK則會讓我們明顯的感受到效能到底如何~你想想如果你的遊戲在1.5 ,1.6上跑起來很流暢,那放在更高版本的SDK機器上更是沒說的啦~

                    總結:遊戲開發中,如果你遊戲不需要更高的API的支援,那麼推薦基於SDK 1.5和1.6來開發!

 

         在上一篇中我給大家介紹了觸控螢幕手勢操作,但是這種觸屏手勢的操作比較有局限性;比如我們都知道Android可以利用手勢來解鎖,比如九宮格形式的,通過自訂的一個單筆畫手勢可以解開螢幕鎖,還可以自訂手寫筆畫手勢來啟動一個應用等,那麼這種所謂的筆畫手勢其實就是今天我要給大家講解的IME手勢識別技術!這種手勢是我們可以自己來自訂,而不像之前的觸屏手勢操作只是利用Android 對一些觸屏動作的封裝罷了。下面上幾張手機自訂手寫筆劃手勢解鎖的的:

 

          

       左圖中最後一個是自訂解鎖的IME手勢~

 

OK,那麼既然利用手勢既然能進行解鎖等操作,那麼我們遊戲開發中,更是可以加入這一亮點了,比如在遊戲中我畫個圓形執行換背景操作,畫個X表示離開遊戲等等,等等、哈哈 是不是感覺很有意思了?好的,下面就開始進入講解!

 

首先本篇主要學習兩點:

     1. 如何建立IME手勢、刪除IME手勢、從SD卡中讀取出手勢檔案!

     2.當IME手勢建立後,如何來匹配出我們的自訂手勢!

 

下面我們來熟習兩個類和幾個概念:

 

1. 什麼是 GestureOverlayView ?  簡單點說其實就是一個手寫繪圖區;

 

2. 什麼是 GestureLibrary ?   這個類是對手勢進行儲存、刪除等操作的,一個存放手勢的小倉庫!

 

3. 筆劃是什麼,字型筆畫?  是的,其實就是跟我們寫字的筆劃一個概念!

 

4.什麼是筆類型?   IME手勢操作中,筆劃類型有兩種;一種是:單一筆劃,另外一種是:多筆劃

    所謂單一筆劃筆劃就是一筆劃畫出一個手勢,從你手指接觸螢幕開始到你離開螢幕筆畫就會立刻形成一個手勢!一氣呵成!

    而多筆劃則是可以在一定緊湊時間內隨意幾筆劃都可!然後超過這個緊湊時間後便會形成一個手勢!

 

先出項目,簡單說下其功能和操作:

                                   【圖1】                                                                                                                        【圖2】

                        

 

圖1介面中分為3塊,從上到下依次是:TextView ,EditText,SurfaceView;然後在SurfaceView後面還有一個覆蓋全屏的GestureOverlayView!

圖2介面是在建立好的手勢中匹配手勢的介面,這裡很清晰看出來,找的很對 ~嘿嘿~

 

先看下main.xml:

 

<?xml version="1.0" encoding="utf-8"?><br /><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"<br />android:orientation="vertical" android:layout_width="fill_parent"<br />android:layout_height="fill_parent"><br /><TextView android:id="@+id/himi_tv" android:layout_width="fill_parent"<br />android:layout_height="wrap_content" android:text="@string/hello"<br />android:textSize="15sp" android:textColor="#FFFFFF00" /><br /><EditText android:id="@+id/himi_edit" android:layout_width="fill_parent"<br />android:layout_height="wrap_content" /><br /><RelativeLayout android:layout_width="fill_parent"<br />android:layout_height="wrap_content" android:layout_weight="1"><br /><com.himi.MySurfaceView android:id="@+id/view3d"<br />android:layout_width="fill_parent" android:layout_height="fill_parent" /><br /><android.gesture.GestureOverlayView<br />android:id="@+id/himi_gesture" android:layout_width="fill_parent"<br />android:layout_height="fill_parent" android:layout_weight="1.0"/><br /></RelativeLayout><br /></LinearLayout><br />

 

xml中註冊的有我們自訂的surfaceview,對此不太熟悉可以去看下【Android2D開發之六】,不多解釋了。關於GestureOverlayView這裡也只是簡單的定義了寬高,還有一些重要的屬性設定在代碼中設定了,當然xml也可以設定的;

 

下面看MainActivity.java

 

**<br /> *@author Himi<br /> *@IME手勢識別<br /> *@注意: android.gesture這個類在api-4(SDK1.6)才開始支援的!<br /> *@提醒:預設存到SD卡中,所以別忘記在AndroidMainfest.xml加上SD卡讀寫權限!<br /> */<br />public class MainActivity extends Activity {<br />private GestureOverlayView gov;// 建立一個手寫繪圖區<br />private Gesture gesture;// 手寫執行個體<br />private GestureLibrary gestureLib;//建立一個手勢倉庫<br />private TextView tv;<br />private EditText et;<br />private String path;//手勢檔案路徑<br />private File file;//<br />@Override<br />public void onCreate(Bundle savedInstanceState) {<br />super.onCreate(savedInstanceState);<br />this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,<br />WindowManager.LayoutParams.FLAG_FULLSCREEN);<br />this.requestWindowFeature(Window.FEATURE_NO_TITLE);<br />setContentView(R.layout.main);<br />tv = (TextView) findViewById(R.id.himi_tv);<br />et = (EditText) findViewById(R.id.himi_edit);<br />gov = (GestureOverlayView) findViewById(R.id.himi_gesture);<br />gov.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);//設定筆劃類型<br />// GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE 設定支援多筆劃<br />// GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE 僅支援單一筆劃<br />path = new File(Environment.getExternalStorageDirectory(), "gestures").getAbsolutePath();<br />//得到預設路徑和檔案名稱/sdcard/gestures<br />file = new File(path);//執行個體gestures的檔案對象<br />gestureLib = GestureLibraries.fromFile(path);//執行個體手勢倉庫<br />gov.addOnGestureListener(new OnGestureListener() { // 這裡是綁定手寫繪圖區<br />@Override<br />// 以下方法是你剛開始畫手勢的時候觸發<br />public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {<br />tv.setText("請您在緊湊的時間內用兩筆劃來完成一個手勢!西西~");<br />}<br />@Override<br />// 以下方法是當手勢完整形成的時候觸發<br />public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {<br />gesture = overlay.getGesture();// 從繪圖區取出形成的手勢<br />if (gesture.getStrokesCount() == 2) {//我判定當使用者用了兩筆劃<br />//(強調:如果一開始設定手勢筆畫類型是單一筆畫,那你這裡始終得到的只是1!)<br />if (event.getAction() == MotionEvent.ACTION_UP) {//判定第兩筆劃離開螢幕<br />//if(gesture.getLength()==100){}//這裡是判定長度達到100像素<br />if (et.getText().toString().equals("")) {<br />tv.setText("由於您沒有輸入手勢名稱,so~儲存失敗啦~");<br />} else {<br />tv.setText("正在儲存手勢...");<br />addGesture(et.getText().toString(), gesture);//我自己寫的添加手勢函數<br />}<br />}<br />} else {<br />tv.setText("請您在緊湊的時間內用兩筆劃來完成一個手勢!西西~");<br />}<br />}<br />@Override<br />public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {<br />}<br />@Override<br />public void onGesture(GestureOverlayView overlay, MotionEvent event) {<br />}<br />});<br />//----這裡是在程式啟動的時候進行遍曆所有手勢!------<br />if (!gestureLib.load()) {<br />tv.setText("Himi提示:手勢超過9個我做了刪除所有手勢的操作,為了介面整潔一些!"<br />+ " IME手勢練習~(*^__^*)~ 嘻嘻!/n操作介紹:(畫手勢我設定必須畫兩筆劃才行哦~)/n1." +<br />"添加手勢:先EditText中輸入名稱,然後在螢幕上畫出手勢!/n2.匹配手勢:"<br />+ "在EditText輸入/"himi/",然後輸入手勢即可! ");<br />} else {<br />Set<String> set = gestureLib.getGestureEntries();//取出所有手勢<br />Object ob[] = set.toArray();<br />loadAllGesture(set, ob);<br />}<br />}<br />} 

 

這個就是MainActivity主要代碼了,其中添加手勢、匹配手勢、遍曆手勢、將手勢轉成圖片這些我都單獨寫成了函數,這樣讓各位童鞋更清晰思路一些。

 

從以上代碼中我們看出在建立手勢之前,手寫繪圖區(GestureOverlayView)肯定先被建立出來,然後我們就可以在其地區中進行筆劃繪畫手勢了,當然繪畫手勢前,我們也需要設定了筆劃類型,也就是我一開始給大家介紹的~其後最重要的就是手寫繪圖區的手勢監聽器綁定,增加OnGestureListener這個監聽器重寫了四個函數,這裡最重要的就兩個函數:

      onGestureStarted   和  onGestureEnded  ; 手勢開始和手勢結束的監聽函數!

尤其是手勢結束監聽這個函數尤為重要,在其中我設定好幾個條件陳述式,這麼幾個條件一方面是讓大家瞭解Gesture中一些比較重要常用的方法,另一方面我要提醒各位童鞋:

如果你設定筆劃類型是多筆劃類型的,那麼理想狀態下,應該是在一段緊湊時間內,不管你使用了幾筆劃來繪製手勢,系統都應該在判定你在一定短暫時間內沒有再進行筆劃的時候才應該建立手勢,並且系統響應此函數;

      其實錯了,一開始我也這麼想,但是發現,不管你設定的筆劃類型是單一的還是多筆劃當你手指離開螢幕,不管你當前是第幾筆,Android都會去響應這個完成函數,so~ 我在這裡調用手勢Gesture類中的getStrokesCount()函數,這個函數會記錄在緊湊時間內你繪製手勢的筆劃數,那麼根據這個函數我們就可以解決手指離開螢幕總被響應的問題了,因為單一筆劃類型永遠這個值不會大於1!

而 if (event.getAction() == MotionEvent.ACTION_UP) {}寫這個只是給大家示範第二個參數按鍵動作該怎麼用;

 

那麼我們下面就來看如何建立一個手勢:

 

public void addMyGesture(String name, Gesture gesture) {<br />try {<br />if (name.equals("himi")) {<br />findGesture(gesture);<br />} else {<br />// 關於兩種方式建立模擬器的SDcard在【Android2D遊戲開發之十】有詳解<br />if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!<br />if (!file.exists()) {// 判定是否已經存在手勢檔案<br />// 不存在檔案的時候我們去直接把我們的手勢檔案存入<br />gestureLib.addGesture(name, gesture);<br />if (gestureLib.save()) {////儲存到檔案中<br />gov.clear(true);//清除筆畫<br />// 注意儲存的路徑預設是/sdcard/gesture ,so~別忘記AndroidMainfest.xml加上讀寫權限!<br />// 這裡抱怨一下,咳咳、其實昨天就應該出這篇博文的,就是因為這裡總是異常,今天仔細看了<br />// 才發現不是沒寫入權限,而是我雖然在AndroidMainfest.xml中寫了許可權,但是寫錯了位置..哭死!<br />tv.setText("儲存手勢成功!因為不存在手勢檔案," + "所以第一次儲存手勢成功會預設先創" +<br />"建了一個手勢檔案!然後將手勢儲存到檔案中.");<br />et.setText("");<br />gestureToImage(gesture);<br />} else {<br />tv.setText("儲存手勢失敗!");<br />}<br />} else {//當存在此檔案的時候我們需要先刪除此手勢然後把新的手勢放上<br />//讀取已經存在的檔案,得到檔案中的所有手勢<br />if (!gestureLib.load()) {//如果讀取失敗<br />tv.setText("手勢檔案讀取失敗!");<br />} else {//讀取成功<br />Set<String> set = gestureLib.getGestureEntries();//取出所有手勢<br />Object ob[] = set.toArray();<br />boolean isHavedGesture = false;<br />for (int i = 0; i < ob.length; i++) {//這裡是遍曆所有手勢的name<br />if (((String) ob[i]).equals(name)) {//和我們新添的手勢name做對比<br />isHavedGesture = true;<br />}<br />}<br />if (isHavedGesture) {//如果此變數為true說明有相同name的手勢<br />//----備忘1-------------------//gestureLib.removeGesture(name, gesture);//刪除與當前名字相同的手勢<br />/*----備忘2-----------------*/gestureLib.removeEntry(name);<br />gestureLib.addGesture(name, gesture);<br />} else {<br />gestureLib.addGesture(name, gesture);<br />}<br />if (gestureLib.save()) {<br />gov.clear(true);//清除筆畫<br />gestureToImage(gesture);<br />tv.setText("儲存手勢成功!當前所有手勢一共有:" + ob.length + "個");<br />et.setText("");<br />} else {<br />tv.setText("儲存手勢失敗!");<br />}<br />////------- --以下代碼是當手勢超過9個就全部清空 操作--------<br />if (ob.length > 9) {<br />for (int i = 0; i < ob.length; i++) {//這裡是遍曆刪除手勢<br />gestureLib.removeEntry((String) ob[i]);<br />}<br />gestureLib.save();<br />if (MySurfaceView.vec_bmp != null) {<br />MySurfaceView.vec_bmp.removeAllElements();//刪除放置手勢圖的容器<br />}<br />tv.setText("手勢超過9個,已全部清空!");<br />et.setText("");<br />}<br />ob = null;<br />set = null;<br />}<br />}<br />} else {<br />tv.setText("當前模擬器沒有SD卡 - -。");<br />}<br />}<br />} catch (Exception e) {<br />tv.setText("操作異常!");<br />}<br />} 

這裡也都很好理解,套路類似之前File檔案儲存體的套路,先判斷SD是否存在,然後是檔案是否存在:

如果檔案不存在就先直接添加到手勢到手勢倉庫中,然後手勢倉調用gestureLib.save()才算把手勢存到SD卡的手勢檔案中。 

檔案存在的話還要去判定是否檔案中包含了相同名字的手勢;當然這裡可以不判定是否有相同手勢名存在,然後進行刪除操作!其實也可不刪除,直接添加進去當前建立的手勢;原因看了下面的備忘解釋就明白了;

 

 

備忘 1:因為gestureLib儲存的手勢是個HashMap, key=手勢的名字,value=手勢,所以gestureLib.removeGesture(name, gesture);這種刪除方式只是刪除了手勢,該手勢名字依舊儲存在hashmap中,下次還有相同的name手勢存入的時候Hashmap就直接覆蓋本條目了。所以根據Hashmap的特徵,我們可以不進行刪除操作,可以直接gestureLib.addGesture(name, gesture);因為如果出現相同的手勢名字的手勢,Hashmap就會根據key(手勢的名字)直接覆蓋其條目的value(手勢)滴~

備忘2 :這裡也是一種刪除手勢的方式,但是這種方式跟備忘1的不同,這裡是將Hashmap中的條目刪除,也就是說key和value都被刪去!

 

下面看下如何把手勢轉成bitmap!

public void gestureToImage(Gesture ges) {//將手勢轉換成Bitmap<br />//把手勢轉成圖片,存到我們SurfaceView中定義的Image容器中,然後都畫出來~<br />if (MySurfaceView.vec_bmp != null) {<br />MySurfaceView.vec_bmp.addElement(ges.toBitmap(100, 100, 12, Color.GREEN));<br />}<br />} 

 

 

下面是如何遍曆手勢!

public void loadAllGesture(Set<String> set, Object ob[]) { //遍曆所有的手勢<br />if (gestureLib.load()) {//讀取最新的手勢檔案<br />set = gestureLib.getGestureEntries();//取出所有手勢<br />ob = set.toArray();<br />for (int i = 0; i < ob.length; i++) {<br />//把手勢轉成Bitmap<br />gestureToImage(gestureLib.getGestures((String) ob[i]).get(0));<br />//這裡是把我們每個手勢的名字也儲存下來<br />MySurfaceView.vec_string.addElement((String) ob[i]);<br />}<br />}<br />} 

 

下面最後來看看手勢的匹配!(超重要的!自己也搞了許久才找到解決的方法)

 

public void findGesture(Gesture gesture) {<br />try {<br />// 關於兩種方式建立模擬器的SDcard在【Android2D遊戲開發之十】有詳解<br />if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!<br />if (!file.exists()) {// 判定是否已經存在手勢檔案<br />tv.setText("匹配手勢失敗,因為手勢檔案不存在!!");<br />} else {//當存在此檔案的時候我們需要先刪除此手勢然後把新的手勢放上<br />//讀取已經存在的檔案,得到檔案中的所有手勢<br />if (!gestureLib.load()) {//如果讀取失敗<br />tv.setText("匹配手勢失敗,手勢檔案讀取失敗!");<br />} else {//讀取成功<br />List<Prediction> predictions = gestureLib.recognize(gesture);<br />//recognize()的返回結果是一個prediction集合,<br />//包含了所有與gesture相匹配的結果。<br />//從手勢庫中查詢匹配的內容,匹配的結果可能包括多個相似的結果,<br />if (!predictions.isEmpty()) {<br />Prediction prediction = predictions.get(0);<br />//prediction的score屬性代表了與手勢的相似程度<br />//prediction的name代表手勢對應的名稱<br />//prediction的score屬性代表了與gesture得相似程度(通常情況下不考慮score小於1的結果)。<br />if (prediction.score >= 1) {<br />tv.setText("當前你的手勢在手勢庫中找到最相似的手勢:name =" + prediction.name);<br />}<br />}<br />}<br />}<br />} else {<br />tv.setText("匹配手勢失敗,,當前模擬器沒有SD卡 - -。");<br />}<br />} catch (Exception e) {<br />e.printStackTrace();<br />tv.setText("由於出現異常,匹配手勢失敗啦~");<br />}<br />} 

 

 

那麼最後給各位童鞋說一下,其實IME手勢操作很是適合遊戲中使用,不管是觸控螢幕手勢操作還是今天講的IME手勢操作如果加到遊戲中那都是相當贊的!但是我們公司網遊引擎和架構不適合插入手勢 - -、唉~


其實前兩天應該發這篇的,但是因為工作忙了幾天,讓大家久等了,挺不好意思的,所以今天熬夜給大家寫了出來,現在都淩晨 7:00 了~

 

源碼: http://www.himigame.com/android-game/340.html

 

 

 

.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.