Ekran Dotykowy i Przyciski Cz.4

Fajnie jakby nasz projekt mógł sterować muchą za pomocą przycisków lub ekranu dotykowego, a nie tylko za pomocą akcelerometru. Dlatego w tym artykule pokażę, jak obsłużyć przyciski i ekran dotykowy. Program jest tak napisany, że gromadzi zdarzenia i wykonuje je w odpowiedniej kolejności.

Wystarczy zmodyfikować plik ProgramLogic.java w następujacy sposób:

package com.gameproject.alltogether;


import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
import java.lang.Thread;
import java.util.concurrent.ArrayBlockingQueue;


public class GameLogic extends Thread {
 
 private SurfaceHolder surfaceHolder;
 private GameView mGameView;
 private int game_state;
 public static final int PAUSE = 0;
 public static final int READY = 1;
 public static final int RUNNING = 2;
 private ArrayBlockingQueue<InputObject> inputQueue = new ArrayBlockingQueue<InputObject>(20);
 private Object inputQueueMutex = new Object();
 
 public GameLogic(SurfaceHolder surfaceHolder, GameView mGameView) {
 super();
 this.surfaceHolder = surfaceHolder;
 this.mGameView = mGameView;
 }
 
 
 public void setGameState(int gamestate) {
 this.game_state = gamestate;
 }
 public int getGameState(){
 return game_state;
 }



 @Override
 public void run() {
 long time_orig = System.currentTimeMillis();
 long time_interim;
 Canvas canvas;
 
 
 while (game_state == RUNNING) {
 canvas = null;
 try {
 
 canvas = this.surfaceHolder.lockCanvas();
 
 synchronized (surfaceHolder) {
 try {
 Thread.sleep(30);
 } catch (InterruptedException e1) {
 }

 time_interim = System.currentTimeMillis(); 
 int adj_mov = (int)(time_interim - time_orig);
 mGameView.update(adj_mov); 
 processInput();
 time_orig = time_interim; 
 this.mGameView.onDraw(canvas);                
 }
 } 
 finally {
 if (canvas != null) {
 surfaceHolder.unlockCanvasAndPost(canvas);
 }
 }
 }
 }
 
 
 public void feedInput(InputObject input) {
 synchronized(inputQueueMutex) {
 try {
 inputQueue.put(input);
 } catch (InterruptedException e) {
 }
 }
 }

 private void processInput() {
 synchronized(inputQueueMutex) {
 ArrayBlockingQueue<InputObject> inputQueue = this.inputQueue;
 while (!inputQueue.isEmpty()) {
 try {
 InputObject input = inputQueue.take();
 if (input.eventType == InputObject.EVENT_TYPE_KEY) {
 mGameView.processKeyEvent(input);
 } else if (input.eventType == InputObject.EVENT_TYPE_TOUCH) {
 mGameView.processMotionEvent(input);
 }
 input.returnToPool();
 } catch (InterruptedException e) {
 }
 }
 }
 }
 
}

Jak widać w metodzie run() wywoływana jest metoda processInput(), która to zbiera zdarzenia z ekranu dotykowego i przycisków. W samej metodzie processInput() wywołane są metody mGameView.processKeyEvent(input) odpowiedzialna za przyciski oraz mGameView.processMotionEvent(input) odpowiedzialna za ekran. Te metody zdefiniowane są w pliku GameView.java i wyglądają następująco.

