Creación de un juego (4): Modulo Input

Modulo encargado de recoger y procesar los eventos de entrada del usuario. En Android tenemos tres métodos de entrada principales: pantalla táctil, teclado y acelerómetro. Podremos recoger esos eventos de entrada de dos maneras:
Polling: solo comprobara el estado actual del evento, cualquier estado entre el evento actual y la ultima comprobación se perderá. Muy útil para los eventos táctiles pero inadecuado para recoger texto.
Handling: este método nos dará una historia cronológica completa de todos los eventos que se han producido. Mecanismo muy adecuado para recoger texto o cualquier otra tarea que se base en el orden de los acontecimientos.
Por lo tanto en este modulo vamos a crear tres manejadores (handlers) para recoger los eventos de teclado, eventos de un toque táctil y eventos multi toque.


1.1 Interface Input

Esta interface sera la encargada de recoger todos los eventos de entrada del usuario. Para ello creamos dos clases: una se encargara de los eventos del teclado (KeyEvent) y la otra de los eventos táctiles (TouchEvent). También declararemos varios métodos que se explicaran a continuación.

    public static class KeyEvent {
        public static final int KEY_DOWN = 0;
        public static final int KEY_UP = 1;

        public int type;
        public int keyCode;
        public char keyChar;

        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (type == KEY_DOWN)
                builder.append("key down, ");
            else
                builder.append("key up, ");
            builder.append(keyCode);
            builder.append(",");
            builder.append(keyChar);
            return builder.toString();
        }
    }
Empezamos creando un par de variables que nos servirán para identificar el tipo de evento del teclado:
KEY_DOWN: sucede cuando el usuario mantiene el dedo en una tecla del teclado.
KEY_UP: este evento sucede cuando el usuario levanta ese dedo de la tecla.
Por lo tanto de este modo conseguimos registrar los caracteres que va pulsando el usuario en su teclado, estos caracteres los almacenaremos en la variable keyChar. La variable type se usara para determinar el tipo de evento (0=KEY_DOWN y 1=KEY_UP). Y en la variable keyCode almacenaremos el valor Unicode de cada carácter.

Creamos el método toString para almacenar todos esos datos de un evento de teclado en un StringBuilder que sera convertido a String y devuelto por el método.

    public static class TouchEvent {
        public static final int TOUCH_DOWN = 0;
        public static final int TOUCH_UP = 1;
        public static final int TOUCH_DRAGGED = 2;

        public int type;
        public int x, y;
        public int pointer;

        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (type == TOUCH_DOWN)
                builder.append("touch down, ");
            else if (type == TOUCH_DRAGGED)
                builder.append("touch dragged, ");
            else
                builder.append("touch up, ");
            builder.append(pointer);
            builder.append(",");
            builder.append(x);
            builder.append(",");
            builder.append(y);
            return builder.toString();
        }
    }
Clase muy similar a la anterior, pero aquí tenemos tres tipos de evento táctil:
TOUCH_DOWN: sucede cuando el usuario mantiene un dedo en la pantalla.
TOUCH_DRAGGED: sucede cuando el usuario mueve un dedo por la pantalla.
TOUCH_UP: sucede cuando el usuario levanta el dedo de la pantalla.
En las variables x e y, almacenaremos las coordenadas de los eventos touch y en la variable pointer la id de ese evento touch. Comentar que podemos tener varios eventos touch simultáneamente y para diferenciarlos lo haremos por su id.

Terminamos con el método to String, que nos devolverá un String con todos los datos de ese evento touch.

    public boolean isKeyPressed(int keyCode)

    public boolean isTouchDown(int pointer);

    public int getTouchX(int pointer);

    public int getTouchY(int pointer);

    public float getAccelX();

    public float getAccelY();

    public float getAccelZ();

    public List<KeyEvent> getKeyEvents();

    public List<TouchEvent> getTouchEvents();
