viernes, 27 de junio de 2014

Corona SDK - Juego de naves (II) : Objetos de visualización y Gestión de eventos

  En este blog veremos como podemos crear un sencillo juego de naves para nuestro dispositivo de forma rápida gracias a las funcionalidades que nos ofrece Corona SDK.

  El juego consiste en una nave que podremos mover desplazando el dedo por la pantalla. La nave deberá esquivar las bombas que irán apareciendo para no explotar y perder la partida. Como podemos ver es un juego muy sencillo pero que nos permitirá ver algunas de las principales funciones de Corona como son los objetos de visualización, gestión de eventos, colisiones, gestión de escenas y el uso de spritee para la animación de la nave.

  A continuación se muestra una imagen del resultado final:



Objetos de Visualización


  Lo primero que pasaremos a crear será el fondo de nuestro juego. Para ello disponemos de la clase Display, con ella podemos crear cualquier objeto de visualización de nuestras escenas conocidos en Corona como DisplayObject. Para la creación y visualización de imágenes en la pantalla del dispositivo disponemos de dos funciones:

  display.newImage()
  display.newImageRect()

  La diferencia entre estas dos funciones es que si usamos la segunda cuando ejecutemos el juego en pantallas de diferente resolución Corona se encargará de re-dimensionarlas y ajustarlas a la nueva resolución, por tanto lo recomendable es esta. Una vez visto está diferencia pasamos a definir nuestros diferentes fondos:

local localGroup = display.newGroup( )
local backgroundGroup = display.newGroup( )

background = display.newImageRect( imgDir.."mainbackground.png", display.contentWidth, display.contentHeight )
background.x = display.contentCenterX ; background.y = display.contentCenterY
background:addEventListener( "touch", playerControl )
localGroup:insert(background)

middleBackground = display.newImageRect( imgDir.."bgLayer1.png", display.contentWidth+10, display.contentHeight )
middleBackground.x = display.contentCenterX ; middleBackground.y = display.contentCenterY
middleBackground.name = "middle"
backgroundGroup:insert( middleBackground )

beforeBackground = display.newImageRect( imgDir.."bgLayer2.png", display.contentWidth+10, display.contentHeight )
beforeBackground.x = display.contentCenterX ; beforeBackground.y = display.contentCenterY
beforeBackground.name = "before"

backgroundGroup:insert( beforeBackground )


 Hemos definido tres fondos para crear el cielo de la escena. El orden de creación de los objetos Display marcará el orden de visualización en pantalla, de tal forma que el último objeto creado se superpondrá a cualquier objeto que haya sido creado con anterioridad y ocupe el mismo espacio en la pantalla.

Cabe destacar en este código el uso de lo que se conoce en Corona como "Grupos". Un grupo es un contenedor de objetos DisplayObject con el que podremos gestionar de mejor forma nuestra escena. La clase Group hereda de DisplayObject y con esto podríamos destacar tres cosas muy importantes:

-  Un grupo puede contener objetos de visualización y/o a otros grupos.
-  A un grupo se le pueden aplicar las mismas transformaciones que a un objeto DisplayObject de tal forma que dichas transformaciones se le aplicará a todos los elementos que contiene.
-  Un objeto DisplayObject no puede estar contenido en dos grupos distintos.

  Con estas implicaciones en mente podemos por tanto plantearnos la gestión de una escena mediante el uso de un "árbol de escena". Un árbol de escena se podría ver como el árbol de directorios de un SO donde dicho árbol contendrá distintos nodos hoja que serán los diferentes objetos de visualización de nuestra escena y los nodos padre serán los objetos Group que los contendrán a los cuales les aplicaremos las distintas transformaciones.

  Otro de los objetos Display que más usaremos en un juego con animaciones 2D serán los sprite. En nuestro caso hemos hecho uso de estos sprites para crear la animación de la nave moviendo las alas:

 local options = {
    width = 115,
    height = 69,
    numFrames = 8
}
local playerSheet = graphics.newImageSheet( imgDir.."playerAnimation.png", options )
local sequenceData = { name = "player", start=1, count=8, time=1000, loopCount=0, loopDirection = "forward" }

spritePlayer = display.newSprite( playerSheet, sequenceData )
spritePlayer.x = display.contentCenterX/2 
spritePlayer.y = display.contentCenterY
spritePlayer.name = "player"
spritePlayer:play( )
backgroundGroup:insert( spritePlayer )

  Con la función "newImageSheet" de la clase Graphics nos creamos una imagen que Corona será capaz de manejar para obtener las diferentes imágenes o fotogramas que contiene para nuestra animación. La tabla sequenceData almacena las diferentes animaciones que podrá contener nuestro objeto Sprite, por tanto como podremos observar podemos definir distintas animaciones para una misma imagen dentro de esta tabla simplemente indicando el nombre "único" que identifique a la animación, el fotograma de inicio, el fotograma final y la duración de la animación. Una vez creado todos estos datos se los pasamos a la funcion "newSprite" para que nos cree nuestro objeto Sprite, lo posicionamos y iniciamos su animación.

  Por defecto se reproduce la primera animación que hayamos declarado en la tabla "sequenceData" pero si quisiéramos  iniciar otra simplemente debemos indicarlo mediante la función "setSequence("nombreAnimacion")" de la clase SpriteObject antes de ejecutar la llamada a la función "play()".



