Vistas de página en total

Mostrando entradas con la etiqueta apps. Mostrar todas las entradas
Mostrando entradas con la etiqueta apps. Mostrar todas las entradas

jueves, 8 de septiembre de 2016

Programación orientada a objetos en Lua

Lua, al igual que otros lenguajes no está orientado a objetos. Otros lenguajes como Java o C# tienen palabras y construcciones especiales para definir clases, crear objetos, herencia, etc. Pero Lua no.
Los conocedores de javascript con un nivel elevado saben que javascript tampoco está orientado a objetos, pero que se pueden realizar algunas "artimañas" para conseguir algo similar a la orientación a objetos. En Lua ocurre exactamente los mismo: utilizando las funciones, y las tablas podemos conseguir una funcionalidad de orientación a objetos muy similar a la de otros lenguajes.
Es importante notar que no existe una única forma de conseguir OOP en Lua, sino que cada desarrollador suele tener una arquitectura, al igual que ocurre en javascript. Yo mostraré la que yo suelo utilizar, que no tiene porqué ser la mejor forma, pero que es muy sencilla de entender y a mí me ha servido durante muchos años.

Clase base

En primer lugar, definiremos una clase base en el fichero BaseClass.lua:

function BaseClass( param1, param2 )
    local this = {}
    local privateAttr = param1
    this.publicAttr = param2
    local function privateMethod()
        return privateAttr
    end
    this.publicMethod = function()
        return privateMethod()
    end
    return this
end

En este ejemplo, hemos definido una función constructora, BaseClass(), de una clase que tiene dos atributos: privateAttr (que es privado) y publicAttr (que es público). También se han definido dos métodos: privateMethod (que es privado) y publicMethod (que es público).
En primer lugar, se crea una tabla (llamada this, aunque valdría cualquier otro nombre). Luego se crean atributos en la tabla this (que serán los atributos públicos) y también funciones en la tabla (que serán las funciones públicas). Los atributos y funciones que sean local se convertirán en atributos y funciones privados.
Por último retornamos la tabla this que será el objeto que acabamos de crear con los atributos y métodos públicos.
En este momento, lo normal es hacerse la pregunta: ¿Qué ocurre con las variables y funciones local? ¿Desaparecen al retornar? Pues no, esas variables y funciones siguen existiendo en el closure de las funciones que hemos metido dentro de la tabla this, y por tanto, las funciones públicas podrán seguir accediendo a ellas una vez retornado this.

Clase derivada

Ahora vamos a definir una clase que hereda de la clase base, en el fichero DerivedClass.lua :

require ("BaseClass")

function DerivedClass( param1, param2, param3 )
    local this = ClaseBase(param1, param2)
    this.attr1 = param1
    this.attr2 = param2
    local attr3 = param3
    local function pp()
        print( this.attr1, attr3, this.publicMethod() ) 
    end
    this.increment = function()
        this.attr1 = this.attr1 + 1
        this.attr2 = this.attr2 + 1
    end
    return this
end

En primer lugar tenemos que hacer require del módulo BaseClass para tener acceso a la función constructora de la clase base.
En este módulo definimos otra función constructora, DerivedClass(), similar a la de la clase base. Pero hay algo distinto. En la clase base creábamos una tabla this vacía, y ahora estamos llamando a la función constructora de la clase base para que nos retorne this. A partir de ese momento, vamos añadiendo nuevos atributos y métodos a this, tal y como hicimos antes.
Terminamos, al igual que antes, retornando la tabla this con los atributos de la clase base y los de la clase derivada.

Utilización

Por último, vamos a utilizar la clase DerivedClass en un fichero main.lua:

require ( "DerivedClass")

local obj = DerivedClass( 1, 2, 3 )
obj.increment()
print(obj.publicAttr)
print(obj.attr1, obj.attr2)

local obj2 = DerivedClass( 4, 5, 6 )
obj2.increment()
print(obj2.publicAttr)
print(obj2.attr1, obj2.attr2)

No es difícil entender este main.lua. Lo primero que tenemos que hacer es la sentencia require para llegar a la clase derivada (no hace falta el require de la clase base, porque eso ya está dentro del módulo de la clase derivada).
Luego creamos objetos de la clase derivada, llamando a la función constructora. Y por último accedemos a sus atributos y métodos públicos.

Otras opciones para OOP en Lua

Como ya he dicho, esta es la forma en que yo hago orientación a objetos en Lua, pero existen muchas otras alternativas. Debido a que Lua no es orientado a objetos, entonces tenemos que "simular" esta característica con construcciones propias del lenguaje. Esta que he descrito es mi forma de hacerlo y hasta ahora me ha funcionado muy bien. De OOP lo que más necesito es la encapsulación, métodos públicos y privados, y la herencia. Con mi método consigo todo lo que necesito de una forma sencilla y que funciona.
Sin embargo, como también he dicho existen otras alternativas. Quizás una de las más conocidas sea la que propone la web de Lua:


También en Corona labs tienen una propuesta:


Y por último, en la wiki de Lua hay otro tutorial también interesante:


Con alguna de estas opciones tendremos características más avanzadas de la orientación a objetos en Lua, pero ya digo que en mi opinión no las he necesitado nunca, y el problema es que utilizan características avanzadas de Lua que pueden complicar el software que desarrollamos.










martes, 6 de septiembre de 2016

Módulos