Podríamos decir que los siete primeros métodos son métodos polling y los dos últimos handling.

Con el metodo isKeyPressed comprobaremos si el parámetro keyCode esta siendo pulsado o no.

Usando el metodo isTouchDown comprobaremos si hay un evento touch en un puntero determinado.

Podremos conseguir las coordenadas X e Y de un evento touch con los métodos: getTouchX y getTouchY.

También podremos consultar las coordenadas X, Y, y Z del acelerómetro con los métodos: getAccelX, getAccelY y getAccelZ.

Los dos últimos métodos: getKeyEvents y getTouchEvents, nos devolverán una lista de sus respectivos eventos.


1.2 Interface TouchHandler
import java.util.List;

import com.example.slange.interfaces.Input.TouchEvent;

import android.view.View.OnTouchListener;

public interface TouchHandler extends OnTouchListener {
    
    public boolean isTouchDown(int pointer);
    
    public int getTouchX(int pointer);
    
    public int getTouchY(int pointer);
    
    public List<TouchEvent> getTouchEvents();
}
Interface que usa los mismos métodos que la anterior pero con la excepción de que esta interface extiende a OnTouchListener que nos ayudara a controlar los eventos touch en una view. A parte importamos la clase TouchEvent de la interface Input.


2 Clase Pool

Se podría decir que su función principal es reutilizar eventos de usuario, esta clase también es conocida como recolector de basura. Continuamente los eventos touch o key generan instancias que se van acumulando en una lista y perjudicando a nuestro sistema Android. Para solucionar esto vamos a implementar un concepto conocido como Pooling. En lugar de ir acumulando nuevas instancias en una lista, esta clase se encargara de reutilizar las instancias mas antiguas y así conseguir un equilibrio para nuestro sistema. A continuación vamos a ver un ejemplo de este concepto explicando detenidamente su metodología:

import java.util.ArrayList;
import java.util.List;

public class Pool<T> {
 
    public interface PoolObjectFactory<T> {
        public T createObject();
    }
Esta clase se denomina Genérica en Java y para implementar clases u objetos genéricos usamos: <>.

Es algo complejo así que recomiendo buscar información en internet.

Pero para hacernos una idea, la T indica la clase de objeto que usaremos en esta clase. Como nosotros vamos a usar esta clase para reciclar los eventos touch y key, podemos sustituir esa T por KeyEvent o TouchEvent y hacernos una idea de su funcionamiento. (En los siguientes puntos veremos el uso de esta clase)

Lo primero que hacemos en esta clase es crear una interface genérica PoolObjectFactory con un único método createObject que nos devolverá una nueva instancia del evento de usuario.

    private final List<T> freeObjects;
    private final PoolObjectFactory<T> factory;
    private final int maxSize;
Primero creamos un ArrayList freeObjects que sera donde se almacenen los eventos de usuario.

El segundo objeto factory, sera donde almacenemos las nuevas instancias de la interface PoolObjectFactory.

Y el ultimo miembro maxSize, sera el numero máximo de eventos que almacenara nuestra clase Pool.

    public Pool(PoolObjectFactory<T> factory, int maxSize) {
        this.factory = factory;
        this.maxSize = maxSize;
        this.freeObjects = new ArrayList<T>(maxSize);
    }
El constructor toma como parámetros las instancias PoolObjectFactory y el numero máximo de eventos a almacenar (maxSize).

Dentro del constructor almacenamos estos parámetros es sus respectivas variables (factory, maxSize) y creamos una nueva lista (freeObjects) con el tamaño máximo de eventos a recoger.

    public T newObject() {
        T object = null;

        if (freeObjects.isEmpty())
            object = factory.createObject();
        else
            object = freeObjects.remove(freeObjects.size() - 1);
        return object;
    }
Este método sera el encargado de entregarnos un nuevo objeto del evento de usuario.

Se comprueba si la lista freeObjects esta vacía y en ese caso se crea una nueva instancia con ese evento. En caso contrario se borra el ultimo evento de la lista.

Para finalizar el método nos devolverá el nuevo objeto.