Gestión de eventos


  Los diferentes tipos de eventos que define Corona podrán ser capturados por los objetos DisplayObject y por el oyente global Runtime. La forma de añadir una captura de evento a un objeto se realiza de la siguiente forma:

  object : addEvenListener("nombreDelEvento", funcion_para_manejarlo)

  En Corona podemos distinguir dos tipos de eventos genéricos: los eventos en tiempo de ejecución y los eventos locales.

 Eventos en tiempo de ejecución


  Los eventos en tiempo de ejecución son capturados por el oyente Runtime ya que no van dirigidos a ningun objeto oyente en concreto. En este tipo de eventos podemos destacar los siguientes:

 -  EnterFrame: que se disparará en cada frame por segundo en el intervalo de nuestra aplicación (30 o 60).
 -  System: este tipo de evento se disparará cuando se produce algún evento externo a la aplicación, por ejemplo cuando el dispositivo se suspende.
 -  Orientation: se produce cuando la orientación del dispositivo cambia.

  Eventos locales


  Los eventos locales solo pueden ser capturados por los objectos Display ya que estos si van dirigidos a uno o varios objetos concretos. Entre todos los eventos locales podemos destacar los siguientes:

  - Hit: este tipo de evento ocurre cuando el usuario toca la pantalla del dispositivo.

  - Collision : se dispara cuando el motor de física detecta una colisión entre dos objetos.

  - Timer: este tipo de evento se dispara cuando transcurre X tiempo desde que se declaró, es el equivalente a un trigger.

  Cabe destacar y entrar en mayor profundidad en los eventos del tipo "hit" ya que serán unos de los más usados . Cuando el usuario pulsa la pantalla del dispositivo se dispararán dos tipos de eventos: "tap" y "touch". La diferencia entre ambos es que el evento tap solo se dispara cuando se pulsa la pantalla mientra que el evento "touch" será disparado en todo el ciclo de pulsación es decir cuando se pulsa, cuando se desplaza el dedo por la pantalla y cuando se suelta. Estos dos eventos serán lanzados para todos aquellos objetos que interceptan con la pulsación en pantalla. El primer objeto en recibir el evento será aquel que este por "encima" o con "menor profundidad" y se irá lanzando sucesivamente hasta el ultimo objeto con mayor profundidad. Esta cadena sucesiva de lanzamiento se podrá detener si la función donde se maneja el evento de cualquier objeto de la cadena devuelve "true".

  La forma de eliminar la captura de un evento de un objeto se realiza mediante la función :

   object : removeEvenListener("nombreDelEvento", funcion_para_manejarlo)

  Con respecto a dicha eliminación existe una gran diferencia entre los eventos locales y los de tiempo de ejecución. Cuando se destruye el objeto Display mediante la llamada a la función "removeSelf()"también se elimina el "listener" de este objeto para los distintos eventos que se haya asignado, por tanto si borramos un objeto Display para cambiar de escena no tenemos que preocuparnos de eliminar sus "listeners" asignados previamente. Sin embargo los "listeners" declarados para los eventos en tiempo de ejecución  permanecen entre el cambio de escena ya que el escuchador global Runtime no puede ser borrado y por tanto debemos asegurarnos de borrar los "listeners" cuando dejemos de necesitarlos para evitar errores indeseados.

  Una vez explicado todo esto pasamos a introducir la gestión de eventos en nuestra escena principal del juego:

local function playerControl( event )
-- body
local dx 
local dy

if (event.phase == "began") then
display.getCurrentStage():setFocus(event.target)

posInic.X = event.x
posInic.Y = event.y
elseif (event.phase == "moved") then

dx = event.x - posInic.X
dy = event.y - posInic.Y

spritePlayer:translate( dx, dy )

checkCollision()

posInic.X = event.x
posInic.Y = event.y
elseif (event.phase=='ended' or event.phase == "cancelled" ) then  
  display.getCurrentStage():setFocus(nil)
  end

return true
end

background:addEventListener( "touch", playerControl )

  En este código lo que hacemos es asignarle al fondo trasero la captura del evento "touch" para la pulsación sobre la pantalla por parte del usuario. La función encargada de manejar este evento será "playerControl" en la cual lo que hacemos es mover la nave a la posición en la que se encuentra el dedo del usuario, dicha posición nos la da las variables event,x y event.y. Como podemos observar en esta función hacemos una distinción de la "fase" en la que nos encontramos cuando se dispara el evento:

  - Si es la fase inicial asignamos el "focus" del programa a nuestra imagen del fondo trasero que fue la que lanzó este evento al ser pulsada (la podemos obtener mediante la variable event.target). Con esto lo que pretendemos es asegurarnos de que mientras el usuario mueva su dedo por la pantalla ningún otro listener sea disparado para este evento y por tanto solo se realice la funcionalidad de mover la nave mientras el usuario toca la pantalla y así evitar errores no deseados.
  - Si la fase es "moved" significa que el usuario no ha soltado el dedo de la pantalla y lo está moviendo por la pantalla por tanto cambiamos la posición de la nave y comprobamos si se ha detectado alguna colisión con alguna bomba.
  - Si la fase es final o cancelado significa que el usuario a dejado de pulsar sobre la pantalla o que por alguna razón externa se debe abortar la captura de este evento y por tanto lo que hacemos es quitar el "focus" del objeto fondo trasero.

  Por ultimo devolvemos "true" para indicar que hemos manejado este evento y que no se necesita seguir lanzandolo a objetos que se encuentre en un nivel inferior de la jerarquía de manejo para este evento.



El código completo lo podrás encontrar en la siguiente dirección:

https://github.com/Muffin-Apps/ShipGame-CoronaSDK

  

domingo, 22 de junio de 2014

Cómo monetizar nuestra aplicacion

Introducción


 Si bien es cierto que la mayoría de la gente a la hora de descargar una aplicación o un juego de la tienda de su dispositivo prefiere utilizar aplicaciones gratuitas y es muy reacia a gastarse el dinero en comprar aplicaciones para su dispositivo, podemos rentabilizar nuestra aplicación de diferentes formas como puede ser mediante el uso de un sistema de micro-pagos o de publicidad dentro de nuestra aplicación. Sin embargo en este blog nos centraremos en el primero.

 Desde hace unos pocos de años a surgido un concepto nuevo y que cada vez cobra más fuerza, especialmente en los videojuegos, y es el concepto de "freemiun":

  El concepto "freemiun" consiste en publicar una app de forma gratuita, de modo que el usuario pueda descargarla sin ningún coste, pero algunos de sus contenidos serán de pago. El usuario podrá realizar ciertas tareas y acceder a ciertas funcionalidades de forma gratuita pero para el resto de contenidos deberá pagar.

  Está es una buena forma de poder rentabilizar los costes y mantenimiento de nuestra aplicación a la vez de conseguir un amplio número de descargas, con lo que ello supone, debido a que la aplicación en sí es gratuita. Para poder implementar un sistema de transacciones dependerá de la plataforma en la que estemos desarrollando:

-  Plataforma iOS: en iOS el sistema de micro-pagos se conoce como "In-App Purchase". Este recurso te permite vender de forma segura recursos virtuales o no, manejar un sistema de suscripciones y contenidos premium. Para introducir este sistema en nuestra aplicación debemos hacer uso del framework "Store Kit framework" el cual nos permitirá conectar nuestra aplicación con la App Store para realizar los pagos de forma segura  y nos devolverá los elementos comprados por el usuario. Un posible ejemplo para la utilización de este sistema sería el de crear un juego con niveles extra, para los cuales el usuario deberá pagar por ellos para poder jugarlos. Para más información puede consultar el siguiente enlace: https://developer.apple.com/in-app-purchase/

-  Plataforma Android: si lo que queremos es introducir un sistema de transacciones en nuestra aplicación podemos hacer uso de "Google Wallet". Sin embargo podemos dejar la gestión y procesamiento de los pagos a Google, utilizando el " Google Play In-App Billing" para poder acceder a los servicios de Google Play Store y realizar las ventas de forma segura. Para poder hacer uso de estos servicios debemos obtener un "client-ID" para OAuth 2.0 en la consola de desarrollador de google para que así el servicio de Google Play pueda identificar de forma única a nuestra aplicación. Para obtener más información acerca de este proceso puede consultar el siguiente enlace : https://developers.google.com/wallet/instant-buy/android/tutorial

  El utilizar las bibliotecas oficiales de estas plataformas implica también el cobro de una comisión por parte de iOS o Android por cada compra realizada por el usuario.


Añadiendo un sistema de micro-pagos a nuestro juego


Una buena forma de rentabilizar nuestro juego y sacar beneficios sin que el usuario deba pagar por él, podría ser el introducir un sistema de monedas. Este sistema de monedas le proporciona al jugador el poder adquirir elementos del juego para poder personalizar su avatar, obtener elementos consumibles, obtener distintas mejoras y poder subir de nivel , etc. En dicho sistema podríamos diferenciar entre dos tipos de monedas:

-  Monedas virtuales: las cuales el jugador obtendrá durante el desarrollo del juego. El uso de este tipo de moneda también propicia una buena forma de "enganchar" al jugador al juego a corto plazo.
-  Monedas reales: las cuales podrán adquirirse mediante el pago con dinero real. Con estas monedas el jugador podrá tener acceso anticipado a elementos que se desbloquearán más tarde en el juego o a contenidos extras solo accesibles mediante el uso de estas monedas.

  El utilizar este sistema para introducir los micro-pagos en nuestro sistema tiene además una ventaja con respecto a comprar directamente los elementos con dinero real. Esto es debido a que si por ejemplo la forma de pago es mediante tarjeta de crédito el uso de la misma conlleva el cobro de unos intereses o comisión extra. Por tanto el uso continuado del pago con tarjeta implicará un mayor coste para el usuario en la compra de varios artículos, en lugar de utilizar un sistema de monedas virtuales donde el usuario solo deberá hacer uso de su tarjeta para comprar "X" monedas con las que podrá comprar diferentes elementos y las monedas restantes quedarán almacenadas para su posterior uso.

  Sin embargo el implementar de forma manual un sistema de micro-pagos en nuestro juego puede ser una tarea muy laboriosa ya que deberemos de implementar un servicio "backend" para dicho sistema, preferiblemente un servicio web ya que esto le proporcionará al usuario la ventaja de poder consultar sus compras o comprar artículos sin la necesidad de entrar en la aplicación. Este servicio web se deberá encargar del almacenamiento y gestión de los distintos elementos consumibles del juego, de la gestión del monedero del usuario y la realización de los pagos de forma segura, como por ejemplo mediante el uso de la API SOAP de PayPal (https://developer.paypal.com/webapps/developer/docs/classic/api/PayPalSOAPAPIArchitecture/).

  Por tanto podemos utilizar diferentes pluggins o servicios que han sido ya creados por otros desarrolladores y que ya están testeados y comprobados en diferentes aplicaciones. El elegir un servicio u otro dependerá de la tecnología que estemos utilizando para desarrollar nuestro juego.

  Por ejemplo si estamos utilizando la plataforma de Unity3D podemos encontrar diferentes pluggins para implementar este servcio de micro-pagos:

  -  Prime31 : nos proporciona diferentes pluggin para la monetización de nuestra aplicación para las plataforma iOS, Android, Windows. El precio de los diferentes pluggin varía entre los 40$ y 140$. Este es el enlace con su página web: https://prime31.com/plugins
  -  SOOMLA: es un framework de código abierto para la gestión de la economía del juego. Este framework permite de forma sencilla integrar una tienda ahorrando al desarrollador el esfuerzo de implementar la funcionalidad del almacén, dependencias de bienes virtuales, interfaces de facturación y beneficios. En enlace a su página web es el siguiente: http://project.soom.la/#unity_anchor.

Conclusiones


  Este nuevo concepto sobre cómo generar beneficios mediante el lanzamiento de aplicaciones gratuitas está cobrando cada vez más fuerza y las principales compañías y desarrolladoras de videojuegos lo están comenzando a adoptar ya que es una buena forma de captar nuevos usuarios debido a la preferencia de la gente a descargar aplicaciones gratuitas antes que tener que pagar por ellas.

  En mi opinión el uso de publicidad para generar beneficios y pagar por la aplicación para eliminarla es una mala forma ayudar al crecimiento de tu aplicación ya que la gente siempre preferirá desinstalar la aplicación y descargarse otra gratuita sin publicidad, a pagar 1€ por eliminar la publicidad. Sin embargo el utilizar un sistema de micro-pagos si se inserta de forma correcta en el juego, puede generar muchos beneficios ya que si el jugador se siente cada vez más atraído por el juego comenzará a pagar por él.

Principales referencias usadas









sábado, 21 de junio de 2014

Añadir el patrón Pull to Refresh a nuestra App


Uno de los patrones de interacción que vemos cada vez más es el de Pull To Refresh. Consiste en que el usuario realiza una acción de pull (tirar) vertical hacia abajo para ejecutar una actualización del contenido que esta viendo. Podemos verlo en aplicaciones móviles como gestores de correo, Google+ o Facebook, es decir, en aplicaciones que muestran contenido obtenido de forma remota y que por lo tanto necesita ser actualizado. Es una forma sencilla e intuitiva de que el usuario realice una actualización manual.

Aunque siempre puedes implementar tu mismo este patrón, existen varias bibliotecas que nos simplificarán la vida bastante. Yo he elegido ActionBar-PullToRefresh. Es muy fácil de usar y de personalizar pero, como se puede ver en su página de GitHub, ya no esta bajo mantenimiento. Otro aspecto a tener en cuenta es que solo funciona para versiones de Android igual o superiores a 4.0 (API 14).


Un ejemplo sencillo:

Vamos a aplicar el patrón sobre un Activity que despliega una lista, dejando la
personalización para mas adelante y trabajando con los valores por defecto. En el caso de los Fragment la inicialización es un poco diferente pero los cambios son mínimos.

Primero debemos incluir un PullToRefreshLayout como layout raíz de la interfaz de nuestra Activity (MainActivity):
<uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/my_ptr_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <ListView
    android:id="@+id/my_listView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >
  </ListView>

</uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout>

PullToRefreshLayout solo funciona por defecto con:
  • Clases que heredan de AbsListView (como ListView o GridView)
  • ScrollView
  • WebView

Para cualquier otro tendremos que implementar nuestro propio ViewDelegate.

Ahora inicializaremos PullToRefreshLayout en el código de MainActivity:
public class MainActivity extends Activity{
  private View txt;
  private PullToRefreshLayout pullLayout;
  private final OnRefreshListener onRefreshListener =
    new OnRefreshListener(){
      @Override
      public void onRefreshStarted(View v){
        // Aqui realizaremos la actualización
      }
    };

  @Override
  protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    pullLayout = (PullToRefreshLayout) findViewById(R.id.my_ptr_layout);
    // Inicializamos el PullToRefreshLayout
    ActionBarPullToRefresh.from(this)
      // Marcamos todos los hijos para que puedan recibir la accion de pull
      .allChildrenArePullable()
      // Le indicamos el OnRefreshlistener
      .listener(onRefreshListener)
      // Terminamos la inicializacion
      .setup(mPullToRefreshLayout);
    //...
  }
  //...
}


