Llegamos a la quinta parte del capitulo 6 en el que seguimos desarrollando el menú de nuestro juego, y ya queda poco para terminarlo. Realmente el capitulo 6 es tan largo debido a que hemos ido introduciendo conceptos importantes sobre la marcha que quizás nos han desviado de la programación en si, en este capitulo hablamos de las variables locales y su ámbito de visibilidad.

Como habréis comprobado, el proceso de diseño descendente sigue las pautas que hemos seguido hasta el momento a la hora de razonar los datos y elementos que necesitaríamos para programar así como la metodología y las soluciones que hemos dado a los problemas que se han ido presentando.

A estas alturas del curso y después del ejemplo anterior, muchos os habréis dado cuenta de que una función es un programa en si mismo (sucesión de instrucciones y acciones con el propósito de resolver un problema), pero se podría decir que por si solos no podrían existir o serían de poca utilidad (de ahí el término subprograma). En general lo que queremos es dar forma de instrucción a lo que es un programa o conjunto de tareas, que necesitamos ejecutar en un determinado orden para alcanzar un objetivo, ya sea hacer un suma de vectores o comprobar si cierta estructura compleja cumple cierta condición.

Volviendo a nuestro juego, nos quedamos en la conclusión de que necesitabamos una función para alternar la selección entre los elementos de nuestro menú. Ahora que ya sabemos como programarla, manos a la obra.

A esta función la llamaremos AlternarSeleccion, y su trabajo consistirá en dados 3 valores booleanos que representen la información de los botones pulsados (2 de la pulsación actual y 2 de la pulsacion anterior), una diferencia y una variable numérica, modificará el valor de dicha variable hacia arriba o hacia abajo la diferencia según se pulse un botón o otro. Es decir, si se pulsa la tecla del primer argumento se incrementará el valor en la diferencia, y si se pulsa la del segundo, se decrementará la diferencia.

El orden de los parámetros es muy importante, ya que las declaración y la llamada coinciden en nombre, en orden y tipo de parámetros. Así si un parámetro A de tipo número está el primero, en la llamada el primer valor que pasemos como argumento se asignará a A. Por llamada entendemos el código necesario para ejecutar una función pasando una serie de parámetros definidos que llamaremos argumentos.

Por tanto para llamar a una función deberemos introducir la firma substituyendo los parametros por valores concretos del tipo que corresponda en el orden marcado. En el ejemplo anterior se llama a la función SumaVector, por tanto no perderemos tiempo en crear otro programa y seguiremos con la programación de la función que necesitamos para nuestro juego.

A la función de alternancia de opciones, valores, en función de dos botones que realicen funciones contrarias de adición y disminución, la denominaremos AlternarOp. Por tanto la firma de la función será la siguiente:

function Alternar(B1, B2, OldB1, OldB2, Elemento, Diferencia)

Recordad que B1, B2 son el valor de pulsación de los botones, b1 para el que sube, b2 para el que baja, y OldB1, OldB2 los valores de pulsación del ciclo anterior (para evitar que el desfase entre los botones que ya comentamos y resolvimos). Elemento es el valor inicial de la variable que contiene la información de la opción, y la diferencia lo que se incrementará a cada pulsación (2, 3 … N opciones).

A continuación os dejo el código de la función con las explicaciones en comentarios. Pero no tiene ninguna dificultad si has seguido hasta ahora el contenido del curso, ya que solo se hace un pulsación de botones con un incremento sobre una variable que será el valor de retorno de una función:

--Funcion para alternar el estado en funcion de las pulsacones
function Alternar(B1, B2, OldB1, OldB2, Elemento, Diferencia)
if B1 and OldB1 ~= B1 then-- Si se pulsa el botón 1 y no hay repeticion
Elemento = Elemento+Diferencia--aumentamos la diferencia
elseif B2 and OldB2 ~= B2 then-- Si no se pulsa el botón 1 y el botón 2 es pulsado y no --hay repeticion
Elemento = Elemento-Diferencia--Disminuimos la diferencia
end
return Elemento--Devolvemos el valor de Elemento tras la funcion
end

Antes de pasar a implementar esta función el código de nuestro juego, es conveniente remarcar un aspecto que hemos comentado de pasada dejandolo para más adelante, y puesto que ya es más adelante, hablemos de variables locales.