Cuando escribimos una aplicación con Corona SDK empezamos con un fichero llamado main.lua
Este fichero es el punto de entrada de nuestra aplicación y siempre será el punto de entrada por el que empieza la ejecución. Si quisiéramos podríamos escribir todo nuestro programa en un único fichero main.lua, lo cual es perfectamente posible, salvo que sería bastante difícil su mantenimiento si la aplicación tiene un tamaño moderado o grande. Otra limitación sería el propio editor o IDE, que probablemente tendría algunos problemas de rendimiento si main.lua tuviera muchas miles de líneas de código Lua. Desde luego, desde un punto de vista de ingeniería del software y de arquitectura, ésta no es la mejor idea (excepto para aplicaciones muy triviales o pruebas).

Los módulos Lua permiten que organicemos mejor nuestro programa. Un módulo es un fichero de código Lua que se incluye en main.lua mediante la sentencia require()
Por ejemplo, si tenemos un módulo en un fichero llamado utils.lua, en el main.lua tendremos que hacer:

    require( "utils" )

Obsérvese que no hay que añadir la extensión del fichero .lua

El contenido del fichero utils.lua podría ser:

    function add(a, b)
        return a + b
    end

Entonces, en main.lua, después de require(), podemos invocar a la función add().

En este ejemplo estamos declarando una función en el contexto global, lo cual no es una buena idea. Por eso, lo normal es que un módulo retorne una tabla con los datos y funciones. Por ejemplo:

    local t = {}

    t.add = function(a, b)
        return a + b
    end

    return t

