lunes, 14 de marzo de 2016

Sobre como mantener diferentes escenas en love2d (El gestor de escenas)



Bueno,retomando el tema del manejo de escenas,ahora le toca el turno al gestor de escenas

Gestor de escenas

El gestor será el encargado de guardar las escenas de nuestro juego y también de mantener el ciclo de vida de las mismas. Nuestro pequeño gestor será capaz de mantener una y sólo una escena en pantalla, quedando el resto de escenas guardadas dentro del gestor para un posterior uso.

Retomando nuestro archivo Escena.lua, creado en un post anterior, podremos ver como el ejemplo de escena, que os mostré, contenía varias funciones a sobre escribir y que la mayoría dejamos en desuso. Esto fue así, porque será ahora cuando el gestor use todas estas funciones para mantenerlas en pantalla.


Nuestro gestor de escenas va a contener las siguientes funciones:

  • construir_escena(nombre): A esta función no se la llamará directamente, si no que será llamada por el propio gestor, cuando queramos queramos generar una nueva escena.
  • nueva_escena(nombre): Función que usaremos para crear una nueva escena. La función recibe una cadena de texto con el nombre de la escena a crear.
  • cambiar_escena(nombre): Función que usaremos para navegar entre las escenas del gestor. La función recibe el nombre de la escena y si la encuentra en el gestor, la coloca como la escena activa.
  • eliminar_escena(nombre): Función que usaremos para eliminar, completamente, una escena del gestor.
  • load(): Será llamada desde love.load y nunca más, durante la ejecución del programa, se volverá a pasar por ella.
  • update(dt): Esta función es llamada por love.update(dt). En esta función se controla toda la lógica del gestor y al mismo tiempo se actualiza la escena activa en pantalla.
  • draw(): Función llamada desde love.draw y encargada de dibujar la escena activa en pantalla.

Para construir nuestro gestor, crearemos un nuevo proyecto con nuestro main.lua, el archivo Escena.lua de la entrada anterior y un nuevo archivo llamado GestorDeEscenas.lua.

Dentro de nuestro main.lua generamos la estructura de nuestro programa en Love2d




function love.load()

     --Aquí crearemos las variables y cargaremos los recursos

end



function love.update(dt)

     --Aquí realizaremos la actualización lógica de la aplicación

end



function love.draw()

     --Aqui se realizara todo el proceso de dibujar la pantalla

end


Ahora lo interesante. Dentro de GestorDeEscena.lua empezamos a escribir lo siguiente


--[[

Gestor de escenas maneja las diferentes escenas disponibles en un juego.

El gestor es capaz de mantener varias escenas en su interior, pero sólo una podrá estar activa y

visible en pantalla.

]]--

GestorDeEscenas=

{

     --Llamamos al archivo escena y lo guardamos dentro del gestor

     --La idea es dejar al cargo del gestor la creación de nuevas escenas

      baseEscena=require("escena"),

     --Será la tabla que contendrá todas las escenas pero.

     -- en un principio iniciamos la variable a nil(sin nada)

      escenas=nil,

     --escena a ocultar fruto de un cambio de escena en el gestor.

      escenaAOcultar=nil,

     --Tabla que contendrá todas las escenas a eliminar del gestor.

      escenasAEliminar={},

     --Nombre de la escena activa en todo momento.

      escenaActiva=nil


}

Desde ahora debemos tener en cuenta, que será el gestor el encargado de generar las escenas.

Después pasaremos a construir la función construir_escena(nombre) que se encargará de suministrar, de una nueva escena, a la función nueva_escena(nombre). Esto quiere decir, que esta función no será llamada directamente por nosotros a la hora de usar el gestor.

--[[Función encargada de crear una nueva escena y almacenarla en el gestor.

Esta función no se llama directamente.Esta función sera llamada, automáticamente,

cada vez que usemos la función nueva_escena.]]--

function GestorDeEscenas:construir_escena(nombre)

     --Usa el objeto escena para proveer una nueva escena,

     --que será automaticamente guardada dentro del gestor antes de retornarla

     --a la función nueva_escena

     self.escenas[nombre]=self.baseEscena:new()

     self.escenas[nombre].nombre=nombre

     self.escenaActiva=nombre

    return self.escenas[self.escenaActiva]

end

La siguiente función, será la que usaremos para crear infinitas escenas dentro del gestor.



--[[Genera una nueva escena, la almacena, la deja como la escena activa y al final la retorna]]--

function GestorDeEscenas:nueva_escena(nombre)

     --Pregunta si la variable escenas aun vale nil y si es así,

     --genera por primera vez la tabla dentro de ella. Acto seguido genera la nueva escena

     --y la retorna.

     if (self.escenas==nil)then

          self.escenas={}

        return self.construir_escena(self,nombre)

    --Si la tabla escena ya está creada, pregunta si el nombre de la escena a crear ya existe en el gestor.

    --Después pregunta si ya tiene una escena activa y si es así la prepara dentro de escenaAOcultar

    --para volver a almacenarla desde update. Acto seguido genera la nueva escena y la retorna.

    elseif (self.escenas[nombre]==nil) then

        if (self.escenaActiva~=nil) then

              self.escenaAOcultar=self.escenaActiva

       end

       return self:construir_escena(nombre)

    end

    --En caso de no poder construir la escena la función retornará el valor nil

    return nil

end

Para cambiar entre las escenas contenidas en el gestor usaremos la siguiente función


--[[Esta función recibe el nombre de una escena y si

se encuentra dentro del gestor, la coloca como la escena activa]]--