Hay que tener en cuenta que el método onRefreshStarted del listener se ejecutará sobre la hebra principal así que lo mejor es que lancemos algún tipo de AsyncTask o Loader. Para parar la animación de actualización llamaremos al método setRefreshComplete de nuestro PullToRefreshLayout:
@Override
public void onRefreshStarted(View view) {
  new AsyncTask<Void, Void, Void>() {
    @Override
    protected Void doInBackground(Void... params) {
    //Aqui realizamos la actualización del contenido
      return null;
    }

    @Override
    protected void onPostExecute(Void result) {
      super.onPostExecute(result);
      //...
      pullLayout.setRefreshComplete();
    }
  }.execute();
}


Con esto ya tenemos completo un ejemplo sencillo de uso de esta biblioteca.


Personalización

Ya hemos visto lo fácil que es usar la biblioteca. Ahora veremos lo fácil que es cambiar el comportamiento de la barra de actualización.

En la biblioteca tenemos la clase Options. Durante la inicialización podemos darle una instancia de esta clase para indicarle distintos elementos de configuración como:
  • headerLayout: Layout (indicado por el identificador del recurso) que se usará para la barra de actualización.
  • headerTransformer: El HeaderTransformer que se encargará de cambiar la barra de actualización según su estado (lo veremos mas adelante).
  • headerInAnimation: Animación que se usará al mostrar la barra de actualización.
  • headerOutAnimation: Animación que se usará al esconder la barra de actualización.
  • refreshScrollDistance: Distancia de pulling necesaria para que se dispare la actualización. Se especifica en porcentaje (de 0.0 a 1.0) con respecto a la altura de la vista a la que se hará pull. Por ejemplo 0.5f significa que solo tenemos que recorrer la mitad de la altura para disparar la actualización.
  • refreshOnUp: Por defecto la actualización se dispara cuando se recorre la distancia de pulling. Si activamos este elemento el usuario tendrá que levantar el dedo después de recorrerla, lo que le da la posibilidad de "arrepentirse" y volver atrás.
  • refreshMinimize: Esconder la barra de actualización durante esta. Es útil cuando la actualización es muy larga y no queremos tapar indefinidamente el ActionBar.
  • refreshMinimizeDelay: Es el tiempo que se esperará antes de esconder la barra de actualización.

Para aplicar nuestras opciones tendremos que actualizar el código de inicialización que hemos visto antes:
@Override
protected void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  pullLayout = (PullToRefreshLayout) findViewById(R.id.my_ptr_layout);
  Options options = Options.create()
    .scrollDistance(.5f)
    .headerLayout(R.layout.customised_header)
    .headerTransformer(new CustomisedHeaderTransformer())
    .build();

  // Inicializamos el PullToRefreshLayout
  ActionBarPullToRefresh.from(this)
    // Le indicamos el objeto options a usar
    .options(options)
    .allChildrenArePullable()
    .listener(onRefreshListener)
    .setup(mPullToRefreshLayout);
  //...
}


La instancia de la clase CustomizedHeaderTransformer es la encargada de actualizar la barra según el estado de la actualización. Hereda de la clase abstracta HeaderTransformer y debe implementar los siguientes métodos:
  • boolean showHeaderView(): Se llama cuando la barra debe mostrarse.
  • boolean hideHeaderView(): Se llama cuando la barra debe esconderse.