    public void free(T object) {
        if (freeObjects.size() < maxSize)
            freeObjects.add(object);
    }
}
Para finalizar la clase, el método free sera el encargado de almacenar los eventos de usuario que ya no necesitemos. El método simplemente inserta la nueva instancia del objeto en la lista freeObjects.

A continuación veremos el uso de esta clase en nuestros manejadores (Handlers).


3.1 Clase KeyBoardHandler

En los siguientes tres puntos vamos a crear tres clases que nos servirán para manejar los eventos de usuario. A estas clases se les denomina manejadores o mas bien "Handlers" en ingles. Primero empezaremos con los eventos del teclado, vamos a ver esta clase en detalle.

import java.util.ArrayList;
import java.util.List;

import android.view.View;
import android.view.View.OnKeyListener;

import com.example.slange.interfaces.Input.KeyEvent;
import com.example.slange.interfaces.Pool;
import com.example.slange.interfaces.Pool.PoolObjectFactory;

public class KeyboardHandler implements OnKeyListener {
    
    boolean[] pressedKeys = new boolean[128];
    Pool<KeyEvent> keyEventPool;
    List<KeyEvent> keyEventsBuffer = new ArrayList<KeyEvent>();    
    List<keyEvent> keyEvents = new ArrayList<KeyEvent>();
Empezamos implementando la interface OnKeyListener que nos pide sobreescribir el método onKey. También importamos la clase KeyEvent de nuestra interface Input, la clase Pool y la interface PoolObjectFactory.

Creamos una variable booleana con 128 posibilidades, que nos servirá para conocer el estado actual (presionado o no) de cada tecla. Cada tecla del teclado tiene asignada una constante (KEYCODE_XXX) que va desde 0 hasta 127.

La siguiente se encargara de recoger los eventos del teclado y llevarlos a nuestra clase Pool. Para ello indicamos entre <> los eventos que queremos mandar a la clase Pool, en este caso KeyEvent.

El primer ArrayList (keyEventsBuffer) es genérico y especificamos que va a ser una lista con eventos KeyEvent. En esta lista se almacenaran los eventos que aun no han sido consumidos por nuestro juego, es decir, los nuevos eventos.

Y el segundo ArrayList (keyEvents) es un segundo buffer para recoger los eventos del teclado, mas adelante veremos su uso.

    public KeyboardHandler(View view) {
        PoolObjectFactory<KeyEvent> factory = new PoolObjectFactory<KeyEvent>() {
            public KeyEvent createObject() {
                return new KeyEvent();
            }
        };
        keyEventPool = new Pool<KeyEvent>(factory, 100);
        view.setOnKeyListener(this);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
    }
En el constructor usaremos como parámetro la view de la que queremos recibir los eventos del teclado.

Creamos un objeto PoolObjectFactory que sera el encargado de recoger los eventos KeyEvent y devolvernos una nueva instancia KeyEvent.

Continuamos creando nuestro recolector de eventos de teclado (keyEventPool), indicando como parámetro la instancia KeyEvent que debe almacenar nuestro Pool y como segundo parámetro el numero máximo de instancias a almacenar.

Establecemos que la view registre los eventos de teclado con el método setOnKeyListener que hará una llamada al método oKey para conocer el evento de teclado.

Nos aseguramos que la view puede recibe el foco aunque este en modo táctil (setFocusableInTouchMode) y así pueda recibir los eventos de teclado.

Y terminamos estableciendo que la view recibe el foco (requestFocus).

    public boolean onKey(View v, int keyCode, android.view.KeyEvent event) {
        if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE)
            return false;

