Dos veces al mes, volvemos a visitar algunas de las publicaciones favoritas de nuestros lectores de toda la historia de Activetuts +. Este tutorial se publicó por primera vez en marzo de 2010 y es la primera parte de una serie..
Código simple y fácil de mantener es hermoso. Sin embargo, cuando tenemos una serie de acciones que deben activarse mutuamente, nuestro código puede ensuciarse, lo que hace imposible cambiarlo más adelante. los Patrón de comando mantiene las cosas limpias.
En este tutorial, te mostraré cómo crear un marco de comandos AS3 minimalista, capaz de realizar acciones en secuencia, en paralelo o con un retraso. Aprenderá cómo usar este marco para crear un efecto complejo con un código simple y limpio..
Encapsular instrucciones en "comandos" es un método de programación popular para simplificar las cosas: el patrón de comando es en realidad uno de los patrones de diseño más utilizados en la programación orientada a objetos. Básicamente, el concepto de comando se implementa mediante la creación de clases de comando, cada una de las cuales representa un tipo de comando. En el resto del tutorial, cuando me refiero a "un comando", me refiero a "un objeto de comando".
Puede pensar en un comando como un botón en un control remoto. Cada botón hace algo diferente, pero todos se usan de la misma manera: lo presionas, luego ocurre la magia. Ya sea encendiendo el televisor, cambiando los canales o ajustando el volumen, todas estas funciones se pueden hacer simplemente presionando un botón.
Imagen cortesía de freedigitalphotos.net
El concepto de comandos es el mismo. La instrucción subyacente de un comando es como la función de un botón de control remoto. Puede encapsular diferentes instrucciones en comandos, como trazar un mensaje simple, mover un objeto de un lugar a otro o alternar la visibilidad de un objeto de visualización. Una vez que se realiza la encapsulación, estos se pueden realizar simplemente indicando al programa que "presione los botones del control remoto", o en otras palabras, que "ejecute los comandos".
Si desea que el programa funcione de manera diferente, simplemente puede cambiar el código dentro de la clase de Comando: el programa aún ejecuta los mismos comandos que lo hizo anteriormente, pero el código subyacente dentro de los comandos es diferente. Su lista general de las acciones que desea que haga el programa está separada de su lista detallada de instrucciones sobre cómo debe realizarse cada acción..
"Gran cosa", podrías decir, "podría hacer eso usando funciones. ¿Por qué molestarme en usar comandos?" Bien, veamos dos conjuntos de código que crean el mismo efecto, uno que usa funciones y el otro que usa el marco de comandos que crearemos en este tutorial. La ventaja de los comandos quedará clara.
Digamos que queremos crear un círculo, agregarlo al escenario, interpolarlo de invisible a visible en medio segundo, esperar dos segundos, volver a invisible en otro medio segundo y luego retirarlo del escenario. Para hacer todo esto usaremos la clase TweenNano de Greensock..
Si solo estás usando funciones, el código se verá así:
var circle: Circle = new Circle (); addChild (círculo); TweenNano.from (circle, 0.5, alpha: 0, onComplete: func1); function func1 (): void TweenNano.to (circle, 0.5, delay: 2, alpha: 0, onComplete: func2); function func2 (): void removeChild (circle);
¿Ves cómo nuestra lista de acciones está enredada con nuestras instrucciones para realizar cada acción? Para averiguar qué va a pasar, tienes que seguir todas las onCompletes y ver a dónde llevan.
Aquí está el mismo código, usando un marco de comando:
var circle: Circle = new Circle (); var comando: Comando = nuevo SerialCommand (0, nuevo AddChild (este, círculo), nuevo TweenNanoFrom (círculo, 0.5, alfa: 0), nuevo TweenNanoTo (círculo, 0.5, demora: 2, alfa: 0), nuevo RemoveChild (esto, círculo)); command.start ();
aquí, AddChild (), TweenNanoFrom, TweenNanoTo, y Quitar niño son todas las clases de comandos que definimos en otras partes del código, y SerialCommand Es otra clase de comandos que podemos usar para crear secuencias de comandos sobre la marcha..
Resultado: no hay más funciones "saltos". Está claro qué hará esta secuencia y en qué orden. También es fácil cambiar el orden de las acciones o insertar una nueva acción entre las existentes, sin tener que buscar el código de cada acción y cambiar su acción. onComplete propiedad.
Los comandos también nos permiten poner en cola diferentes acciones para que se realicen al mismo tiempo, pero llegaremos a eso más adelante.!
Un ejemplo de trabajo rápido vale más que mil palabras, así que echemos un vistazo al elemento esencial de nuestro marco de comando: la clase de comando.
comandos de paquete import flash.events.Event; import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.utils.Timer; El comando público de la clase extiende EventDispatcher private var _timer: Timer; Función pública Comando (retraso: Número = 0) _timer = nuevo Temporizador (int (1000 * retraso), 1); _timer.addEventListener (TimerEvent.TIMER_COMPLETE, onTimerComplete); función privada onTimerComplete (e: TimerEvent): void execute (); / ** * Inicia el comando. * Espera a que se complete el temporizador y llama al método execute (). * Este método se puede utilizar directamente como un detector de eventos. * / public public function start (e: Event = null): void _timer.start (); / ** * El método abstracto que debe anular para crear su propio comando. * / función protegida ejecutar (): void / ** * Completa el comando. * Despacha un evento completo. * Este método se puede utilizar directamente como un detector de eventos. * / función final protegida completa (e: Evento = nulo): void dispatchEvent (nuevo Evento (Event.COMPLETE));
El método "más vacío" es el método execute (); sin embargo, este método es la parte más importante del comando. Para crear varios objetos de comando, debe extender esta clase de comando y anular el método execute (), completando las instrucciones que desea que realice su programa..
Para hacer que un objeto Command funcione, debe llamar a su método start (); hace una cuenta regresiva del tiempo de demora usando un objeto Timer, y llama al método execute () cuando el temporizador termina la cuenta regresiva. Un tiempo de demora cero simplemente significa que se llamará al método execute () de su objeto Command justo después de que llame a su método start ().
(Tenga en cuenta que cuando complete su comando, deberá llamar al método complete () manualmente, lo que provocará que envíe un evento COMPLETE. El propósito de este método se aclarará más adelante en el tutorial).
Por cierto, la configuración del parámetro de evento para los métodos start () y complete () con un valor predeterminado nulo es solo mi hábito personal. De esta manera, los métodos se pueden llamar como lo haría con cualquier otro método de parámetro cero, o se pueden usar directamente como escuchas de eventos.
Ahora que tenemos nuestra clase de Comandos, comencemos a jugar con ella con un simple rastreo.
En primer lugar, tenemos que abrir el IDE de Flash y crear un nuevo documento de Flash. Llámalo SimpleTracing.fla.
A continuación, cree una clase de documento para este documento de Flash. Lea este Consejo rápido para una introducción a las clases de documentos..
paquete import flash.display.Sprite; clase pública SimpleTracing extiende Sprite función pública SimpleTracing ()
Guárdelo como SimpleTracing.as.
Cree un nuevo archivo AS y copie la clase de Comando (desde arriba).
Cree una nueva carpeta en su ruta de clase llamada "comandos" y guarde este nuevo archivo AS como Comando.así dentro de esa carpeta.
Nos gustaría comenzar por encapsular una función de rastreo en comandos, así que extendamos la clase Command para crear una clase TraceCommand para este propósito. Esta clase contendrá una cadena de mensaje que se rastreará cuando se llame al método execute (), y llamará al método complete () después del rastreo.
comandos de paquete clase pública TraceCommand extiende Comando private var _message: String; función pública TraceCommand (retraso: número, mensaje: cadena) super (retraso); _message = mensaje; anular la función protegida execute (): void trace (_message); completar();
Guarde esto como TraceCommand.as, también en la carpeta "comandos". Vea cómo hemos anulado el ejecutar() Función para hacer que este comando realmente haga algo.?
Completa la clase de documento con objetos TraceCommand. Agregue escuchas para el evento COMPLETO de estos comandos..
paquete comandos de importación. Comando; comandos de importación. TraceCommand; importar flash.display.Sprite; import flash.events.Event; la clase pública SimpleTracing extiende Sprite función pública SimpleTracing () var comando: Comando; comando = nuevo TraceCommand (0, "primer comando"); command.addEventListener (Event.COMPLETE, onCommandComplete); command.start (); comando = nuevo TraceCommand (1, "segundo comando"); command.addEventListener (Event.COMPLETE, onCommandComplete); command.start (); comando = nuevo TraceCommand (2, "tercer comando"); command.addEventListener (Event.COMPLETE, onCommandComplete); command.start (); función privada onCommandComplete (e: Evento): void trace ("un comando está completo");
Decirle al programa que ejecute los comandos es tan simple como llamar a los métodos start () de los objetos Command. Pruebe la película y verá la siguiente salida, impresa línea por línea con un intervalo de tiempo de un segundo. Además, puede ver los mensajes de entrelazado impresos por el detector de eventos completo de los comandos. La misma variable se usa para mantener referencias a diferentes objetos de Comando, pero el programa hace lo mismo con la variable: llama al método start () y escucha un evento COMPLETO.
Hay ocasiones en las que le gustaría ejecutar varios comandos con tiempos complejos. Aquí presentaré dos tipos comunes de comandos que pueden lograr una temporización avanzada de comandos: comandos paralelos y en serie. Ambos son comandos compuestos, lo que significa que contienen múltiples subcomandos. Vamos a echarles un vistazo uno por uno.
Un comando paralelo ejecuta todos sus subcomandos al mismo tiempo, o, en otras palabras, en paralelo. El comando está completo solo cuando todos sus subcomandos están completos. La siguiente figura da un concepto visual de un comando paralelo. Las puntas de flecha negras denotan el "flujo" de la ejecución del comando
Ahora es el momento de crear nuestra clase para comandos paralelos..
A continuación se muestra el código completo de la clase ParallelCommand. Guárdelo como ParallelCommand.as en su carpeta de "comandos".
Los subcomandos se pasan al constructor como el? (resto) parámetro. Esto nos permite pasar tantos comandos como queramos al constructor; se colocarán automáticamente en una matriz llamada comandos. Veremos la belleza de este tipo especial de parámetro muy pronto.
comandos de paquete import flash.events.Event; la clase pública ParallelCommand extiende el comando private var _commands: Array; función pública ParallelCommand (retraso: número, comandos?) //? comandos es el parámetro "? (resto)" super (retardo); _comandos = comandos; private var _completeCommandCount: int; anular la función protegida final execute (): void // establecer el recuento de comandos completo en cero _completeCommandCount = 0; para cada (comando var: comando en _comandos) // ¿escucha el evento completo de un subcomando? command.addEventListener (Event.COMPLETE, onSubcommandComplete); //? e inicie el subcomando command.start (); función privada onSubcommandComplete (e: Event): void // dejar de escuchar el comando completo del evento (e.target) .removeEventListener (Event.COMPLETE, onSubcommandComplete); // incrementa el recuento de comandos completo _completeCommandCount ++; // si todos los comandos están completos? if (_completeCommandCount == _commands.length) //? entonces este comando paralelo está completo completo ();
Esta clase anula el método execute (); el nuevo método execute () ahora llama al método start () de todos los subcomandos, y escucha sus eventos COMPLETO. El detector de eventos COMPLETO para los subcomandos cuenta cuántos subcomandos se han completado; Una vez que se completan todos los subcomandos, se llama al método complete () de ParallelCommand y se envía un evento COMPLETE propio..
Probemos la clase ParallelCommand. Cree un nuevo documento de Flash, copie la carpeta de "comandos" en su ruta de clase y escriba una nueva clase de documento como se muestra a continuación:
paquete comandos de importación. Comando; comandos de import.ParallelCommand; comandos de importación. TraceCommand; importar flash.display.Sprite; import flash.events.Event; la clase pública ParallelTracing extiende Sprite función pública ParallelTracing () var parallelCommand: Command = new ParallelCommand (0, new TraceCommand (0, "1st of 3"), nuevo TraceCommand (0, "2nd of 3"), new TraceCommand (0 , "3 de 3"),); parallelCommand.addEventListener (Event.COMPLETE, onCommandComplete); parallelCommand.start (); función privada onCommandComplete (e: Evento): void trace ("todos los comandos están completos");
El beneficio de usar el parámetro "? (Resto)" para el parámetro constructor ahora se hace evidente. Puede formatear los subcomandos con la sangría de código adecuada para escribir códigos visualmente autoexplicativos.
Pruebe la película y verá los tres mensajes trazados al mismo tiempo, luego un mensaje final que indica la finalización del comando paralelo:
¿Qué pasa con la configuración de retrasos dentro de un comando paralelo? Sencillo. Cambie la función constructora de su clase de documento de esta manera:
función pública ParallelTracing () var parallelCommand: Command = new ParallelCommand (0, new TraceCommand (0, "first wave, 1st of 2"), nuevo TraceCommand (0, "first wave, 2nd of 2"), nuevo TraceCommand (1 , "segunda ola, primero de 3"), nuevo TraceCommand (1, "segunda ola, segundo de 3"), nuevo TraceCommand (1, "segunda ola, tercero de 3"), nuevo TraceCommand (2, "última ola, 1st of 2 "), nuevo TraceCommand (2," last wave, 2nd of 2 ")); parallelCommand.addEventListener (Event.COMPLETE, onCommandComplete); parallelCommand.start ();
Pruebe la película y verá las siguientes tres oleadas de mensajes impresos, con un espacio de un segundo entre cada ola:
Para tener una mejor idea de lo que está pasando, mira esta ilustración:
El segundo tipo de comando compuesto es el comando serial. Un comando en serie ejecuta sus subcomandos uno tras otro, o, en otras palabras, en serie. Por ejemplo, el segundo comando se ejecuta después de completar el primero y el tercero se ejecuta después de completar el segundo. La siguiente figura da un concepto visual de un comando en serie:
Aquí está el código fuente de la clase SerialCommand. El método de ejecución () anulado llama al método start () del primer subcomando y escucha su evento COMPLETO. Luego, el detector de eventos inicia el siguiente subcomando y escucha su evento COMPLETO, y así sucesivamente, hasta que se completen todos los subcomandos. En ese momento, se envía el evento COMPLETE para todo SerialCommand.
comandos de paquete import flash.events.Event; la clase pública SerialCommand extiende el comando private var _commands: Array; Función pública SerialCommand (delay: Number ,? command) super (delay); _comandos = comandos; private var _completeCommandCount: int; anular la función protegida final execute (): void // establecer el recuento de comandos completo en cero _completeCommandCount = 0; // ¿Escucha el evento completo del primer subcomando? _commands [0] .addEventListener (Event.COMPLETE, onSubcommandComplete); //? e inicie el subcomando _comandos [0] .start (); función privada onSubcommandComplete (e: Event): void // dejar de escuchar el comando completo del evento (e.target) .removeEventListener (Event.COMPLETE, onSubcommandComplete); // incrementa el recuento de comandos completo _completeCommandCount ++; // si todos los comandos están completos? if (_completeCommandCount == _commands.length) //? entonces este comando serial es completado completo (); else //? De lo contrario, ¿escuchar el evento completo del próximo subcomando? _commands [_completeCommandCount] .addEventListener (Event.COMPLETE, onSubcommandComplete); //? e inicie el subcomando _comandos [_completeCommandCount] .start ();
Usemos la clase SerialCommand para hacer un seguimiento en serie. Como antes, cree un nuevo documento de Flash, copie la carpeta de "comandos" y escriba una nueva clase de documento:
paquete comandos de importación. Comando; comandos de importación.SerialCommand; comandos de importación. TraceCommand; importar flash.display.Sprite; import flash.events.Event; clase pública SerialTracing extiende Sprite función pública SerialTracing () var serialCommand: Comando = nuevo SerialCommand (0, nuevo TraceCommand (0, "primer comando"), nuevo TraceCommand (1, "segundo comando"), nuevo TraceCommand (1, " tercer comando ")); serialCommand.addEventListener (Event.COMPLETE, onCommandComplete); serialCommand.start (); función privada onCommandComplete (e: Evento): void trace ("todos los comandos están completos");
Pruebe la película y los siguientes mensajes se rastrearán uno por uno, con un intervalo de tiempo de un segundo, seguido de "todos los comandos están completos".
Aquí hay una figura conceptual de este ejemplo que le ayuda a comprender mejor lo que está sucediendo..
Hasta ahora, solo hemos explorado el uso más básico de los comandos paralelos y en serie, y no parece haber ningún punto para usarlos en lugar de comandos separados. Sin embargo, hay ocasiones en las que necesita ejecuciones de comandos mucho más complejas, y puede combinar varios comandos compuestos para crear comandos anidados que se ajusten a sus necesidades. El siguiente ejemplo muestra cómo usar la clase ParallelCommand y la clase SerialCommand para crear tales comandos anidados.
Como antes, cree un nuevo documento de Flash, copie la carpeta de "comandos" y escriba una nueva clase de documento:
paquete comandos de importación. Comando; comandos de import.ParallelCommand; comandos de importación.SerialCommand; comandos de importación. TraceCommand; importar flash.display.Sprite; import flash.events.Event; la clase pública NestedCommands extiende Sprite función pública NestedCommands () var nestedCommands: Command = new SerialCommand (0, nuevo ParallelCommand (0, nuevo TraceCommand (0, "comando paralelo # 1, parte 1 de 2"), nuevo TraceCommand (0 "comando paralelo # 1, parte 2 de 2"), nuevo TraceCommand (0, "------------------------------- - ")), nuevo ParallelCommand (1, nuevo TraceCommand (0," comando paralelo # 2, parte 1 de 3 "), nuevo TraceCommand (0," comando paralelo # 2, parte 2 de 3 "), nuevo TraceCommand (0 , "comando paralelo # 2, parte 3 de 3"), nuevo TraceCommand (0, "------------------------------ - ")), nuevo ParallelCommand (1, nuevo TraceCommand (0," último comando "), nuevo TraceCommand (0," ---------------------- ---------- "))); nestedCommands.addEventListener (Event.COMPLETE, onCommandComplete); nestedCommands.start (); función privada onCommandComplete (e: Evento): void trace ("todos los comandos están completos");
Pruebe la película y el programa imprimirá los siguientes fragmentos de mensajes uno por uno, con un intervalo de tiempo de un segundo. Como en los ejemplos anteriores, se imprimirá un mensaje final completo cuando todos los subcomandos estén completos.
Aquí está la figura del concepto de este ejemplo..
Finalmente, veamos un ejemplo más práctico. Vamos a utilizar el marco de comandos que hemos construido para crear una demostración del circuito de luz, con temporización avanzada. Antes de comenzar, (lo has adivinado) crea un nuevo documento de Flash, copia la carpeta de "comandos" y crea una nueva clase de documento.
Cree un símbolo de clip de película, con una animación de línea de tiempo donde un círculo cambia su color de gris a amarillo.
En la línea de tiempo, en el último fotograma clave, agregue el siguiente código. Esto hace que el clip de película deje de animar y envíe un evento COMPLETE:
detener(); dispatchEvent (nuevo evento (Event.COMPLETE));
Si desea evitar la codificación en la línea de tiempo, puede crear una clase para su clip de película ligero, con una función:
función pública reachEndOfAnimation (): void stop (); dispatchEvent (nuevo evento (Event.COMPLETE));
? y luego en el constructor de esa clase, escribe lo siguiente:
addFrameScript (4, reachEndOfAnimation) // donde 4 es uno menos que el número de cuadros
Organice las instancias de luz en el escenario y nómbrelas como muestra la siguiente figura:
Arrastre un componente Button desde el Panel de componentes al escenario y asígnele el nombre "start_btn". Queremos ejecutar nuestros comandos cuando se presiona este botón.
Cree un campo de texto en el escenario y escriba su mensaje de finalización. A continuación, conviértalo en un símbolo de clip de película y nombre a la instancia "completeMessage_mc".
Ahora es el momento de editar la clase de documento. Declare una variable privada "circuitCommand", que se utilizará para mantener una referencia a un objeto Command:
private var circuitCommand: Comando;
Al comienzo del programa, todas las luces se apagarán, es decir, se detendrán en el primer cuadro, y el mensaje de finalización debería estar oculto. Así llamamos al método reset () en el constructor..
Reiniciar();
Luego, cree nuestros comandos anidados que reproducen las animaciones de los clips de película livianos, iluminándolos con la sincronización adecuada. Usamos una clase PlayCommand aquí, que simplemente llama al método play () de un clip de película. Escribiremos la clase mas tarde.
circuitCommand = nuevo SerialCommand (0.5, nuevo PlayCommand (0, light_1), nuevo ParallelCommand (0.5, nuevo PlayCommand (0, light_2_1), nuevo PlayCommand (0, light_2_2)), nuevo PlayCommand (0.5, light_3), nuevo ParallelCommand (0.5, nuevo PlayCommand (0, light_4_1), nuevo PlayCommand (0, light_4_2)), nuevo PlayCommand (0.5, light_5));
A continuación, escuche el evento COMPLETO del comando y el evento CLICK del botón de inicio:
circuitCommand.addEventListener (Event.COMPLETE, onCommandComplete); start_btn.addEventListener (MouseEvent.CLICK, startCircuit);
Mostrar el mensaje de finalización cuando se complete el comando:
función privada onCommandComplete (e: Evento): void completeMessage_mc.visible = true;
Reinicie el circuito e inicie el comando cuando se hace clic en el botón de inicio.
función privada startCircuit (e: MouseEvent): void reset (); circuitCommand.start ();
La última parte de la clase de documento es el método reset (). Nada que ver con los comandos aquí..
función privada reset (): void completeMessage_mc.visible = false; light_1.gotoAndStop (1); light_2_1.gotoAndStop (1); light_2_2.gotoAndStop (1); light_3.gotoAndStop (1); light_4_1.gotoAndStop (1); light_4_2.gotoAndStop (1); light_5.gotoAndStop (1);
La última parte de este ejemplo es la clase PlayCommand. Como se mencionó anteriormente, lo que hace es tan simple como llamar al método play () de un clip de película. Tan pronto como se llama al método play () en el método de ejecución () anulado del comando, también se llama al método complete ().
comandos del paquete import flash.display.MovieClip; import flash.events.Event; la clase pública PlayCommand extiende el comando private var _movieClip: MovieClip; función pública PlayCommand (delay: Number, movieClip: MovieClip) super (delay); _movieClip = movieClip; anular la función protegida execute (): void _movieClip.addEventListener (Event.COMPLETE, completo); _movieClip.play ();
Guarda esto como PlayCommand.as en tu carpeta de "comandos".
De acuerdo, ¡hemos terminado! Ahora pruebe la película y verá que las luces se encienden de izquierda a derecha después de hacer clic en el botón de inicio. El mensaje de finalización se muestra cuando todas las luces están encendidas.
Aquí está la representación visual de lo que está pasando en este Ejemplo:
Compárelo con el código real y vea lo fácil que es entenderlo:
nuevo SerialCommand (0.5, nuevo PlayCommand (0, light_1), nuevo ParallelCommand (0.5, nuevo PlayCommand (0, light_2_1), nuevo PlayCommand (0, light_2_2)), nuevo PlayCommand (0.5, light_3), nuevo ParallelCommand (0.5, nuevo PlayCommand (0, light_4_1), nuevo PlayCommand (0, light_4_2)), nuevo PlayCommand (0.5, light_5));
Nuevamente, con la sangría de código adecuada, un comando anidado complejo se puede expresar como un código simple y limpio.
En este tutorial has aprendido el concepto de comandos. Las instrucciones se pueden encapsular en comandos que tienen interfaces idénticas, al igual que cada botón en un control remoto tiene una acción diferente, pero el método para invocar cada acción es el mismo: presione el botón.
Además, este tutorial le presentó dos tipos de comandos compuestos: paralelo y en serie. Se pueden usar para crear comandos anidados que permiten la ejecución avanzada de comandos mientras se mantiene el código limpio.
El concepto de comandos es muy conveniente y poderoso. La encapsulación de código es el enfoque principal para simplificar las cosas durante la programación, y uno de los métodos más utilizados es el uso de objetos de comando. Espero que este tutorial te ayude a entender mejor cómo usar los comandos en aplicaciones prácticas..
En la siguiente parte de este tutorial, le mostraré cómo integrar TweenLite con el marco de comandos que creamos en este tutorial, y luego manejar las transiciones de escena con un código simple y limpio. Muchas gracias por leer.