Pese a que no es un concepto muy difícil de asimilar, he preferido dejar las variables locales hasta este momento porque una vez habituados a las funciones, su comprensión es más sencilla. Hemos comentado que “Lua tampoco nos permitirá modificar el valor de las variables introducidas como parámetros, sino que se realizará una copia a una variable local de ámbito de visibilidad la función”, lo que significa que esas variables “modelo” que pueden recibir cualquier valor del tipo especificado para trabajar, y que conocemos como parámetros, solo pueden ser accedidas dentro de la función: desde la firma hasta el end (visualmente).

Por tanto, podemos afirmar que las funciones son un ámbito local para los parámetros. Si fuera de nuestra función alternar intentásemos acceder a la variable parámetro Elemento, resultaría que esta no existe, es decir que sólo existe dentro de la función y mientras esta se ejecuta. Puesto que no existía antes de que la función fuese ejecutada (su valor era nil) al salir de ella el valor tampoco existirá (volverá a ser nil) aunque durante el transcurso de la ejecución de la función haya recibido varios valores.

Hasta ahora hemos trabajado tan sólo con variables globales que declarábamos justo antes de un bucle principal de nuestro juego. Ahora tendremos un nuevo criterio que será el trabajar con variables locales siempre que nos sea posible. El motivo es sencillo: las variables locales no dejan información basura ni afectan directamente a otros componentes del programa, y es que básicamente nos interesará que cada parte de nuestro programa deje todo como lo encontró antes de ponerse a trabajar.

Pero hasta ahora tan sólo hemos visto un caso concreto de variables locales, como son las funciones y sus parámetros, como para poder hacer un uso eficiente de ellas. Por tanto, ahora que tenemos una pequeña comprensión de lo que son, podemos pasar a entenderlas en todos ámbitos. Y es que una variable local es aquella cuyo valor se mantiene tan sólo dentro de un ámbito local de visibilidad, y existen varios elementos que definen esos ámbitos.

Dentro de esos ámbitos locales las variables locales serán visibles por todas las estructuras y entidades que se ejecuten dentro de él, pudiendo así acceder a ellas aunque dichas entidades conformen por si mismas otro ámbito local. Por tanto podemos hablar de algo así como un orden o jerarquía, en el que encontraremos ámbitos locales más restrictivos dentro de otros ámbitos locales. Algo que se ilustra en el siguiente esquema:

Por eso antes de pasar a la declaración de variables locales me gustaría comentar las entidades que generan un ámbito local en Lua. El primero de ellos es el propio script del programa, la diferencia es que puesto que cualquier estructura dentro de ese script podrá acceder a la variables locales del mismo (tal y como hemos comentado), las denominaremos globales. Sin embargo, el sistema PSP si que contemplará esas variables como locales pero al programa en que se ejecuta nuestro script (sin ser esta afirmación del todo precisa).

Las estructuras condicionales (if, elseif, else) y las iterativas (while y for) también determinan ámbitos locales, desde su inicio hasta el end que concluye las acciones que realizarán:

if Condicion then

— Ambito Local 1

elseif

— Ambito Local 2

else

— Ambito Local 3

end

while Codicion do

— Ambito Local

end

for Condicion do

— Ambito Local

end

Las funciones también determinarán un ámbito local, como ya hemos comentado al introducir las variables locales, y este no se limitará sólo a los parámetros sino también a otras variables locales auxiliares o necesarias que necesite la función para realizar correctamente su trabajo.

function Firma
-- Ambito Local
end

Para declarar una variable local debemos hacerlo como cualquier otra pero con la palabra clave local precediendo la expresión normal:

local Variable = Valor

