Cache de flash(como evitar dolores de cabeza)

11 04 2008

Cabeza

La explicación
Se denomina “cache”(se pronuncia caché) a un “lugar”(puede ser memoria o disco) usado para conservar información que ha sido solicitada con anterioridad. En el caso que nos ocupa, usaremos el término para referirnos a cualquier información que haya sido pedida por la red y que, como ya la pedimos con anterioridad, no la volvemos a pedir en sucesivas llamadas, sino que utilizamos la información que ya tenemos.

Cuando navegamos por internet, muchas de las cosas que viajan por la red son almacenadas en este cache para acelerar la navegación. Generalmente se guardan los recursos mas pesados, como imágenes o archivos flash.

El contra que tiene esta técnica es que si estamos usando una aplicación web que necesita algún tipo de recurso actualizado al momento, podemos estar viendo información vieja.

Hasta hace poco yo conocía solo tres tipos de cache que afectan el trabajo de un desarrollador web. El cache de internet explorer(ya explicado), el cache del servidor web y el cache de base de datos(este ultimo muy poco común). Pero hace poco me encontré que el plugin de flash tambien tiene un cache de swf’s que utilizamos como recursos externos(es muy comun cargar un swf desde otro swf, piensen en un preloader).

La solución
En fin, lo que hacemos para cargar un swf y asegurarnos de que sea el último, le agregamos un signo de pregunta (”?”) y un número aleatorio generado en el momento a la url del securso que queremos cargar.

Ejemplo:

Si queremos cargar desde una película flash que hace de preloader el recurso:

“http://www.misitio.com/flash/mi_archivo.swf”

Este nos debe quedar de la siguiente manera

“http://www.misitio.com/flash/mi_archivo.swf?[numero_aleatorio]“

Donde numero_aleatorio es algún número generado mediante una llamada al método “Math.random()”.

Con eso evitamos que cualquier swf sea cacheado por el flash player.

Saludos!



Moral en el equipo de desarrollo.

8 04 2008

Hace tiempo jugaba a un juego multiplayer que se llama “Enemy Territory”. En este juego, los participantes elegían un equipo, nazi o aliado y tenían que proteger o destruir objetivos según la pantalla.

Existía una pantalla en la que el equipo aliado debía:

A. Destruir un paredon para ganar acceso a dos cañones anti tankes.
B. Destruir los dos cañones anti tankes.

El problema era que había un bug en el motor de física que permitía hacer un “super salto”. Por medio de este supersalto se podía acceder a los cañones sin destruir el paredón. El bug era muy difícil de utilizar, ya se que necesitaba una máquina capaz de generar un muy alto numero de frames por segundo y un timing sobrehumano. Sin embargo, allí estaba.

Resulta que estaba yo jugando en esta pantalla, del lado nazi, defendiendo el paredón. Habían pasado ya 15 minutos y habíamos mantenido al equipo contrario a raya sin demasiados inconvenientes. Cuando de repente, se escucha la alarma. Un aliado había utilizado el bug del juego para saltar el paredón sin ser visto y había plantado la bomba en el primero de los cañones anti tanke.

Lo que debía hacer mi equipo era. Pasar al lado de los cañones, matar al ingeniero y desactivar la bomba. Dado que el ingeniero necesita dejar pasar un tiempo para volver a plantar la bomba, el equipo nazi poseía 30 segundos para hacer todo esto. Aunque no pudiera desactivar la bomba, la pantalla todavía podía ser ganada si mataban al ingeniero y se quedaba haciendo guardia del otro lado, ya que aún nos quedaba un cañón anti tanke.

Pero el equipo nazi, al tener una bomba plantada en el objetivo principal se desmoralizó, perdió seguridad y se dejó vencer.

En cualquier proyecto que se encare en la vida, sobre todo si es en grupo, tener una moral alta es vital si se desea tener éxito. Lo saben los lideres militares cuando dirigen un discurso motivador a sus ejércitos, lo saben las grandes empresas cuando organizan jornadas de integración grupal y, por supuesto, lo saben los que juegan al “Enemy Territory”.

En un equipo de desarrollo, donde las cosas se hacen con el cerebro, es nuestro deber fomentar mecanismos que eliminen a los factores que atentan contra la moral del equipo. Por ejemplo:

* Utilizar un sistema de seguimiento de bugs: Esto permite saber que pasó con determinado bug que se solucionó y luego vuelve a aparecer, entre otras cosas.
* Asegurarse que todos respetan su trabajo y el de los demás: Evitar motivos que den lugar a frases como “este código esta mal” o “es una chanchada, pero hay que hacerlo así”. Si no se hace bien, que no se haga.
* En todo momento debe tenerse la certeza de que lo que se esta haciendo tiene sentido. Si no es así, se debe para el trabajo inmediatamente y replanificar.
* etc.

Si algo atenta contra la moral del equipo, atenta contra el proyecto, por ende, atenta contra la empresa. Tengamoslo siempre en cuenta.

Salutes!



¿Que pasa si nos piden meter una animación en medio de nuestro juego?

1 04 2008

Imaginen que estamos programando muy contentos nuestro juego en flash. De repente, el diseñador gráfico viene y nos dice que hay que poner una animación antes de que comience el juego, a modo de tutorial.