        synchronized (this) {
            KeyEvent keyEvent = keyEventPool.newObject();
            keyEvent.keyCode = keyCode;
            keyEvent.keyChar = (char) event.getUnicodeChar();
            if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) {
                keyEvent.type = KeyEvent.KEY_DOWN;
                if(keyCode > 0 && keyCode < 127)
                    pressedKeys[keyCode] = true;
            }
            if (event.getAction() == android.view.KeyEvent.ACTION_UP) {
                keyEvent.type = KeyEvent.KEY_UP;
                if(keyCode > 0 && keyCode < 127)
                    pressedKeys[keyCode] = false;
            }
            keyEventsBuffer.add(keyEvent);
        }
        return false;
    }
Este método sera llamado cada vez que la view registre un evento de teclado.

Lo primero que hacemos es comprobar si el evento es de acción múltiple (ACTION_MULTIPLE) y directamente lo descartamos ya que estos eventos no nos sirven.

Debemos utilizar un bloque synchronized ya que los eventos se reciben en la interface de usuario y se leen en el hilo principal. Tenemos que asegurarnos de que ninguno de los dos accede en paralelo.

Dentro del bloque lo primero que hacemos es mandar el evento de teclado a nuestro recolector de basura (este se encargara de crear una nueva instancia o de reciclar una antigua).

Después de registrar el evento, almacenamos el carácter del teclado (keyCode) y su valor Unicode (keyChar) ayudandonos de los parámetros del método.

Lo siguiente es comprobar que tipo de evento a tenido lugar (ACTION_DOWN, ACTION_UP) en los dos casos registramos en nuestro keyEvent.type el tipo de evento. Comprobamos si la tecla pulsada esta dentro del rango de constantes (0 hasta 127) y dependiendo del tipo de evento devolveremos true o false a nuestra variable booleana pressedKeys.

Para terminar almacenamos nuestro evento de teclado en la lista keyEventBuffer.

    public boolean isKeyPressed(int keyCode) {
        if (keyCode < 0 || keyCode > 127)
            return false;
        return pressedKeys[keyCode];
    }
Con este método sabremos si una tecla es pulsada o no.

Para ello comprobamos si el parámetro keyCode esta dentro del rango (0 a 127) y en caso de que no lo este devolvemos el método con un false. En caso de que se encuentre dentro de ese rango, el método devolverá true en la posición keyCode de nuestra variable pressedKeys.

    public List<KeyEvent> getKeyEvents() {
        synchronized (this) {
            int len = keyEvents.size();
            for (int i = 0; i < len; i++) {
                keyEventPool.free(keyEvents.get(i));
            }
            keyEvents.clear();
            keyEvents.addAll(keyEventsBuffer);
            keyEventsBuffer.clear();
            return keyEvents;
        }
    }
}
Método que nos devolverá la lista de eventos de teclado mas nuevos. Se encargara de pasar los eventos de nuestra lista a nuestro recolector de basura Pool. A parte de limpiar nuestra lista. Y aquí es donde entra en juego el segundo buffer de almacenamiento de eventos. Comentar que deberemos llamar frecuentemente a este metodo para limpiar los eventos de teclado.

Como en el método onKey, aqui también debemos usar un bloque synchronized.

Con el bucle for mandamos todos los eventos de la lista keyEvents a nuestro recolector de basura keyEventPool, haciendo uso del método free que se encargara de almacenarlos.

Después limpiamos la lista keyEvents con el método clear, añadimos a la lista keyEvents los nuevos eventos keyEventsBuffer y limpiamos nuestro buffer principal keyEventsBuffer.

Finalmente el método devuelve la lista keyEvents con los últimos eventos producidos.


3.2 Clase SingleTouchHandler

En los dos siguientes puntos vamos a crear dos clases que se encargaran de manejar los eventos touch (single y multi). Antiguamente los eventos singletouch se usaban para un nivel de API 4 o inferior. Y lo eventos multitouch se implementaron a partir de la API 5.