Además de esos dos métodos se pueden implementar los siguientes:
  • onViewCreated(Activity activity, View headerView): Se llama cuando se ha creado el View de la barra de actualización.
    • activity: Activity a la que esta asociada la barra.
    • headerView: Barra de actualización creada.
  • onReset(): Se llama cuando la barra tiene que ser reiniciada.
  • onPulled(float perc): Se llama cuando el usuario realiza la acción de pulling.
    • perc: Porcentaje de distancia de pulling.
  • onRefreshStarted(): Se llama cuando se dispara la actualización.
  • onReleaseToRefresh(): Se llama cuando el usuario ha recorrido toda la distancia de pulling pero aún necesita levantar el dedo.
  • onRefreshMinimized(): Se llama cuando la actualización está llevando más tiempo que el especificado en refreshMinimizeDelay.
  • onConfigurationChanged(Activity activity, Configuration conf): Se llama cuando la configuración de la Activity a la que está asociada la barra cambia.
    • activity: Activity a la que esta asociada la barra.
    • conf: Nueva configuración.

Como hemos visto usar esta biblioteca es muy sencillo y nos da una gran libertad a la hora de personalizar el comportamiento de la barra de actualización. Con esto podremos implantar el patrón Pull to Refresh a nuestras aplicaciones.








miércoles, 14 de mayo de 2014

Corona SDK - Juego de naves (I) : Introducción, Elección de la herramientas y el lenguaje Lua.

Introducción


  Hoy en día dado el auge de las aplicaciones para dispositivos móviles, existe una gran cantidad de herramientas para los desarrolladores para facilitar el trabajo y es muy importante elegir correctamente la tecnología a usar para nuestro proyecto ya que esto nos facilitará mucho las cosas.

 Elección de la herramienta


  Una vez tengamos nuestra idea bien detallada nos podemos poner en la
búsqueda de las herramientas necesarias para desarrollar nuestra idea. Sin embargo el abanico de herramientas es, por suerte, muy amplio y podremos vernos desbordados sin saber cual elegir.

  Existen multitud de cuestiones que debemos tener en cuenta para esta elección, por ejemplo:

  • Desarrollar para una plataforma concreta o que sea multi-plataforma.
  • Que la curva de aprendizaje sea mínima o disponemos del tiempo suficiente para aprender a usarlo. 
  • Elegir entre gráficos 2D o gráficos 3D.
  • Trabajar con modelos 3D de objetos o con imágenes planas.
  • Que sea gratis o contamos con una apoyo económico y podemos permitir pagar cierta cantidad para el uso de la herramienta.
  • Que esté bien documentado o cuente con un refuerzo importante por parte de la comunidad ya que esto puede reducir considerablemente el tiempo de desarrollo. 
  • Y otras muchas cuestiones relativas a nuestro proyecto y más personales como puede ser: el acceso a los componentes físicos del dispositivo (acelerómetro, cámara, etc), conectividad a internet (con facebook, twitter, etc), manejo con las bases de datos, etc.
  No es necesario que la herramienta cumpla con todos los puntos pero si que cubra el máximo de ellos y que las ventajas superen a los inconvenientes de manera personal.

  Estas son algunas de las herramientas que podemos encontrar por el mercado:

-  Unity3D : un motor de videojuegos 2D/3D muy popular y con una potencia extraordinaria tanto para juegos 2D o 3D. Además con la ultima actualización podemos publicar nuestra aplicación para dispositivos moviles de forma gratuita.

-  Cocos-2D : es un framework muy enfocado a la creación de juegos 2D. Tiene la peculiaridad de que si queremos desarrollar juegos para una plataforma distinta a IOs debemos hacerlo en C++ y pelearnos con los punteros tipicos de este lenguaje para la gestión de escenas.

-  Corona : un framework enfocado tanto a la creación de juegos como a la de apps de de todo tipo. Esto último es posible gracias a que podemos incluir bibliotecas como OpenGL, Facebook Connect o Google Maps entre otras.

-  AddEngine : motor de juegos de codigo abierto con el cual podremos desarrollar nuestra aplicaciones en Java. Tambien posee MultiTouch o Red multijugador entre otras características. Sin embargo cuenta con una excasa documentación oficial teniendo que recurrir a los foros para dar con las soluciones a nuestros peores problemas.

  Sin embargo para este blog y en esta ocasión nos quedaremos con Corona ya que en mi opinión nos ofrece la posibilidad de elaborar un juego 2D potente y de forma muy sencilla, abstrayendonos casi por completo de los detalles de más bajo nivel.


LUA


  El lenguaje utilizado para desarrollar en Corona es Lua. El objetivo de este blog no es aprender este lenguaje sin embargo combiene dar algunas menciones especiales:

  Lua es un lenguaje no tipado, es decir en una variable podemos almacenar cualquier elemento ya sean datos o funciones. La forma de declarar una variable es la siguiente:

-  Local nombreVariable : si queremos definir la variable de forma local al módulo o función en la que estamos trabajando.
-  _G.nombreVariable : si queremos definir una variable de forma global. Tambien se puede definir una variable global sin nada delante, es decir:

-  nombreVariable

  Sin embargo una vez se interprete esta sentencia esta variable será introducida igualmente en la tabla _G produciendo el mismo resultado. Por tanto es recomendable utilizar la primera forma para distinguir cuando queremos declarar una variable global de cuando se nos ha olvidado poner "local" delante de una variable.

  En mi opinión esta forma de declarar variables globales es un tanto peligrosa ya que existen palabras reservadas para algunas variables del entorno o  bibliotecas específicas de Corona en la tabla _G y si tuviéramos la suerte de declarar nosotros alguna variable con el mismo nombre de alguna de estas variables reservadas, machacaríamos su valor y podríamos provocar errores indeseados. Por ejemplo:

  _G.debbug = "lo que sea"

 Esto provocaría machacar la biblioteca que usa Corona para los mensajes de depurarción y errores mostrados por consola.

  Mi consejo sería definirnos nosotros nuestra propia tabla de variables globales en un modulo aparte, por ejemplo globalVar.lua, y cuando quisiéramos utilizar alguna variable en otro modulo, importemos el módulo globalVar.lua mediante la sentencia require("globalVar.lua") para acceder a dicha tabla.

  En Lua existe un tipo especial llamado "table". Las tablas son colecciones de objetos con la característica de que podemos indexar la tabla de la forma tradicional mediante indices o mediante entradas de tabla:

-  local t = {4, 5} -- nos crea una tabla de dos elementos
   print(t[1]) -- imprime 4

-  local t = {}
   t.usuario = "Juan" -- esto crearía una entrada con el nombre "usuario"  
   print(t.usuario) -- imprime Juan

  Como podemos observar una característica importante de esta colección es que el índice del primer elemento es "1".

  Para más información sobre este lenguaje podemos consultar el siguiente enlace para la documentación oficial sobre Lua 5.1:

http://www.lua.org/manual/5.1/es/manual.html

Animación SwipeList en Android

Introducción

  En el siguiente post expondremos como hemos añadido a nuestra aplicación varias animaciones para cuando el usuario realiza el gesto "swipe" sobre un elemento de una lista para que se visualice la acción asociada a ese gesto mientras se realiza el mismo.

  Una de los principales puntos de un diseño centrado en el usuario es que la interfaz sea agradable y fluida. Por ejemplo si desarrollamos una aplicación para la lectura de documentos el cambio de página no se limite simplemente a dejar de mostrar la pagina actual y mostrar la siguiente, sino que cuando el usuario deslice el dedo por la pantalla se realice una animación de transición del cambio de página entre la nueva y la actual y produzca el efecto visual de leer un libro real.

  Android nos proporciona diferentes animaciones para esta transiciones intermedias y en nuestro caso la que nos interesaba era la animación "Screen Slider" de tal forma que cuando el usuario deslice el dedo sobre un elemento de una lista se produzcan acciones diferentes para ese elemento y además la vista del elemento cambie conforme se mueve el dedo para mostrar la acción que se va a realizar. En nuestro caso las acciones serían las siguientes:

-  Si se desliza de izquierda a derecha el elemento se actualiza.
-  Si se desliza de derecha a izquierda el elemento queda seleccionado para después poder borrarse.

  Sin embargo esta animación que nos proporciona Android tiene una pega y es que por defecto una vez que se detecta el gesto, la animación se reproduce de principio a fin y no tenemos la posibilidad de ajustarla al movimiento del dedo de tal forma que si el usuario inicia el gesto y de repente decide cancelar la acción no habría posibilidad de parar y volver atrás en la animación.

  Una posible solución a este problema sería el de definir la animación paso a paso para tener un mayor control sobre ella. Sin embargo este trabajo nos lo podemos ahorrar gracias a la amplia comunidad de Android y concretamente al grupo "47deg". Este grupo no da la solución y nos proporciona una biblioteca para realizar dos tipos de animaciones para cuando el usuario realiza el gesto "Swipe" sobre un elemento de una lista. Una aplicación de ejemplo para comprender mejor esto la puedes encontrar en su cuenta "gitHub" en la siguiente dirección:

- https://github.com/47deg/android-swipelistview-sample

Instalación

  En nuestro caso no pudimos utilizar la herramienta "gradle" para generar el jar de la biblioteca así que para poder integrar la biblioteca a nuestro proyecto lo que hemos hecho a sido integrar el código de la biblioteca en nuestro proyecto: concretamente introduciendo el paquete "fortysevendeg" en nuestro proyecto.

  Para resolver las dependencias necesarias para esta biblioteca nos bajamos la ultima versión de la biblioteca "nineoldandroids" y la integramos en nuestro proyecto. La podéis encontrar en la siguiente cuenta de gitHub:

- https://github.com/JakeWharton/NineOldAndroids/

SWIPE LIST VIEW Library

  Antes de pasar a utilizar la biblioteca explicaremos brevemente el contenido de la misma:
  
  -  SwipeListView: esta clase define un listView con la posibilidad de visualizar la animación al realizar el gesto swipe sobre un elemento de la lista. La clase define cuatro modos para activar o desactivar las diferentes animaciones:
 
   -  SWIPE_MODE_RIGHT: activar solo la animación de transición de izquierda a derecha.

   -  SWIPE_MODE_LEFT: activar solo la animación de transición de derecha a izquierda.
   -  SWIPE_MODE_BOTH: activar ambas animaciones
   -  SWIPE_MODE_NONE: no se activa ninguna.

 El atributo "swipeMode" lo podemos cambiar mediante la siguiente función pasándole alguno de los diferentes modos:
 
 setSwipeMode(int modoNuevo)

 -  BaseSwipeListViewListener: esta clase "abstracta" implementa la interfaz SwipeListViewListener la cual define las diferentes acciones que podemos realizar cuando se produce un cambio en un elemento de la lista: por ejemplo podemos realizar un acción concreta cuando la animación finaliza. La ventaja de utilizar esta clase y no implementar directamente la interfaz es que podemos sobreescribir solo los métodos que queramos usar. 

 Utilización de la biblioteca

  Ahora veremos como utilizar esta biblioteca y como dar solución a nuestro problema de la forma más fácil:

  Lo primero que debemos hacer es crearnos el layout para nuestro elemento de la lista el cual deberá estar formado por dos capas. En nuestro caso son las siguientes:

- La capa delantera compuesta de varias etiquetas de organizadas en un linearLayout.
- Y la capa trasera compuesta por una sola etiqueta correspondiente a la acción de "editar".

  La estructura del layout sería la siguiente (se he omitido los atributos no relevantes para este caso):

<FrameLayout
...
>
   <LinearLayout
   android:id = "@+id/back"
   android:tag="back"
   ...
   >
      <TextView ... >
   </LinearLayout>

   <LinearLayout
   android:id = "@+id/front"
   android:tag = "front"
   ...
   >
     <TextView ... >
   </LinearLayout>
</FrameLayout>
 