@Override
 public boolean onTouchEvent(MotionEvent event) {
 try {
 int hist = event.getHistorySize();
 if (hist > 0) {
 for (int i = 0; i < hist; i++) {
 InputObject input = inputObjectPool.take();
 input.useEventHistory(event, i);
 mGameLogic.feedInput(input);
 }
 }
 InputObject input = inputObjectPool.take();
 input.useEvent(event);
 mGameLogic.feedInput(input);
 } catch (InterruptedException e) {
 }
 try {
 Thread.sleep(16);
 } catch (InterruptedException e) {
 }
 return true;
 }
 @Override
 public boolean onKeyDown(int kodKlaw, KeyEvent event) {
 try {
 InputObject input = inputObjectPool.take();
 input.useEvent(event);
 mGameLogic.feedInput(input);
 } catch (InterruptedException e) {
 }
 try {
 Thread.sleep(16);
 } catch (InterruptedException e) {
 }
 return super.onKeyDown(kodKlaw, event);
 }
 public void processMotionEvent(InputObject input){
 
 if (MenuPrzycisk.motionTakNie(super.getContext())) {
 if (input.y!=0) {
 this.playsound(ID_bzyk);
 
 }
 sprite.setX(input.x);
 sprite.setY(input.y);
 }
 }
 
 public void processKeyEvent(InputObject input){
 if (MenuPrzycisk.przyciskiTakNie(super.getContext())) {
 if(input.keyCode == KeyEvent.KEYCODE_DPAD_UP){
 sprite.setY(sprite.getY()-15);
 }
 if(input.keyCode ==  KeyEvent.KEYCODE_DPAD_DOWN){
 sprite.setY(sprite.getY()+15);
 }
 if(input.keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
 sprite.setX(sprite.getX()+10);
 }
 if(input.keyCode ==  KeyEvent.KEYCODE_DPAD_LEFT){
 sprite.setX(sprite.getX()-10);
 }
 }
 }

Oczywiście metody processKeyEvent i processMotionEvent nie mogą pobrać danych z przycisków i ekranu w tym celu należy nadpisać metody onKeyDown i onTouchEvent. Dopiero w tych metodach pobieramy zdarzenia i wysyłamy je do tablicy zdarzeń. Tablice zdarzeń umieściłem w pliku InputObject.java:

package pl.nstrefa.avrkwiat.gra1;


import java.util.concurrent.ArrayBlockingQueue;

import android.view.KeyEvent;
import android.view.MotionEvent;

public class InputObject {
 public static final byte EVENT_TYPE_KEY = 1;
 public static final byte EVENT_TYPE_TOUCH = 2;
 public static final int ACTION_KEY_DOWN = 1;
 public static final int ACTION_KEY_UP = 2;
 public static final int ACTION_TOUCH_DOWN = 3;
 public static final int ACTION_TOUCH_MOVE = 4;
 public static final int ACTION_TOUCH_UP = 5;
 public ArrayBlockingQueue<InputObject> pool;
 public byte eventType;
 public long time;
 public int action;
 public int keyCode;
 public int x;
 public int y;

 public InputObject(ArrayBlockingQueue<InputObject> pool) {
 this.pool = pool;
 }

 public void useEvent(KeyEvent event) {
 eventType = EVENT_TYPE_KEY;
 time = event.getEventTime();
 keyCode = event.getKeyCode();
 }

 public void useEvent(MotionEvent event) {
 eventType = EVENT_TYPE_TOUCH;
 int a = event.getAction();
 switch (a) {
 case MotionEvent.ACTION_DOWN:
 action = ACTION_TOUCH_DOWN;
 break;
 case MotionEvent.ACTION_MOVE:
 action = ACTION_TOUCH_MOVE;
 break;
 case MotionEvent.ACTION_UP:
 action = ACTION_TOUCH_UP;
 break;
 default:
 action = 0;
 }
 time = event.getEventTime();
 x = (int) event.getX();
 y = (int) event.getY();
 }

 public void useEventHistory(MotionEvent event, int historyItem) {
 eventType = EVENT_TYPE_TOUCH;
 action = ACTION_TOUCH_MOVE;
 time = event.getHistoricalEventTime(historyItem);
 x = (int) event.getHistoricalX(historyItem);
 y = (int) event.getHistoricalY(historyItem);
 }

 public void returnToPool() {
 pool.add(this);
 }
}

Dopiero w tym pliku widać jak pobierane są współrzędne z ekranu i to tu przechwytujemy kod przycisku.

W opcjach menu dodałem także możliwość wyłączania przycisków, ekranu lub akcelerometru, tak samo jak można wyłączyć muzykę w grze.

Cały projekt można pobrać tu