Actualmente hay un porcentaje muy pequeño de terminales en el mercado que usa una versión de Android inferior a la 2.0 (0.2%) y con el tiempo terminara por desaparecer del mercado. Por lo tanto no tenemos que preocuparnos por esto. Pero vamos a conocer las dos clases (multi y single) que nos serán de gran ayuda para crear nuestros juegos. Vamos a ver la clase SingleTouch en detalle:

import java.util.ArrayList;
import java.util.List;

import android.view.MotionEvent;
import android.view.View;

import com.example.slange.interfaces.Pool;
import com.example.slange.interfaces.TouchHandler;
import com.example.slange.interfaces.Input.TouchEvent;
import com.example.slange.interfaces.Pool.PoolObjectFactory;

public class SingleTouchHandler implements TouchHandler {
    
    boolean isTouched;
    int touchX;
    int touchY;
    Pool<TouchEvent> touchEventPool;
    List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
    List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();
    float scaleX;
    float scaleY;
Empezamos implementando nuestra interface TouchHandler que nos hará sobreescribir sus cuatro métodos mas el método onTouch de la interface OnTouchListener. También importamos la clase TouchEvent de nuestra interface Input, la clase Pool y la interface PoolObjectFactory.

En el primer miembro isTouched almacenaremos si un puntero (un dedo) esta tocando la pantalla. Y en las dos siguientes variables (touchX, touchY) almacenaremos las coordenadas de ese puntero.

Creamos los tres miembros ya vistos en el punto anterior, para manejar las instancias de los eventos touch.

Y los dos últimos miembros los usaremos para tratar con las diferentes resoluciones de pantalla, mas adelante veremos su uso.

    public SingleTouchHandler(View view, float scaleX, float scaleY) {
        PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>() {
            public TouchEvent createObject() {
                return new TouchEvent();
            }            
        };
        touchEventPool = new Pool<TouchEvent>(factory, 100);
        view.setOnTouchListener(this);

        this.scaleX = scaleX;
        this.scaleY = scaleY;
    }
Constructor muy parecido al de KeyBoardHandler, a diferencia que en este registramos los eventos touch en nuestra view a través del método setOnTouchListener que hará una llamada al método onTouch. Y aquí almacenamos los parámetros (scaleX, scaleY) en sus respectivas variables.

    public boolean onTouch(View v, MotionEvent event) {
        synchronized(this) {
            TouchEvent touchEvent = touchEventPool.newObject();
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchEvent.type = TouchEvent.TOUCH_DOWN;
                isTouched = true;
                break;
            case MotionEvent.ACTION_MOVE:
                touchEvent.type = TouchEvent.TOUCH_DRAGGED;
                isTouched = true;
                break;
            case MotionEvent.ACTION_CANCEL:                
            case MotionEvent.ACTION_UP:
                touchEvent.type = TouchEvent.TOUCH_UP;
                isTouched = false;
                break;
            }
            
            touchEvent.x = touchX = (int)(event.getX() * scaleX);
            touchEvent.y = touchY = (int)(event.getY() * scaleY);
            touchEventsBuffer.add(touchEvent);                        
            
            return true;
        }
    }
Método que consigue el mismo resultado que el método onKey de nuestra clase KeyboardHandler, pero en este caso manejamos eventos TouchEvent.

Creamos un nuevo objeto TouchEvent que almacenamos en nuestro Pool touchEventPool.

Con el bloque switch comprobamos que tipo de evento a tenido lugar y lo almacenamos en la variable de nuestro objeto touchEvent.type. Según el tipo de evento almacenamos en nuestra variable isTouched true o false.

Almacenamos las coordenadas de ese evento en las variables de nuestro objeto (touchEvent.x, touchEvent.y) y también en las variables de nuestra clase (touchX, touchY). Para conocer las coordenadas usamos el parámetro del método (event) con sus métodos (getX, getY) y esto lo multiplicamos por la escala de la pantalla (mas adelante veremos como calcular la escala para los diferentes tamaños de pantalla).