Y después de mucho camino recorrido y varios conceptos clave, volvemos a trabajar en nuestro menú allí donde lo dejamos. Para aquellos que hayan perdido el hilo y todavía tengan la cabeza en funciones y variables locales, recapitulemos. Seguramente más de uno estará disgustado por el hecho de que después de todo lo andado, aún no hemos dotado de ninguna funcionalidad al menú y lo único que hace es mostrar opciones y datos sobre los que no podemos “navegar”. Pero no es del todo cierto, hemos allanado el terreno con los últimos conceptos dados hasta el punto de que la interacción con el menú se basará en la pulsación de dos botones y una función que nos permitirá movernos por ellas según la pulsación de otros dos. Es por eso que antes de programar la función juego dotaremos de funcionalidad a nuestros menús para así recoger parte de la “cosecha sembrada” y que de alguna forma se intuya el avance. También porque es poco ordenado y poco recomendable dejar una parte a medias cuando podemos dejarla lista sin esfuerzo.

Pues manos a la obra, lo primero que debemos añadir a nuestro programa principal es la declaración e inicialización de las variables pad y oldpad que nos servirán para recibir información de los controles, elemento fundamental para la interacción con el usuario.

Ahora que ya disponemos de la información relativa a los controles, podemos cambiar el estado del juego en consecuencia. Empezaremos por el cambio de selección, para ello creamos una función llamada Alternar a la que pasábamos por parámetro la información de los controles además de otros argumentos.

Por último en el menú principal deberemos tener en cuenta que no hay ningún menú superior, por tanto no habrá una opción “atrás” sino que tan solo deberemos preocuparnos de cuando se selecciona la selección. El botón asignado para la selección, como es habitual en la plataformas PlayStation, será el X. Pese a que de momento no sea necesario, determinaremos que el botón por defecto para volver atrás o cancelar una operación será el O.

Como último apunte antes de implementar lo comentado a nuestro menú, será especificar que las funciones que creemos se almacenarán en una carpeta llamada “DATOS”. En esta carpeta crearemos 3 scripts uno que llamaremos “Interfaz.lua” que contendrá las funciones relacionadas con los menús y interacciones comunes con el usuario, otro que llamaremos “Juego.lua” que contendrá las funciones que programaremos para que el juego funcione y un último llamado “Otras.lua” en el que meteremos el resto. Esta división en 3 ficheros nos ayudará a organizarnos, para Lua el efecto será el mismo que si estuvieran en un mismo fichero o dentro del mismo programa principal.

Con lo comentado el código del menú principal y del fichero “Interfaz.lua” respectivamente será:

while Juego.Menu == 1 do
     screen:clear()
     pad = Controls.read()
     --Funcion para alternar el estado en funcion de las pulsacones
     Juego.OpcionMenu = Alternar(pad:down(), pad:up(), oldpad:down(), oldpad:up(), Juego.OpcionMenu, 1)
     for n = 1,4 do
         screen:print(50, 50+20*(n-1),OpMenuPrincipal[n],Blanco)
     end
     screen:print(50,50+20*(Juego.OpcionMenu-1),OpMenuPrincipal[Juego.OpcionMenu], Azul)
     oldpad = pad
     screen:flip()
     screen.waitVblankStart()
 end
--Funcion para alternar el estado en funcion de las pulsacones
function Alternar(B1, B2, OldB1, OldB2, Elemento, Diferencia)
if B1 and OldB1 ~= B1 then-- Si se pulsa el bot�n 1 y no hay repeticion
Elemento = Elemento+Diferencia--aumentamos la diferencia
elseif B2 and OldB2 ~= B2 then-- Si no se pulsa el boton 1 y el boton 2 es pulsado y no --hay repeticion
Elemento = Elemento-Diferencia--Disminuimos la diferencia
end
return Elemento--Devolvemos el valor de Elemento tras la funcion
end

Una vez que lo hemos probado detectamos un error que nos impide la ejecución “attemp to call global ‘Alternar’ (a nil value)”. Esta es la primera vez que hablamos de los mensajes de debug que nos proporciona Lua, y en realidad son realmente importantes porque entender su significado nos ayudará mucho a la hora de resolver errores de sintaxis o de accesos “prohibidos” por así llamarlos. Este error nos informa de que cuando se ha ejecutado la función Alternar no se ha encontrado ninguna declaración valida. Esto tiene mucho sentido, sobretodo porque en nuestro programa principal no hay ni una sola linea de código que parezca hacer referencia a una declaración de esta funcionalidad, y no la hay. Para hacerlo tendremos que hablar de la funcionalidad dofile que será nuestro primer acceso a un fichero externo que no sea un gráfico.