En lugar de mandarlo a la mierda, creamos otra clase al mismo nivel que nuestra clase principal, que herede de sprite(nos va a decir que la pongamos adentro de un paquete, pero podemos tranquilamente ignorar el warning). En las propiedades del proyecto, agregamos esta nueva clase como aplicación. Desde ella creamos una instancia de la clase en la que estábamos programando en primer lugar y luego hacemos un addChild de esa misma instancia.

Ahora, si corremos la clase que creamos ultimo, veremos que el juego sigue andando. Antes de hacer el addChild de la clase principal del juego, podemos poner la animación sin problemas.

Una buena práctica es cargar los niveles por separado y agregarlos como aplicaciones independientes, haciendo que tomen parámetros del juego* de un singleton, de manera tal que podamos irlos probando por separado y meter cualquier animación o transición entre ellos sin problemas. Los niveles deben cargarse por medio de un “addChild” y descargarse por medio de un “removeChild”.

* Como vidas restantes, puntaje y demás.



Optimizando las entradas por teclado en ActionScript 3.0

1 01 2008

Resulta que estaba haciendo un juego donde tenía que capturar la entrada por teclado, algo muy simple, hay muchisimos ejemplos en la web. Básicamente agregaba un listener al stage para que, cuando una tecla era presionada, se cambie la dirección del personaje en pantalla. Una vez implementado esto, me encontré con un problema:

Cuando cambiaba de dirección, por ejemplo, iba a la derecha y giraba a la izquierda, el personaje tardaba en reaccionar, algo así como 0.5 de segundo.

Entonces, en lugar de meter todo el código dentro del evento “onKeyPress”, el cual tenía metido un switch donde testeaba por el código de tecla presionada y pasaba a modificar la posición del sprite en pantalla, agregué dos listeners al objeto stage: onKeyDown y onKeyUp.

Estos métodos lo único que hacían era actualizar un elemento de una matriz de 255 elementos, poeniendolo en true en el evento onKeyDown y en false en el evento onKeyUp.

La matriz la puse dentro de un singleton, para poder acceder a ella de cualquier parte. Después, solo tenía que fijarme si el elemento del array que correspondía a la tecla que quería testear estaba a true, si esto era cierto, actualizaba la posición del objeto.

Así fue como obtuve una respuesta del personaje mas fluida. Espero que esta experiencia les sea de utilidad.

Saludos!



Consideraciones del modelo de eventos en ActionScript 3

1 01 2008

Las siguientes consideraciones deben tenerse en cuenta cuando se programa usando el modelo de eventos de actionscript 3, es decir, clases que heredan de EventDispatcher o que implementan la clase IEventDispatcher.

En este post, llamo listeners a las funciones que "escuchan" eventos. Las mismas tienen la siguiente forma:

Actionscript:
  1. [public|private] function myListener(evt:Event):void{
  2.         //bla bla bla
  3. }

1. Los eventos son identificados por la propiedad type, por lo que, si tenemos un listener que escucha al evento "onMyListenerDispatch", para llamarla desde el objeto hacemos dispatchEvent(new Event("onMyListenerDispatch"));.

Si tenemos...

Actionscript:
  1. //Mas código
  2.  
  3. myClase.addEventlistener("myDispatchEvent", myEventListener);
  4.  
  5. //Mas código
  6.  
  7. public function myEventListener(evt:Event){
  8. }

para llamar a la funcion "myEventListener" hacemos:

Actionscript:
  1. //Desde dentro de la función que dispara eventos
  2.  
  3. dispatchEvent(new Event("myDispatchEvent"));

2. Si tenemos una clase1 que dispara un evento e implementa una función1 que lo escucha, y otra clase2 que la extiende, es decir, que hereda de ella y sobreescribe la función1. Cuando la clase1 en uno de sus miembros no sobreescritos dispara un evento, el listener que se disaparará será el sobreescrito por la clase2.

por ejemplo, si tenemos:

Clase padre:

Actionscript:
  1. public class Clase1 extends EventDispatcher
  2. {
  3.     public function FSSceneStateGeneric()
  4.         {
  5.             super();
  6.             addEventListener("onBeginUpdate", onBeginUpdate);
  7.             addEventListener("onEndUpdate", onEndUpdate);
  8.         }
  9.  
  10.         public function onBeginUpdate(evt:Event):void{
  11.             trace("super: on Begin Update!");
  12.         }
  13.        
  14.         public function onEndUpdate(evt:Event):void{
  15.             trace("super: on End Update!")
  16.         }
  17.        
  18.         public function update():void{
  19.             dispatchEvent(new Event("onBeginUpdate"));
  20.         }
  21. }

Clase derivada

Actionscript:
  1. public class FSSceneStateFadding extends FSSceneStateGeneric
  2.     {
  3.         public function Clase2()
  4.         {
  5.    
  6.         }
  7.        
  8.         public override function onBeginUpdate(evt:Event):void{
  9.             trace("STATE: ON BEGIN UPDATE");
  10.         }
  11.        
  12.        
  13.         public override function update():void{
  14.             super.update();
  15.         }
  16. }

Lo que obtendremos en el trace será:

STATE: ON BEGIN UPDATE

Ampliaremos.