Para terminar añadimos nuestro objeto touchEvent a nuestra lista touchEventsBuffer y devolvemos true al método.

    public boolean isTouchDown(int pointer) {
        synchronized(this) {
            if(pointer == 0)
                return isTouched;
            else
                return false;
        }
    }
Con este método comprobaremos si el puntero esta tocando la pantalla

    public int getTouchX(int pointer) {
        synchronized(this) {
            return touchX;
        }
    }

    public int getTouchY(int pointer) {
        synchronized(this) {
            return touchY;
        }
    }
Usando estos dos métodos podremos conocer las coordenadas del puntero. Nos devolverá el valor que haya almacenado en touchX y touchY.

    public List<TouchEvent> getTouchEvents() {
        synchronized(this) {     
            int len = touchEvents.size();
            for( int i = 0; i < len; i++ )
                touchEventPool.free(touchEvents.get(i));
            touchEvents.clear();
            touchEvents.addAll(touchEventsBuffer);
            touchEventsBuffer.clear();
            return touchEvents;
        }
    }
Mismo método que la clase KeyboardHandler. Recordar que deberemos llamar a este método frecuentemente.


3.3 Clase MultiTouchHandler

Clase que va a ser muy similar a la anterior pero con la diferencia que en esta clase vamos a usar mas de un puntero.

import java.util.ArrayList;
import java.util.List;

import android.annotation.TargetApi;
import android.view.MotionEvent;
import android.view.View;

import com.example.slange.interfaces.Input.TouchEvent;
import com.example.slange.interfaces.Pool;
import com.example.slange.interfaces.TouchHandler;
import com.example.slange.interfaces.Pool.PoolObjectFactory;

public class MultiTouchHandler implements TouchHandler {
    
    private static final int MAX_TOUCHPOINTS = 10;
    boolean[] isTouched = new boolean[MAX_TOUCHPOINTS];
    int[] touchX = new int[MAX_TOUCHPOINTS];
    int[] touchY = new int[MAX_TOUCHPOINTS];
    int[] id = new int[MAX_TOUCHPOINTS];
    Pool<TouchEvent> touchEventPool;
    List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
    List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();
    float scaleX;
    float scaleY;
Tenemos la misma implementación e importación que la clase anterior.

Empezamos declarando el numero máximo de punteros que vamos a controlar, en este caso serán 10.

Luego creamos cuatro variables para almacenar los datos de cada puntero. Si esta tocando la pantalla (isTouched), sus coordenadas x e y (touchX, touchY) y su identidad (id).

Lo siguiente es lo mismo que en la clase anterior.

    public MultiTouchHandler(View view, float scaleX, float scaleY) {
        PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>() {
            public TouchEvent createObject() {
                return new TouchEvent();
            }
        };
        touchEventPool = new Pool<TouchEvent>(factory, 100);
        view.setOnTouchListener(this);

        this.scaleX = scaleX;
        this.scaleY = scaleY;
    }
El constructor es el mismo que la clase anterior.