De esta forma, en el main.lua tendremos que hacer lo siguiente:

    local utils = require( "utils" )
    print( utils.add( 5, 34 )

La sentencia require() devuelve el valor retornado por el fichero del módulo. En nuestro ejemplo, retorna una tabla que tiene una función.

Cualquier variable o función declarada en un modulo con local, queda en el ámbito local del módulo y no se puede acceder a ella desde otros sitios fuera del módulo.

Otro punto importante es que si ejecutamos la sentencia require() varias veces en nuestro programa, sólo se ejecutará el módulo la primera vez. Las siguientes veces simplemente detectan que ya se ha hecho require() de ese módulo y no lo vuelve a ejecutar, aunque sí que retorna el mismo valor que devolvió la primera vez.

Se podrían ejecutar todas las sentencias require() de todos los módulos en main.lua y asignar el resultado a variables globales, por ejemplo:

    utils = require( "utils" )
    constants = require( "constants" )

Las variables utils y constants son globales y por tanto podremos acceder a ellas desde cualquier punto de nuestro programa. Esta es una práctica razonable, pero se recomienda que cada módulo haga los require() que necesite y que asigne el resultado a variables locales:

    local utils = require( "utils" )
    local constants = require( "constants" )

En este caso, usaríamos en cada módulo otros módulos que se necesiten, y como no se ejecuta el módulo nada más que la primera vez, no hay problemas de rendimiento.

Por último, y para terminar el tema de los módulos Lua, quiero comentar que los ficheros de los módulos se pueden colocar en distintos subdirectorios (siempre debajo del directorio del proyecto donde debe estar obligatoriamente main.lua). Podríamos tener una estructura similar a la siguiente:


  • main.lua
    • utils
      • dateutils.lua
      • file.lua
    • scenes
      • splash_scene.lua
      • menu_scene.lua

Para acceder a estos módulos, se haría de la siguiente forma:

    require( "utils.dateutils" )

O sea, los subdirectorios se especifican con un punto y no hay que poner la extensión .lua






jueves, 1 de septiembre de 2016

Lua - Parte 5

Estructuras de control

Lua, como casi todos los lenguajes de programación, tiene una serie de estructuras de control que permiten iterar y hacer comprobaciones dentro del código:
  • if
  • for
  • while
  • repeat

IF

La sentencia if permite chequear una o más condiciones:

    if condición1 then
        sentencias1
    elseif condición2 then
        sentencias2
    elseif condición3 then
        sentencias3
    else
        sentencias4
    end

Las condiciones son expresiones cuyo resultado es un boolean, o sea, true o false. Si una expresión es nil se considera false, cualquier otro valor, se considera true. Ejemplos de condiciones:

    3 > 5    -- false
    5 > 1    -- true
    a        -- variable no existente -> nil -> false
    6        -- true
    0        -- true
    "hola"   -- true
    ""       -- true
    nil      -- false

Las partes elseif y else de la sentencia if son opcionales.

Lua no tiene la sentencia switch típica de otros lenguajes de programación, pero se puede sustituir muy fácilmente con la sentencia if.

FOR

Forma general de la sentencia for:

    for variable = start, end, step do
        sentencias
    end

La "variable" es el índice que va cambiando y que se puede utilizar dentro de las sentencias del bucle for. "start" es el valor de inicio y "end" es el valor final (incluidos ambos). "step" es opcional y permite pasar desde start hasta en end en pasos distintos de uno.

Ejemplo:

    for i = 3, 10 do
        print(i)
    end

Otro ejemplo, contando hacia atrás:

    for i = 100, 50, -1 do
        print(i)
    end

WHILE

La sentencia while permite ejecutar un bloque de sentencias mientras se cumpla una condición:

    while condición do
        sentencias
    end

REPEAT

La sentencia repeat permite ejecutar un bloque de sentencias mientras hasta que se cumpla una condición:

    repeat
        sentencias
    until condición

El bloque repeat se ejecuta siempre al menos una vez.

BREAK

La sentencia break permite salirse un bucle. Por ejemplo:

    for i = 100, 50, -1 do
        print(i)
        if i > 80 then
            break
        end
    end

Hay que tener en cuenta que Lua no tiene la sentencia continue, típica en otros lenguajes de programación para ir al principio del bucle.

Lua - Parte 4

Funciones

Todos hemos usado funciones de una u otra forma en muchos lenguajes de programación. En Lua también tenemos funciones y la idea es muy similar a la de los demás lenguajes. Sin embargo, hay algunos matices que es interesante considerar.

En Lua una función se puede definir de varias formas. La más sencilla y quizás la más utilizada es:

    function suma(a, b)
        return a + b
    end

De esta forma, estamos definiendo una función denominada "suma" de ámbito global (o sea, accesible desde cualquier sitio), que acepta dos argumentos, a y b, suma los dos argumentos y retorna el resultado de la suma. Creo que todo esto está bastante claro para todos aquellos que alguna vez han programado en algún lenguaje, no parece haber nada nuevo. Pero quiero destacar el hecho de que la función tiene ámbito global, es decir, que podemos invocarla desde cualquier punto de nuestro programa. Esto ocurre porque no hemos utilizado la palabra clave "local". Si hubiéramos querido definir una función con ámbito local:

    local function suma(a, b)
        return a + b
    end

En este caso, sólo podríamos invocarla desde el módulo en el que estamos (ya hablaremos de módulos en una entrada posterior, de momento adelanto que un módulo es un fichero Lua con funciones y otros elementos de código).

Otro tema interesante a destacar es que los parámetros, a y b, no declaran un tipo. Es decir, podemos invocar a la función "suma" con dos valores o varialbes cualquiera, de cualquier tipo. Lo normal será invocarla con dos números, pero nada nos impide utilizar otros tipos (eso sí, probablemente tendremos un error de ejecución, por ejemplo al invocar la función "suma" con dos strings).

Destacar también que la función no declara el tipo retornado. Retornará un valor numérico porque la sentencia "return" devuelve la suma de sus dos parámetros, pero realmente el lenguaje no obliga a retornar ningún tipo concreto. De hecho (reconozco que esto es algo enrevesado), la función podría retornar en un caso un número y en otros casos un string.

Por último no quiero olvidar que una función podría no retornar ningún valor. Para ello podemos utilizar la palabra clave "return" simplemente o bien llegar al final de la función sin retornar ningún valor.

Funciones dentro de una tabla

Como vimos al hablar de las tablas, una función puede ser un miembro de una tabla:

    local table = {
        add = suma,
    }

O bien:

    local table = {
        add = function(a, b) return a + b end,
    }

Retorno de múltiples resultados

Una función puede retornar más de un resultado:

    function statistics()
        return a, b, c
    end

Estos múltiples resultados se recogen de la siguiente forma:

    local x, y, z = statistics()

Número variable de argumentos

Una función no sólo puede retornar múltiples resultados, si no que también puede aceptar un número variable de argumentos:

    function guarda( ... )
        print( arg[1], arg[2], arg[3] )
    end

Los argumentos se recogen en la lista predefinida arg. El operador # nos permite conocer el número de argumentos que se han pasado a la función:

    function guarda( ... )
        for i = 1, #arg do   -- más adelante veremos los bucles
            print( arg[i] )
        end
    end

También podemos tener un número mínimo de argumentos a una función y los demás variables:

    function guarda( a, b, ... )
        print( a, b, arg[1], arg[2], arg[3] )
    end

Lo importante es que los tres puntos (argumentos variables) siempre tienen que ir al final.

Más sobre tablas y funciones

Ya he dicho varias veces que una función puede ser un miembro de una tabla. Esto es muy importante y se utiliza mucho en Lua. Una de las ventajas de esta construcción es que de esta forma no se crean funciones globales. Podríamos resolver también este problema con la palabra "local", pero en este caso el acceso a la función sería únicamente local, sin embargo, las funciones dentro de una tabla son accesibles desde cualquier punto del programa, pero de una forma que podría ser similar a los namespaces o packages de otros lenguajes (como C# o Java). Por ejemplo:

    myobject = {
        add = function(a, b) return a + b end,
    }

Para invocar a esta función haríamos:

    myobject.add(3, 5)

De esta forma estamos añadiendo un "namespace" a la función. Y no sobreescribiría a esta otra función que tiene el mismo nombre, pero está en otro "namespace":

    utils = {
        add = function(a, b) return a .. b end,
    }

Otra forma equivalente de añadir la función a la tabla sería:

    myobject = { }
    function myobject.add(a, b) return a + b end

Tablas, funciones y objetos

Lo que hemos visto sobre tablas y funciones hará pensar a algún lector en objetos y en programación orientada a objetos. Sin embargo, la forma que hemos visto de meter una función en una tabla no es realmente un objeto. El motivo principal es que la función no es "consciente" de en qué objeto (tabla) está, de hecho, no tiene acceso a otros datos de la tabla. Veámoslo con un ejemplo:

    myobject = {
        dato = 3,
    }
    function myobject.calcula(a) return dato + a end

En este caso tenemos una tabla con dos miembros: un número "dato" y una función "calcula".
Si intentamos invocar a la función "calcula":

    myobject.calcula(5)

Obtendremos un error similar al siguiente:

    attempt to perform arithmetic on global 'dato' (a nil value)

Esto se debe a que no existe una variable global llamada "dato" y por tanto su valor es nil, lo cual provoca un error al intentar sumarlo con el argumento "a". La variable "dato" de la tabla no es accesible a la función.

Podríamos resolverlo de la siguiente forma:

    myobject = {
        dato = 3,
    }
    function myobject.calcula(a) return myobject.dato + a end

Pero tenemos que reconocer que no es una forma muy elegante de acceder a la variable dato (ya que dependemos del nombre que demos a la tabla, en este caso, "myobject").

Otra solución, tampoco muy elegante por cierto, sería pasar como argumento a la función la propia tabla (reconozco que esto es todavía peor):

    myobject = {
        dato = 3,
    }
    function myobject.calcula(this, a) return this.dato + a end

    myobject.calcula(myobject, 5)

Aunque esta solución es incluso más horrible que la anterior, me permite introducir la solución que propone Lua para estos casos.

    myobject = {
        dato = 3,
    }
    function myobject:calcula(a) return self.dato + a end

Y para invocar a esta función:

    myobject:calcula(6)

Es una sintaxis un poco extraña, pero fácil de comprender. En primer lugar, para añadir la función a la tabla utilizamos dos puntos, en lugar de un punto como hemos hecho hasta ahora. También utilizamos dos puntos para invocar a la función. Los dos puntos indican a Lua que tenemos que necesitamos un parámetro intrínseco, denominado "self", y que es la propia tabla donde está la función. Este parámetro lo pone Lua (sin que hagamos nada y de forma similar al parámetro arg que Lua introduce en las funciones con número variable de argumentos).

En una entrada posterior trataré el tema de la programación orientada a objetos en Lua, donde veremos cómo podemos tener objetos reales en nuestros programas, a pesar de que Lua no es realmente un lenguaje orientado a objetos. Tendremos que hacer unas construcciones especiales, siguiendo un determinado patrón para conseguirlo.

Closure de una función

El closure de una función es un término que suele ser difícil de comprender de partida, salvo que se haya utilizado previamente en otros lenguajes como javascript.
En pocas palabras, el closure de una función son las variables y funciones que existen en su scope. Cuando una función está dentro de otra función, la función interna tiene acceso a todas las variables de la función externa. A esto se le conoce como lexical scope.
Estos dos conceptos que parecen a primera vista muy sencillos, se complican un poco, como ahora veremos.

    local function external(paramExt)        local varExt = paramExt + 1        local internal = function(paramInt)            local varInt = paramInt + varExt + paramExt        end        return internal    end

    local f = external(5)
    print( f(4) )

Este es un ejemplo bastante complejo, si no se tiene costumbre de manejar funciones como objetos. Voy a tratar de explicarlo despacio.
En primer lugar, tenemos una función llamada external que devuelve otra función llamada internal. Hasta aquí no hay nada extraño. Sin embargo, la función internal utiliza variables e incluso parámetros (varExt y paramExt) que pertenecen a la función external. Y nos podemos preguntar: ¿Cuánto valen esa variable y ese parámetro cuando mucho más abajo llamamos a la función f? Pues bien, esa variable y ese parámetro tienen los valores que tenían cuando llamamos a la función external. Es decir, paramExt vale 5 y varExt vale 6. Por tanto, la función f, que devuelve la suma de esos dos objetos más el parámetro paramInt, devuelve 5 + 6 + 4, o sea, 15.
La variable varExt y el parámetro paramExt son el closure de la función internal, y cuando se le invoque más adelante, tendrá acceso a esos valores de su closure. Cada vez que invocamos a la función external y recibimos una función como resultado, esa función tendrá asociado el closure correspondiente.



lunes, 29 de agosto de 2016

Lua - Parte 3

Tablas

Las tablas son la estructura de datos más potente que tiene Lua. Pero al mismo tiempo es quizás una de las más difíciles de entender (los programadores javascript tienen ventaja en este caso, y entenderán las tablas Lua muy rápidamente).

¿Qué son tablas Lua?

Muy brevemente: las tablas Lua son objetos. O mejor dicho, son arrays asociativos, o sea, arrays donde se puede acceder a los elementos por un identificador (el cual, como veremos, puede ser numérico o no númerico). Esta primera explicación de las tablas es perfecta para programadores javascript, los cuales ya habrán entendido un porcentaje muy alto de lo que son las tablas Lua.
Para programadores que vengan de otros lenguajes, las tablas son muy parecidas a los diccionarios o mapas.
Como vamos a ver, con las tablas Lua podemos implementar arrays (indexados por índice numérico), mapas (accedidos con identificador no númerico) e incluso objetos.

Arrays

Los arrays en Lua son tablas:

    local array = { "a", "b", "c" }

Se puede acceder a los elementos con la notación típica de corchetes:

    array[2] = "b"
    var v = array[3]

Es importante indicar que los arrays en Lua empiezan con el índice 1 (no 0 como ocurre en muchos otros lenguajes de programación). Así, en la tabla anterior, el elemento 1 vale "a", el elemento 2 vale "b" y el elemento 3 vale "c".
En un array podemos guardar elementos de distintos tipos:

    local otroArray = { 1, "a", 45, "Toni" }

Se puede obtener la longitud de un array con el operador # como anticipé en la entrada anterior:

    local len = #otroArray

Por último, podemos crear un array vacío y añadirle luego los elementos:

    local array = { }
    array[1] = "a"
    array[2] = "b"
    array[3] = "c"

Podríamos incluso dejar "huecos" y entonces los elementos sin rellenar sería nil:

    array[5] = "e"
    print(array[4])     -- nil

Mapas o diccionarios

Como he dicho, con las tablas Lua también podemos implementar mapas o diccionarios:

    local socio = { nombre = "Luis", edad = 23, dni = "34756453H" }

En este caso, hemos dado un nombre a cada elemento de la tabla. Este nombre se conoce como identificador y nos permite acceder a los distintos elementos:

    socio["nombre"] = "Luis"
    var age = socio["edad"]

Como se ve, para acceder a los elementos tenemos que entrecomillar el identificador. Si no ponemos las comillas, Lua pensaría que edad es una variable. Por ejemplo:

    print(socio[edad])

La variable edad no existe, y por tanto, el resultado en la consola sería nil.
Lua también permite otra notación para acceder a los elementos por su identificador:

    print(socio.edad);
    socio.nombre = "Ramón"

De esta forma, no se ponen comillas al identificador.
Cuando queremos acceder a un elemento de un mapa de forma indirecta, entonces tendremos que utilizar la notación de corchetes de forma obligatoria:

    var element = "edad"
    print(socio[element])

En este caso, se accede al elemento socio["edad"] o bien socio.edad de forma indirecta.

Al igual que ocurre con los arrays, podemos crear un mapa vacío y añadirle elementos después:

    var socio = { }
    socio.nombre = "Luis"
    socio["edad"] = 34

Incluso, un caso más enrevesado, podemos tener una estructura de datos que sea un mapa y que tenga elementos de array (yo no recomiendo esta práctica, aunque Lua lo permite):

    var socio = { }
    socio.nombre = "Luis"
    socio[3] = "XXX"

pero en este caso, sólo podremos acceder al elemento 3 con la notación de corchetes:

    print(socio[3])      -- correcto
    print(socio.3)        -- error

El operador # para obtener la longitud de un array sólo funciona para arrays "puros", es decir, no funcionará en el caso de mapas.

Anidación

Los elementos de un tabla (o de un array) pueden ser de cualquier tipo, y por tanto, un elemento podría ser un array o tabla, permitiendo así estructuras anidadas:

    local t = { }
    t.nombre = "Luis"
    t.direccion = { }
    t.direccion.calle = "Gran Vía"
    t.direccion.poblacion = "Madrid"

Funciones como elementos de una tabla 

Aunque todavía no hemos visto en profundidad las funciones y sus características, es importante reseñar que una función puede ser un elemento de una tabla. Veamos varios ejemplos:

    local t =  { }

    t.calculo = function(a, b)  return a+b end

    function suma(a,b)
        return a + b
    end
    t.calculo = suma

En el primer caso asignamos una función inline a un elemento de una tabla, y en el segundo caso, asignamos una función existente a un elemento de una tabla.






















martes, 23 de agosto de 2016

Lua - Parte 1

El lenguaje Lua

Corona SDK proporciona los APIs necesarios para desarrollar aplicaciones multiplataforma para dispositivos móviles. El lenguaje de programación para utilizar esos APIs es Lua. Por eso, necesitamos un conocimiento lo más profundo posible del lenguaje Lua si queremos desarrollar con soltura aplicaciones. Poer no hay que asustarse, en primer lugar, la sintaxis de Lua, además de ser muy similar a la de otros lenguajes (Javascript por ejemplo), es muy sencilla.

Según la web de Lua: Lua es un lenguaje de programación potente, eficiente y ligero, es un lenguaje de script embebido. Soporta programación procedural, programación orientada a objetos (aunque no de forma nativa como Java o C#), programación funcional y descripción de datos.

Lo más relevante es que Lua no es un lenguaje para escribir programas stand-alone, como lo son Java, C++, C# etc. Un programa Lua se tiene que integrar (embeber) en un programa contenedor (host). El programa host invoca al programa Lua y puede acceder a sus datos. También es posible invocar a rutinas del programa host desde Lua. En realidad, todo esto es cultura sobre Lua, porque en la práctica nosotros vamos a desarrollar 100% con Lua, y es Corona SDK quien se encarga de realizar el trabajo sucio de integrar nuestro programa Lua con los APIs para aplicaciones móviles. Corona es el programa host. Nosotros usaremos el lenguaje Lua para implementar la lógica e invocaremos los APIs de Corona para crear nuestras aplicaciones. Nada más.

Otra característica importante de Lua es que tiene un recolector de basura integrado, y por tanto, no es necesario liberar la memoria que vamos creando, se libera automáticamente cuando ya no se usa. Aunque es recomendable desreferenciar las variables que ya no se van a usar (ya veremos esto más adelante) para facilitar la tarea al recolector de basura.

Sintaxis de Lua

Lua es uno de los lenguajes de programación inspirado "más o menos" en C (como otros muchos, Java, C++, C#, Javascript, etc). Esto significa, en primer lugar, que Lua es sensible a mayúsculas y minúsculas.
Las sentencias pueden terminar opcionalmente en punto y coma.
Los nombres de variables, funciones o cualquier cosa que pueda tener nombre, son una combinación de letras, dígitos y subrayados, de cualquier longitud, Pero no pueden empezar con un dígito. Se deben evitar nombres que empiecen con subrayado, porque aunque son legales en Lua, sin embargo, por convención, Lua utiliza nombres que comienzan por subrayado para sus variables internas.
Los bloques son una serie de sentencias que se ejecutan una detrás de otra y comparten un contexto de ejecución. El bloque más común es la función:

function calculate()
    -- bloque de sentencias
end

Todas las sentencias dentro de la función son un bloque.
Otros bloques Lua son las sentencias if y los bucles.
También es posible definir bloques anónimos:

do
    -- sentencias
end

Palabras clave

Esta es la (breve) lista de palabras clave del lenguaje Lua:
  • and
  • break
  • do
  • else
  • elseif
  • end
  • false
  • for
  • function
  • goto
  • if
  • in
  • local
  • nil
  • not
  • or
  • repeat
  • return
  • then
  • true
  • until
  • while

Operadores

Los operadores en Lua son similares a los de la mayoría de los lenguajes de programación. Sin embargo, hay algunos operadores que son distintos y merece la pena mencionarlos:

  • ∼= operador distinto
  • .. concatenación de strings
  • . . . número variable de argumentos en una función

Comentarios

En Lua, como en muchos lenguajes de programación, existen dos tipos de comentarios.
Los comentarios de línea:

-- esto es un comentario hasta el final de la línea

Los comentarios multilínea:

--[[
esto es un comentario que puede abarcar varias líneas
]]--

Trazas en consola

Antes de terminar esta primera parte dedicada al lenguaje Lua, me gustaría indicar, que como en casi todos los lenguajes y entornos de programación, disponemos de una consola para sacar mensajes, trazas, etc.
La rutina que tiene Lua para sacar un mensaje en la consola es print(). Es una función global y por tanto podemos acceder a ella desde cualquier punto de nuestro programa:

    print("Hola mundo")

Pero, ¿dónde salen los mensajes? Cuando abrimos el simulador de Corona en Windows, se abren dos ventanas y una de ellas es la consola que tiene un aspecto similar a esto:
Aquí podemos ver los mensajes de print()

La función print() puede recibir múltiples argumentos separados por comas:

    print("La edad es", 27, ", y el nombre es",nombre)












lunes, 22 de agosto de 2016

Hola mundo de Corona

Una vez tenemos instalado Corona SDK, vamos a realizar un sencillo ejemplo para comprobar que todo ha ido bien y además para ver lo sencillo que es realizar una pequeña app para móvil. La desplegaremos en Android, de momento, porque el despliegue en iOS es algo más complejo y además, requiere tener una licencia de desarrollador. Esto no quiere decir que la app que vamos a crear no pueda funcionar en iOS. La app funciona perfectamente en iOS, pero no vamos a hacer el despliegue hasta más adelante.

En primer lugar, tenemos que crear un directorio en cualquier lugar de nuestro disco duro. En este directorio situaremos todos los ficheros de la aplicación.
Seguidamente, abrimos nuestro editor de texto y escribimos el siguiente programa Lua:




Aunque en este momento quizás no entendamos todo el contenido de nuestro programa, es importante teclearlo y ejecutarlo en nuestro ordenador. Al final de este capítulo daré una pequeña explicación de lo que hace (sin entrar en detalles), aunque un programador experimentado no tendrá muchas dificultades en entender qué estamos haciendo.

Guardamos el fichero con el nombre main.lua en el directorio que hemos creado. No dar otro nombre al fichero, ya que el punto de entrada de nuestra aplicación tiene que ser obligatoriamente main.lua

El siguiente paso es ejecutar nuestra aplicación en el simulador de Corona. Para ello, abrimos Corona Simulator. Pulsamos en Open, navegamos hasta el directorio donde tenemos nuestro main.lua y seleccionamos este fichero. Si todo ha ido bien se abrirá el simulador y tendremos nuestra aplicación ejecutándose. El mecanismo para arrancar el simulador es el mismo tanto en Windows como en MacOS.



Podemos cambiar el dispositivo que muestra el simulador desde el menú View > View As
Esto sólo cambia el tamaño de la pantalla del simulador, pero es muy útil para comprobar la ejecución de nuestras aplicaciones en distintos tamaños de pantalla. Por ejemplo, podemos poner iPhone 6 plus:



Antes de hacer el despliegue en Android, voy a tratar de dar una ligera explicación de lo que hace el programa.
En las líneas 1-5 creamos un rectángulo centrado en la pantalla con las dimensiones de la pantalla del dispositivo, o sea, cubre toda la pantalla. En la línea 6 ponemos color blanco a este rectángulo.
En las líneas 8-13 creamos un texto "Hola mundo..." centrado en la pantalla con un tamaño de fuente de 48 puntos. En la línea 14 damos un color rojo oscuro a este texto.
En las líneas 16-21 creamos otro texto "CORONA" un poco por debajo del texto anterior, con una fuente de 96 puntos. En la línea 22 damos un color rojo intenso a este texto. En la línea 23 hacemos que el texto parpadee continuamente.
Si el lector lo desea, puede aventurarse a hacer algunos cambios en el programa, por ejemplo, cambiar colores, posiciones, e incluso crear nuevos objetos.

Por último, vamos a la parte más deseada. Vamos a ejecutar nuestra aplicación en un móvil Android.
Si no tenemos el JDK de Java tendremos que instalarlo. Es necesario instalar el JDK, no vale el JRE para compilar la versión Android.

En el simulador de Corona, vamos al menú File > Build > Android
Nos aparecerá una pantalla donde tendremos que rellenar los siguientes datos:


  • Application name: Nombre de la aplicación. Por defecto es el nombre del directorio de nuestra aplicación, pero podemos cambiarlo.
  • Versión code: Versión de la aplicación. Es un número que tendremos que ir incrementando cuando generemos nuevas versiones. En nuestro caso, lo podemos dejar en 1. Este número será importante cuando despleguemos una aplicación en la tienda de aplicaciones de Android.
  • Version name: Versión de la aplicación, pero esta vez una cadena de caracteres, donde podemos poner algo como 1.0.0
  • Package: Identificador único de la aplicación. Aquí tenemos que poner un identificador con el formato de package java, o sea, un dominio en formato inverso seguido por un nombre de aplicación. Por ejemplo: com.joseanquiles.apps.HolaMundo
  • Target App Store: Tienda de aplicaciones donde se va a desplegar la aplicación. En nuestro caso, vamos a instalar directamente la aplicación en un móvil Android, así que seleccionaremos Google Play.
  • Keystore: Clave para firmar la aplicación. De momento utilizaremos una clave que nos genera Corona. Esta clave es suficiente de momento, Pero tendremos que generar una clave propia (ya veremos cómo) para publicar nuestra aplicación en Google Play store.
  • Key Alias: alias para la clave. De momento dejamos androiddebugkey
  • Save to folder: Directorio donde se generará el fichero .apk de la aplicación Android. Este fichero .apk es el que instalaremos en el móvil.
  • After build: Qué hacer al terminar la compilación. Seleccionar Do Nothing, para que no haga nada.

Pulsamos el botón Build para que empiece la compilación. Necesitamos conexión a internet para compilar, ya que la compilación se hace en los servidores de Corona. Esto, que pudiera parecer una molestia al principio, realmente es algo muy cómodo, sobre todo en el caso de la compilación para Android, ya que no es necesario instalar el SDK de Android en nuestro PC (algo que no es obvio habitualmente).
Al cabo de unos segundos, tendremos nuestro fichero .apk generado en el directorio que hemos indicado en las opciones de Build. Este fichero es el que tenemos que instalar en el móvil Android.

Pero antes de eso, debemos asegurarnos que en nuestro móvil hemos activado la opción de instalar aplicaciones de fuentes desconocidas. ¿Qué significa esto? Muy sencillo. Por defecto, Android sólo permite instalar aplicaciones desde la Google Play Store, por seguridad. Pero permite, si el usuario lo desea, instalar aplicaciones que provengan desde otras fuentes, por ejemplo, un fichero .apk que tengamos en nuestro móvil. Este es nuestro caso, así que tendremos que activar esta opción antes de nada. El problema es que cada versión de Android, e incluso cada fabricante de móviles, mete esta opción en un sitio distinto dentro de la aplicación Ajustes, por eso, no puedo dar el detalle de cómo hacerlo, porque no sería válido para todos los modelos. Lo mejor es hacer una búsqueda en Google indicando nuestra versión de Android. Lo normal es que esté en la aplicación de Ajustes, en la sección de Seguridad y buscar Orígenes desconocidos. Aparecerá una advertencia cuando intentamos activar esta opción.

Ahora tenemos que llevar el fichero .apk que hemos generado en nuestro PC o Mac al móvil. Para mí, la forma más sencilla es enviarlo adjunto por mail, aunque también tenemos la opción de dejarlo por ejemplo en DropBox y bajarlo desde el móvil.

Cuando pulsamos sobre el fichero .apk en el móvil (sea el adjunto del mail, sea en dropbox, o por cualquier otro medio), nos preguntará si queremos instalarlo y tras unos cuantos segundos tendremos nuestra aplicación instalada en el móvil. El icono de la aplicación lo pone Corona, pero ya veremos cómo poner un icono personalizado.

Llegó el momento, sólo tenemos que pulsar sobre el icono de nuestra aplicación y tendremos nuestra primera aplicación corriendo sobre el móvil.

Vuelvo a recordar que ahora no vamos a hacer el despliegue en iOS, lo haremos más adelante, pero la idea es similar al despliegue en Android. Lo importante es tener claro que el mismo main.lua, sin NINGUNA modificación se puede ejecutar en un iPhone o iPad.












jueves, 14 de julio de 2016

Quiero hacer apps y juegos para móviles

Este blog va a estar dedicado a la programación de aplicaciones y juegos para móviles utilizando Corona SDK.

Desde que aparecieron los primeros smartphones, mucha gente ha tenido ideas para desarrollar un juego o una aplicación para estos dispositivos. Pero hay varios problemas a los que hay que enfrentarse, sobre todo si no se tiene mucha experiencia en este mundo del desarrollo para móviles.

El primer problema al que nos enfrentamos es que no existe una única plataforma o sistema operativo para móviles. Nos encontramos con al menos, dos grandes plataformas: iOS y Android. También podríamos incluir Windows Phone, pero a día de hoy, no está tan expandido como las otras dos.

Las aplicaciones para Android se desarrollan en Java y se utiliza Android Studio como IDE. Las aplicaciones para iOS se desarrollan en Swift (o bien en el antiguo Objective-C) y se utiliza XCode como IDE. Ambos entornos son completamente diferentes, tienen APIs muy distintos y requieren de una especialización importante para desarrollar aplicaciones o juegos de una complejidad media.

Podríamos pensar que lo mejor es centrarse en una de las plataformas, Android o iOS, especializarse en ella y desarrollar nuestra idea sólo para esa plataforma. Si con el paso del tiempo, empieza a tener éxito, habría que plantearse desarrollarla de nuevo para la otra plataforma. Esto tiene algunos inconvenientes: Nunca sabemos si nuestra idea va a tener éxito en una plataforma y quizás empecemos por la opción errónea, además, quizás el éxito está asociado al momento actual. Os voy a contar mi experiencia. Hace unos cuatro años, desarrollé un juego para Android (Retro Kong Jungle), tuvo un éxito inicial inesperado para mí. En pocos meses llegó a las 50000 descargas. Entonces me planteé desarrollarla de nuevo para iOS, pero la falta de tiempo para aprender el nuevo entorno y la inexperiencia, hicieron que esto se retrasara hasta 3 años. Cuando por fin me decidí a hacer el desarrollo del juego para iOS, me llevé la gran sorpresa que prácticamente no tiene descargas (Retro Kong Jungle). Conclusión, las cosas hay que hacerlas en su momento, no dejarlas para después. Si cuando desarrollé el juego inicialmente lo hubiera hecho para las dos plataformas, quizás la historia hubiera sido muy distina. Nunca lo sabré.

Una solución por la que optan algunos desarrolladores es la aplicación Web. Se trata desarrollar la aplicación y publicarla en un servidor web, pero haciéndola responsiva, es decir, adaptada a dispositivos móviles. Ésta no es una mala solución para algunas aplicaciones (por supuesto no para todas), y desde luego, no es válida en absoluto para juegos. Creo que este asunto, no merece más explicación.

Por suerte, existen herramientas multiplataforma, las cuales con una única base de código fuente, permiten el despliegue en múltiples plataformas móviles.

Pero hay muchas diferencias entre todas estas plataformas, y cada una tiene sus ventajas e inconvenientes si comparamos con implementar un desarrollo distinto para cada plataforma.

El primer tipo de herramientas está basado en HTML-Javascript-CSS. No se trata de aplicaciones web, si no de herramientas en las que se programa en estos lenguajes y generan una especie de navegador web embebido en la aplicación. A diferencia de la aplicación web de toda la vida, estas aplicaciones no se ejecutan online, si no que se ejecutan localmente. El código javascript que controla la aplicación lleva el flujo de toda la aplicación en el propio móvil, sin necesidad de conectarse a un servidor web para cargar las páginas. El ejemplo más típico de estas herramientas es PhoneGap. La gran ventaja de estas herramientas es que se utilizan lenguajes muy conocidos para los desarrolladores. El problema es que desde javascript no se puede acceder a todos los recursos nativos de cada plataforma, además no es una solución para desarrollar juegos.

Otro tipo de herramientas, como Xamarin, permite desarrollar en un único lenguaje de programación y ofrece unas librerías en cada plataforma de forma que el API sea común. También ofrecen otros APIs específicos de cada plataforma, y por lo tanto, casi siempre habrá que desarrollar un porcentaje, más o menos elevado de la aplicación, específicamente para cada plataforma. La gran ventaja es la utilización de un lenguaje de programación y de APIs comunes. Pero obliga a desarrollar partes de la aplicación para cada plataforma.

Por último, tenemos las herramientas que traducen el código fuente común a cada plataforma. Este es el caso de Corona SDK. En el caso de Corona, no se utilizan widgets nativos para la interfaz de usuario, sino que dibujan en la pantalla utilizando OpenGL. De esta forma, el resultado en cada plataforma es prácticamente igual. Lo cual es una ventaja, pero también se podría considerar un inconveniente, ya que los usuarios de cada plataforma están acostumbrados a utilizar ciertos widgets. Por ejemplo, una aplicación Corona que ponga un botón, tendrá el mismo aspecto tanto en Android como en iOS. La gran ventaja es que, además de utilizar un lenguaje común, se pueden desarrollar todo tipo de aplicaciones multiplataforma rápidamente, así como juegos.

Este blog está dedicado a Corona SDK, e iremos viendo poco a poco sus características. Haremos ejemplos desde lo más sencillo hasta aplicaciones completas. Aprenderemos el lenguaje de programación que utiliza. Y también, veremos algunas herramientas útiles que utilizaremos constantemente en el desarrollo para móviles: editores de texto, bitmaps, vectorial, audio, etc.

Espero que este blog os ayude a empezar en el mundo de la programación móvil de una forma sencilla y sobre todo, divertida. Yo creo que el desarrollo tiene que ser algo divertido. El programador que no se divierte programando, quizás debería reflexionar un poco sobre su trabajo, porque la programación es algo muy divertido, y si además, hacemos videojuegos, mucho más.

Por último, sólo una advertencia. Aunque voy a explicar el lenguaje de programación de Corona SDK, doy por supuesto que tenéis unos conocimientos mínimos de programación. No voy a explicar lo que es un if o un bucle. No hacer falta ser expertos programadores, pero sí hace falta cierta base mínima.