Y definir el layout para nuestra lista de elementos del tipo SwipeListView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.fortysevendeg.swipelistview.SwipeListView
        xmlns:swipe="http://schemas.android.com/apk/res-auto"
        android:id="@id/android:list"
        android:listSelector="#00000000"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        swipe:swipeFrontView="@+id/front"
        swipe:swipeBackView="@+id/back"
        swipe:swipeCloseAllItemsWhenMoveList="true"
        swipe:swipeMode="both"/>
        <!-- swipe:swipeDrawableChecked="@drawable/choice_selected" swipe:swipeDrawableUnchecked="@drawable/choice_unselected" -->

</LinearLayout>
 

Pues bien ya solo nos queda utilizar la biblioteca para crearnos nuestro listView animado. 

public class ExamListFragment extends ListFragment {
 
@Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);
        ExamListListener examListener;
        SwipeListView swipeListView = (SwipeListView) getListView();
       
        swipeListView.setSwipeActionLeft(SwipeListView.SWIPE_ACTION_CHOICE);
        swipeListView.setSwipeOpenOnLongPress(false);
       
        examListener = new ExamListListener(this, swipeListView, actionListener);
       
        swipeListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            swipeListView.setMultiChoiceModeListener(examListener);
       
        swipeListView.setSwipeListViewListener(examListener);
        swipeListView.setOnItemClickListener(examListener);
    }
 


public static class ExamListListener extends BaseSwipeListViewListener implements AbsListView.MultiChoiceModeListener{

 @Override
        public void onOpened(int position, boolean toRight) {
            // Aqui se llama cuando la animación de abrir termina
        }

        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position,
                long id, boolean checked) {
            mode.setTitle("Selecionado/s (" + swipeListView.getCountSelected() + ")");
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
            case R.id.action_delete:
                

                // ...

                return true
            default:
                return false;
            }
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.main_context, menu);
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            swipeListView.unselectedChoiceStates();
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }


}



 Lo primero que hacemos es obtener el ListView de nuestra lista que será una instancia de la clase SwipeListView. Una vez obtenida la instancia establecemos la acción de dejar seleccionado el elemento cuando se produzca un "swipe" de derecha a izquierda y quitamos la acción correspondiente al "long click" ya que por defecto este gesto tiene asignada la acción de abrir el elemento. Después creamos una instancia del objeto ExamListListener el cual será el encargado de recibir todos los eventos producidos sobre los elementos de la lista y realizar las acciones oportunas.

domingo, 11 de mayo de 2014

Loaders de Android (II): Implementar nuestro propio Loader


Implementar Loaders

Como el origen de nuestro interés por implementar nuestro propio Loader es la negativa a usar la clase CursorLoader, veamos que nos aporta esta clase para saber que deberíamos incluir en la nuestra:
  • Carga asíncrona de datos. La mayoría lo conseguimos heredando de AsyncLoader pero aún así tendremos que añadir código para manejar los datos dentro de la clase durante las distintas etapas. Afortunadamente este código es totalmente reutilizable, así que nos puede servir para distintos Loaders.
  • Consulta encapsulada. Al usar el modelo de consulta de los ContentProvider, CursorLoader proporciona una manera de expresar las consultas usando diversos parámetros (uri, projection...). Como queremos cumplir con el primer apartado, tendremos que ser capaces de expresar las consultas sin llegar a realizarlas.
  • Monitorización de datos. Cuando los datos originales cambian CursorLoader es notificado. Podemos obtener esta funcionalidad fácilmente, aunque nos obligará a añadir cierta funcionalidad a la fuente de datos.

Estos serían los tres "pilares" de nuestro Loader. No solo podemos conseguir implementarlos todos sino que podemos tener una solución aún mas independiente de la fuente de datos y del formato de los datos y con una monitorización con mayor alcance.


Carga asíncrona

Para este apartado se sigue como fuente principal el artículo de Alex Lockwood sobre implementar Loaders. No solo recomiendo leer el artículo completo (son cuatro partes), sino que también recomiendo echarle un vistazo a su blog sobre Android porque hay artículos bastante interesantes.

Como se ha comentado antes, gran parte de esta funcionalidad se consigue heredando de la clase abstracta AsyncLoader. La implementación de nuestro Loader pasa por implementar correctamente algunas de las funciones definidas en AsyncLoader:
  • loadInBackground(): Es la función donde realizaremos la lectura de datos en sí. En este primer apartado no tendremos la consulta completa ya que profundizaremos en eso en el segundo apartado.
  • deliverResult(): Función llamada para enviar el resultado de la lectura al LoaderManager.
  • onStartLoading(): Función llamada cuando LoaderManager inicia nuestro Loader.
  • onStopLoading(): Función llamada cuando LoaderManager detiene nuestro Loader.
  • onReset(): Función llamada cuando LoaderManager desactiva nuestro Loader.
  • onCanceled(): Función llamada cuando LoaderManager cancela la lectura de datos.
  • releaseResources(): Esta realmente no es un método de AsyncLoader sino del código de Lockwood. Sirve para hacer una limpieza de los resultados de la lectura cuando estos pasan a ser inválidos. Por ejemplo, en el caso de usar Cursor (allá vosotros), tendríamos que incluir aquí una llamada a close().

En el artículo de Lockwood se nos presenta una plantilla para implementar nuestros Loaders. Usaremos este código para implementar nuestro primer pilar, pero lo iremos modificando para conseguir un loader más genérico. A continuación vemos una versión "traducida" del código:

public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {

    // Mantenemos una referencia al resultado obtenido
    private List<SampleItem> mData;

    public SampleLoader(Context ctx) {
        super(ctx);
    }

    @Override
    public List<SampleItem> loadInBackground() {
        List<SampleItem> data = new ArrayList<SampleItem>();
        // TODO: Realizar la verdadera consulta (apartado 2)
        return data;
    }

    @Override
    public void deliverResult(List<SampleItem> data) {
        if (isReset()) {
            // Si el Loader ha sido desactivado liberamos los resultados y salimos.
            releaseResources(data);
            return;
        }

        // Mantenemos una referencia a los resultados anteriores para que
        // el recolector de basura no los elimine hasta que hallamos entregado los
        // nuevos resultados.
        List<SampleItem> oldData = mData;
        mData = data;
    
        if (isStarted()) {
            // Si el Loader está en estado iniciado entregamos los resultados.
            // La clase AsyncLoader se encargará de ello.
            super.deliverResult(data);
        }

        // Liberamos los viejos resultados ahora que hemos entregado los nuevos.
        if (oldData != null && oldData != data) {
            releaseResources(oldData);
        }
    }

    @Override
    protected void onStartLoading() {
        if (mData != null) {
            // Si ya tenemos resultados los entregamos.
            deliverResult(mData);
        }

        //TODO: Registrarse a la fuente de datos para monitorizar cambios (apartado 3)

        if (takeContentChanged() || mData == null) {
            // Si hemos sido notificados de cambios o si no tenemos resultados
            // iniciamos la lectura de datos.
            forceLoad();
        }
    }

    @Override
    protected void onStopLoading() {
        // Intentamos cancelar la lectura de datos.
        cancelLoad();
    }

    @Override
    protected void onReset() {
        // Nos aseguramos de que el Loader ha sido detenido
        onStopLoading();

        // Liberamos los resultados que manteniamos
        if (mData != null) {
            releaseResources(mData);
            mData = null;
        }

        // TODO: Eliminar el registro de la fuente de datos (apartado 3)
    }

    @Override
    public void onCanceled(List<SampleItem> data) {
        // intentamos cancelar la lectura de datos.
        super.onCanceled(data);

        // Liberamos los resultados
        releaseResources(data);
    }

    protected void releaseResources(List<SampleItem> data) {
        // En este ejemplo no es necesario añadir código aqui.
    }
}


Con esto ya tenemos un Loader que realizará la lectura de datos en segundo plano. Sin embargo, el hecho de que la lectura en sí forme parte de su implementación hace necesario que tengamos una clase Loader por cada lectura distinta. Este problema se soluciona en el siguiente apartado.


Consulta encapsulada

Lo bueno del código de Lockwood es que, aparte del método loadInBackground(), todo es reutilizable. Lo único que varía es la consulta de datos en sí. Por ello debemos extraer la consulta y así podremos reutilizar todo lo demás. Además podemos conseguir un añadido sobre CursorLoader: tener un Loader independiente del tipo de dato que devuelva la consulta. Hay dos maneras de conseguir esto: mediante herencia y mediante composición.

Mediante herencia: Convertimos la implementación antes vista en una clase abstract AbsLoader<D> (ahora debemos parametrizarla para manejar cualquier tipo de dato). Mantenemos la implementación de todos los métodos excepto de loadInBackground(), que dejaremos sin implementar. Para tener lecturas concretas solo tenemos que crear una clase que herede de AbsLoader y que proporcione la lectura de datos implementando loadInBackground().

public abstract class AbsLoader<D> extends AsyncTaskLoader<D> {
    // ...
}


Mediante composición: Delegaremos la lectura de datos en un objeto externo. Para ello creamos la interfaz Query<D> con un solo método, execute(), que nos devolverá un objeto de tipo D. Solo queda que modifiquemos SampleLoader para que utilice este objeto en loadInBackground():

public class BaseLoader<D> extends AsyncTaskLoader<D> {

    // ...
    private Query<D> query;

    public SampleLoader(Context ctx, Query<D> query) {
        this.query = query;
        super(ctx);
    }

    @Override
    public D loadInBackground() {
        return query.execute();
    }

    // ...
}


Sea cual sea el método escogido ya tenemos un Loader básico que podremos reutilizar con distintos tipos y fuentes de datos.


Monitorización de datos

Si queremos podemos hacer que nuestro Loader sea notificado cuando los datos que ha leído cambian en la fuente original. Lo más sencillo es aplicar un patrón observador. No es la intención de este post el explicar este patrón de diseño. Para ello hay mejores fuentes (como esta o esta otra).

Definimos una interfaz Observer (con un solo método update()) que nuestro Loader implementará de la siguiente manera:

@Override
public void update(){
    onContentChanged();
}

Definimos la interfaz Subject que será la encargada de administrar los Observer y notificarles. Podemos hacer que la propia fuente de datos implemente la interfaz o creamos explícitamente una clase que la implemente y en la fuente de datos guardamos una instancia de esta clase. En cualquier caso debemos añadir una llamada a notifyObservers() al final de cada método que realice cambios en los datos.

Por último solo queda modificar nuestra clase Loader para que acepte objetos Subject y se registre o se elimine del registro cuando sea necesario (en el lugar donde se colocaron comentarios TODO en el primer apartado):

public class BaseLoader<D> extends AsyncTaskLoader<D> implements Observer {
    private D mData;
    private Query<D> mQuery;
    private ArrayList<Subject> mSubjects;
    private boolean registered;

    public ObserverLoader(Context context, Query<D> query){
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>();
    }

    public ObserverLoader(Context context, Query<D> query, Subject dataSubject) {
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>();
        if(dataSubject != null)
            mSubjects.add(dataSubject);
    }

    public ObserverLoader(Context context, Query<D> query, List<Subject> dataSubjects){
        super(context);
        registered = false;
        mQuery = query;
        mSubjects = new ArrayList<Subject>(dataSubjects);
    }

    // ...
    @Override
    protected void onStartLoading(){
        // ...
        if(!registered){
            registered = true;
            for(Subject sub : mSubjects)
                sub.registerObserver(this);
        }
        // ...
    }

    // ...

    @Override
    protected void onReset() {
        // ...

        if(registered){
            registered = false;
            for(Subject sub : mSubjects)
                sub.unregisterObserver(this);
        }
    }

    // ...

    @Override
    public void update(){
        onContentChanged();
    }
    // ...
}

Podemos ver que nuestro Loader puede registrarse a más de un Subject. Esto nos da la posibilidad de que el Loader sea notificado no solo sobre cambios en los datos originales, sino cuando datos que afectan a la consulta (como filtros) cambian.

Con esto ya tenemos Loader completos y reutilizables. Si queréis ver o usar una implementación ya realizada de estos Loader, tenéis la ObserverLoaderLibrary. Cualquier feedback sobre este post o sobre la biblioteca (bugs, recomendaciones, aportes...) es agradecido.