    public boolean onTouch(View v, MotionEvent event) {
        synchronized (this) {
            int action = event.getAction() & MotionEvent.ACTION_MASK;
            int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) 
                    >> MotionEvent.ACTION_POINTER_ID_SHIFT;
            int pointerCount = event.getPointerCount();
            TouchEvent touchEvent;
            for (int i = 0; i < MAX_TOUCHPOINTS; i++) {
                if (i >= pointerCount) {
                    isTouched[i] = false;
                    id[i] = -1;
                    continue;
                }
                int pointerId = event.getPointerId(i);
                if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) {
                    continue;
                }
                switch (action) {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_POINTER_DOWN:
                    touchEvent = touchEventPool.newObject();
                    touchEvent.type = TouchEvent.TOUCH_DOWN;
                    touchEvent.pointer = pointerId;
                    touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
                    touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
                    isTouched[i] = true;
                    id[i] = pointerId;
                    touchEventsBuffer.add(touchEvent);
                    break;

                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_POINTER_UP:
                case MotionEvent.ACTION_CANCEL:
                    touchEvent = touchEventPool.newObject();
                    touchEvent.type = TouchEvent.TOUCH_UP;
                    touchEvent.pointer = pointerId;
                    touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
                    touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
                    isTouched[i] = false;
                    id[i] = -1;
                    touchEventsBuffer.add(touchEvent);
                    break;

                case MotionEvent.ACTION_MOVE:
                    touchEvent = touchEventPool.newObject();
                    touchEvent.type = TouchEvent.TOUCH_DRAGGED;
                    touchEvent.pointer = pointerId;
                    touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
                    touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
                    isTouched[i] = true;
                    id[i] = pointerId;
                    touchEventsBuffer.add(touchEvent);
                    break;
                }
            }
            return true;
        }
    }
Método que a priori puede parecer complicado pero es muy similar al anterior, lo que en este caso almacenamos todos los punteros que pueda haber en la pantalla.

En la variable action almacenaremos el tipo de evento que tiene lugar. En pointerIndex almacenaremos el índice del puntero, este índice puede cambiar si un puntero suelta la pantalla, por lo que usamos la variable pointerId para almacenar la identidad real de cada puntero.

    public boolean isTouchDown(int pointer) {
        synchronized (this) {
            int index = getIndex(pointer);
            if (index < 0 || index >= MAX_TOUCHPOINTS)
                return false;
            else
                return isTouched[index];
        }
    }

    public int getTouchX(int pointer) {
        synchronized (this) {
            int index = getIndex(pointer);
            if (index < 0 || index >= MAX_TOUCHPOINTS)
                return 0;
            else
                return touchX[index];
        }
    }

    public int getTouchY(int pointer) {
        synchronized (this) {
            int index = getIndex(pointer);
            if (index < 0 || index >= MAX_TOUCHPOINTS)
                return 0;
            else
                return touchY[index];
        }
    }
Con estos métodos podremos saber si un puntero en concreto (lo indicaremos con el parámetro pointer) esta tocando la pantalla y también podremos conocer sus coordenadas x e y.

    public List<TouchEvent> getTouchEvents() {
        synchronized (this) {
            int len = touchEvents.size();
            for (int i = 0; i < len; i++)
                touchEventPool.free(touchEvents.get(i));
            touchEvents.clear();
            touchEvents.addAll(touchEventsBuffer);
            touchEventsBuffer.clear();
            return touchEvents;
        }
    }
Mismo método que en clases anteriores.

    private int getIndex(int pointerId) {
        for (int i = 0; i < MAX_TOUCHPOINTS; i++) {
            if (id[i] == pointerId) {
                return i;
            }
        }
        return -1;
    }
Con este ultimo método podremos conocer el índice de un puntero en concreto, para ello usamos el parámetro pointerId.


Con esto terminamos el articulo, pero comentar que podríamos hacer mas clases Handler para controlar por ejemplo los sensores de un terminal. En el código de ejemplo tenemos dos clases mas para manejar estos sensores.


CODIGO DE EJEMPLO: DESCARGAR

3 comentarios:

  1. Recién ando estudiando como aprender a programar juegos, debido al enorme éxito que tienen en android, como el de zgirls

    ResponderEliminar
  2. While you can still download and install previous versions of CyanogenMod, you're better off

    using its successor, Lineage OS, which is built to continue CyanogenMod's legacy. Check out our

    guide on how to download and install Install cyanogenmod

    ResponderEliminar
  3. que buen tema de como crear un juego para android es un muy buen sistema operativo me gusta mucho entretenerme jugando lo ya que es muy fácil y me atrapa, por eso creo que es muy bueno android.

    ResponderEliminar