function GestorDeEscenas:cambiar_escena(nombre)

     --Preguntamos si existe la escena en el gestor

     if (self.escenas[nombre]~=nil) then

         --Preguntamos si la escena a mostrar no se esté mostrando ya

         if (self.escenaActiva~=nombre) then

            --Preguntamos si existe ya alguna escena activa

            --y si es así , la pasamos a escenaAOcultar para

            --ocultarla desde el método update(dt)

            if (self.escenaActiva~=nil) then

               self.escenaAOcultar=self.escenaActiva

               self.escenas[self.escenaActiva].activa=false

           end

        end

        --Si todo lo anterior fue correcto, pasamos el nombre

        --a la variable escenaActiva y a partir de aquí el método

        --update(dt) se encargará del resto

       self.escenaActiva=nombre

   end

end

La próxima función será la encargada de marcar las escenas a eliminar del gestor. Digo marcar porque, realmente, la acción de eliminar una escena del gestor será realmente en el método update(dt). La función sería la siguientes



--[[Función que se encarga de agrupar en una tabla, todas las escenas a eliminar del gestor,

después la función update(dt) será la encargada de eliminar las escenas aquí agrupadas]]--

function GestorDeEscenas:elimiar_escena(nombre)

     indice=1

     --Nos aseguramos de que la escena a eliminar esté en el gestor

     if (self.escenas[nombre]~=nil) then

         --Si la escena a eliminar es la escena activa, primero nos aseguramos

         --de que ya no se vuelva a mostrar en pantalla

         if (self.escenaActiva==nombre) then

             self.escenaAOcultar=self.escenas[nombre]

             self.escenaActiva=nil

        end

        --Insertamos la escena a eliminar dentro de la

        --tabla para su posterior borrdo.

        if (#self.escenasAEliminar>0) then

             indice=#self.escenasAEliminar

        end

             self.escenasAEliminar[indice]=self.escenas[nombre]

    end

end

La función load será llamada una sola vez desde el método principal love.load. Por ahora esta función asegura que la primera vez que se pase por la función update(dt) la escena inicial ya este viva. Perfectamente podríamos a ver prescindido por ahora de esta función, pero por mantener una buena construcción casi prefiero ponerla.

--[[Función que será llamada desde el main.lua.

La función carga los datos de la escena actualmente

apuntada, en el gestor, por escenaActiva]]--

function GestorDeEscenas:load()

     if (self.escenaActiva~=nil) then

         self.escenas[self.escenaActiva].load()

     end

end

La función draw, será la encargada de dibujar la escena activa en pantalla. Para esta tarea la función draw tendrá en cuenta que la escena ya este viva y en pantalla.


--[[Función llamada desde el main.lua y que se encarga

de dibujar la escena activa en el gestor]]--

function GestorDeEscenas:draw()

    if (self.escenaActiva~=nil) then

        --[[Aquí se dibuja el contenido de la escena activa, teniendo en cuenta que ya esté viva y mostrandose en pantalla]]--

        if (self.escenas[self.escenaActiva].estaViva==true) and (self.escenas[self.escenaActiva].estaEnPantalla==true) then

            self.escenas[self.escenaActiva].draw()

        end

    end

end

Y por fin el salsa del asunto, la función update(dt). Esta función sera la encargada real de llevar toda la lógica del gestor y en su proceso se realizarán las tareas de eliminar, mostrar, cargar y actualizar las escenas del mismo durante la ejecución de un juego.

--[[Función llamada desde el main.lua y que se encarga

de actualizar toda la lógica del gestor y las escenas contenidas en el mismo]]--

function GestorDeEscenas:update(dt)



     --iteramos sobre la tabla "escenasAEliminar" para liberar del gestor las escenas no necesarias

      for c=1, #self.escenasAEliminar do

          self.escenasAEliminar[c].hidden()

          self.escenasAEliminar[c].destroy()

          self.escenasAEliminar[c].estaViva=false

          nombre=self.escenasAEliminar[c].nombre

          self.escenas[nombre]=nil

          self.escenasAEliminar[c]=nil

     end

     --[[Controlamos el flujo de actualización de la escena activa en el gestor]]--

     if (self.escenaActiva~=nil) then

         --[[Antes de actualizar preguntamos si la escena ya esta viva,

         de no ser así, primero procedemos a pasar por su función load

         y marcamos si variable está viva.

         Update no actualiza la escena en esta vuelta]]--

         if (self.escenas[self.escenaActiva].estaViva~=true) then

             self.escenas[self.escenaActiva].load()

             self.escenas[self.escenaActiva].estaViva=true

         --[[Preguntamos si la escena está en pantalla, de no ser así,

         procedemos a pasar por su función show.

         Update no actualiza la escena en esta vuelta]]--

         elseif (self.escenas[self.escenaActiva].estaEnPantalla~=true) then

             self.escenas[self.escenaActiva].show()

             self.escenas[self.escenaActiva].estaEnPantalla=true

         else

         --[[Si la escena ya esta viva y en pantalla, pasamos a actualizarla mediante su función update(dt)]]--

             self.escenas[self.escenaActiva].update(dt)

        end

        --[[Si el gestor tiene alguna escena pendiente de ocultar, la hará pasar

         aquí por su función hidden]]--

        if (self.escenaAOcultar~=nil) then

            if (self.escenas[self.escenaAOcultar]~=nil) then

                self.escenas[self.escenaAOcultar].hidden()

                self.escenas[self.escenaAOcultar].estaEnPantalla=false

            end

            self.escenaAOcultar=nil

       end

     end

end



return GestorDeEscenas

Y como marca la última linea, sólo nos quedaba retornar el gestor que acabamos de construir.

Por no hacer esto más largo, me montaré un pequeño ejemplo en otro post, mientras tanto hasta la próxima.

No hay comentarios:

Publicar un comentario