Guía práctica Desarrollo de un juego de comparaciones con AS3 y paquetes Preparado por Fco. Rocco CREANDO UN JUEGO DE COINCIDENCIAS: arrastrar y soltar Empezaremos con 8 elementos que forman nuestro juego: 4 textos y 4 dibujos.Crearemos una clase para los 4 textos del escenario, y la llamaremos txtBanderas.as. Recordemos que al escribirla con mayúscula nos referimos a una clase. POO: Arrastrar y soltar Comenzamos entonces escribiendo el siguiente código dentro de ella: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 package { import.flash.display.movieClip; import.flash.events.MouseEvent; public class txtBandera extends MovieClip { public function txtBandera ( ): void { this.addeventListener( MouseEvent.MOUSE_DOWN, arrastrarlo); this.addeventListener( MouseEvent.MOUSE_UP, soltarlo); } private function arrastrarlo (e: MousEvent) : void5 { this.startDrag ( ); private function soltarlo(e: MousEvent ) : void7 { this.stopDrag ( ); } } Cometarios: 01: Lo 1ro es crear un paquete que contenga una clase publica la que llamaremos “txtBandera” 03-04: Importamos las clases que ocuparemos 06: Creamos propiedades para las clases, las que son variables públicas.Incluimos todos los métodos y propiedades de la clase MovieClip con la palabra clave de definición extends. 08: Agregamos los detectores de eventos dentro de una función constructora Arrastrar y soltar POO: para que las funcionalidades de arrastrar y soltar ocurran, es decir al hacer clck en el mouse y al soltarlo. Para ello creamos una función pública que aloje estos detectores. 10: A esta clase (this) le agregamos un detector de eventos del tipo MouseEvent..MOUSE_DOWN, para que la función ocurra solamente cuando mantenemos el botón del mouse presionado. La función CLICK es cuando hacemos click y luego soltamos el botón. Sin embargo no es lo que queremos. 11: A esta clase (this) le agregamos un detector de eventos del tipo MouseEvent..UP_DOWN, para que la función cuando soltemos el botón del mouse que se ha presionado. 13: Definimos la funcionalidad de arrastrar que se activará en los textos del escenario, con la función privada “soltar”. Es privada porque los demás objetos no necesitan saber que es lo que pasa al arrastra y soltar. Declaramos la variable “e” la que almacena los datos que envía el detector de eventos. 15: Para arrastrar un objeto en el escenario ocupamos la funcion startDrag 17: Definimos la funcionalidad de soltar que se activará en los textos del escenario, con otra función privada llamada “soltar”. Declaramos la variable “e” la que almacena los datos que envía el detector de eventos. 19: Para soltar un objeto en el escenario ocupamos la función stopDrag. Ahora que hemos escrito el código base para nuestro juego de comparación, asociamos el archivo .fla con el archivo .as. Sin embargo debemos tener presente que ActionScript nos pedirá un nombre único de clase al asociar cada movieclip en el escenario. Si no hacemos esto nos arrojará un error. De modo que la manera correcta de vincular ambos archivos es: CREANDO UN JUEGO DE COINCIDENCIAS: detectando colisiones POO: Colisiones Lo que debemos tener en mente es el target u objetivo que debemos darle a cada texto, de manera que este coincida con su bandera correspondiente . Para esto necesitamos una variable pública para la clase txtBandera y así darle ciertas propiedades. Creamos el archivo txtBandera.as con el siguiente código: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 package { import.flash.display.MovieClip; import.flash.events.MouseEvent; public class txtBandera extends MovieClip { public var _acertar:MovieClip; public function txtBandera ( ): void { this.addeventListener( MouseEvent.MOUSE_DOWN, arrastrarlo); this.addeventListener( MouseEvent.MOUSE_UP, soltarlo); } private function arrastrarlo (e: MousEvent) : void { this.startDrag ( ); private function soltarlo(e: MousEvent ) : void { this.stopDrag ( ); } } Cometarios: 08: Declaramos una variable de nombre _acertar la que almacenará el objetivo actual al que estamos apuntando. Es una convención general que al crear una variable pública que le de propiedades a una clase, ésta comience con guión bajo. Definimos el tipo de variable que será MovieClip, por estar dirigida a alguna de las banderas. En este punto creamos un 2do archivo .as que servirá como una clase documento para nuestro juego y lo llamaremos “Puntaje.as”. De manera que linkeamos nuestro archivo .fla con la clase que recién crearemos: POO: Colisiones El código para la nueva clase documento Puntaje es: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package { import flash.display.MovieClip; import flash.events.MouseEvent; public class Puntaje extends MovieClip public function Puntaje():void { // acertar es la propiedad y Chile_mc el objetivo de txtChile_mc txtChile_mc._acertar = Chile_mc; txtEcuador_mc._acertar = Ecuador_mc; txtEmiratos_mc._acertar = Emiratos_mc; txtIndia_mc._acertar = India_mc; txtChile_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEcuador_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEmiratos_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtIndia_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); } private function resultado(e:MouseEvent):void { // mc1_mc.hitTestObject (mc2_mc) // Fórmula a ocupar: if (e.currentTarget.hitTestObject(e.currentTarget._acertar)) trace (” Aquí está el resultado...”); } } } POO: Colisiones Comentarios: 01: Comenzamos creando un paquete de clases 03-04: Importamos las clases que ocuparemos 06: Creamos clase pública Puntaje ( mismo nombre que el archivo .as) que usará la clase MovieClip, debido a que la línea de tiempo principal es básicamente un gran Movie Clip. 08: Dentro de la clase pública Puntaje creamos la función pública constructora con el mismo nombre. Esta función constructora almacena todos los datos necesarios para que el juego sea funcional. 11 al 14: Creamos un grupo de propiedades del tipo _acertar para cada bandera. Recordemos que en el archivo txtBandera.as, ya habíamos creado la variable _acertar, la que ahora será usada para cada bandera. 16 al 19: A los textos, le agregamos detectores de eventos del tipo MouseEvent.MOUSE_UP y definimos el nombre de la función “resultado” que recibirá los datos enviados por ellos. 22: Creamos la función privada “resultado” y declaramos la variable “e” que almacena los datos del tipo MouseEvent enviados por el detector. Cerramos con void para que no retorne valores 25:La función a ejecutarse corresponde a la formula mc1_mc.hitTestObject (mc2_mc) la que retornará valores true o false. Si mc1_mc toca a mc2_mc, el método retorna true; si no es así retorna false .Esta fórmula la colocaremos dentro de una declaración if, de modo que si el movieclip1 toca al movieclip2, algo debe pasar. De manera que en lugar de mc1_mc colocamos el actual Movie Clip que gatilla al detector de eventos ( txtChile_mc, etc), y la manera de hacerlo es usando la propiedad currentTarget. Reemplazamos los elementos correspondientes y tenemos: currentTarget.hitTestObject (e. currentTarget._acertar) en donde: currentTarget corresponde a txtChile_mc que es el Movie Clip que se arrastrará y soltará. Propiedad hitTestObject prueba si un objeto a colisionado con otro. Cuando este Movie Clip esté tocando a Chile_mc (e. currentTarget._acertar), el detector se activará. 26: Si el retorno es true, ejecute el trace ” Aquí está el resultado...” POO: Respuestas a colisiones CREANDO UN JUEGO DE COINCIDENCIAS: respondiendo a las colisiones. Lo que pretendemos en este capítulo, es hacer mas ineractivo nuestro juego, por lo que estudiaremos como aplicar la desaparición de un objeto cuando colisione con otro. Para esto en la declaración if de nuestro arhivo Puntaje.as. Para lograr lo pretendido reemplazamos la función trace por el método removeChild, y que su función es eliminar ciertos objetos definidos por nosotros en nuestro ejercicio, queremos que desaparezcan txtChile_mc y Chile_mc por lo que nuestro código sería: 01 02 03 04 05 if (e.currentTarget.hitTestObject(e.currentTarget._acertar)) { removeChild (e.currentTarget); removeChild (e.currentTarget._acertar); } Este pareciera el código correcto, sin embargo al correr la película, nos encontramos con el siguiente error: 1118: Conversión implícita de un valor con tipo estático Object a un tipo flash.display:DisplayObject posiblemente no relacionado. El objeto removeChild es el DisplayObject y e.currentTarget es la instancia de la clase Object .La razón del error es que flash está esperando cierto tipo de objeto pero obtiene otro, por lo que debemos definir más claramente el tipo de objeto que deseamos eliminar. Lo que haremos será convertir la instancia en un Movie Clip: 01 02 03 04 05 if (e.currentTarget.hitTestObject(e.currentTarget._hitTarget)) { removeChild (MovieClip (e.currentTarget)); removeChild (e.currentTarget._hitTarget); Ahora si probamos la película veremos que corre OK. Con un pequeño cambio en el tipo de easing usado, con el fin de mover el fondo, cada vez que pinchemos en el escenario: Pantalla de felicitaciones POO: 01 02 03 04 05 06 07 08 09 10 import fl.transitions.Tween; import fl.transitions.easing.*; stage.addEventListener(MouseEvent.MOUSE_DOWN,animacion); // stage puede reemplazarse por Chile_mc function animacion(e:MouseEvent):void { new Tween (mapa_mc, "x", Regular.easeOut, stage.x,mouseX,1,true); new Tween (mapa_mc, "y", Regular.easeOut, stage.y,mouseY,1,true); } CREANDO UN JUEGO DE COINCIDENCIAS: pantalla de felicitaciones. En este capítulo, crearemos una pantalla que aparezca cada vez que hemos completado las 4 banderas, para felicitarnos por completar el juego. Lo primero será crear un 3er layer en nuestro archivo 04_Banderas.fla en el cual dibujamos un rectángulo y enseguida con F8 lo convertimos en movieclip. Al hacerlo nos aseguramos de configurar su exportación para ActionScript: Echo esto, con Crtl + E , entramos al movieclip recién creado y comenzamos su edición, para que finalmente luzca asi: Pantalla de felicitaciones POO: Despés de esto, volvemos a nuestro escenario principal y borramos el layer recién creado que contiene la pantalla de felicitaciones, de modo que solo dispongamos de ella a través del movieclip guardado en biblioteca. Vamos ahora a nuestro archivo Puntaje.as al que agregaremos las siguientes lineas de código: Pantalla de felicitaciones POO: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 POO: Pantalla de felicitaciones Comentarios: 09: Declaramos la variable pública “miPuntaje” de tipo numérica y la seteamos por defecto en su valor mínimo 0. 30: Incrementamos el valor de “miPuntaje” en 1 cada vez que los removeChild de arriba se ejecuten. 31: Hacemos la comparación de elementos eliminados en el escenario, y cuando sea igual a 4 se ejecuta la función dentro de los paréntesis { } 32: Declaramos la variable “ganador” del tipo “Ganar” 33-34: Centramos el Movie Clip en el escenario. 35: Atachamos el Movie Clip de felicitaciones (el que resolvimos al comienzo) al escenario. Al probar la película vemos que al finalizar el juego, se abre la pantalla de felicitaciones. OK. De no haber coincidencia haremos que los textos vuelvan a su posición original. Para esto , en la función privada “resultado” de nuestro clase Puntaje.as, debemos agregar una declaración else 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 private function resultado(e:MouseEvent):void { if (e.currentTarget.hitTestObject(e.currentTarget._acertar)) { removeChild (e.currentTarget._acertar); miPuntaje++; var ganador:Ganar = new Ganar(); ganador.x = stage.stageWidth /2; = stage.stageHeight /2; addChild (ganador); } else { // función a ejecutar: que los textos regresen a su posición de origen si no coinciden con la bandera correspondiente. } Comentarios: POO: Pantalla de felicitaciones 12: Si if retorna false, entonces ejecute la función dentro de else. Sin embargo, debemos tener a mano un par de variables que ayuden a ejecutar esta sentencia else, por lo que debemos ir a la clase txtBandera.as y declararlas: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package { import.flash.display.MovieClip; import.flash.events.MouseEvent; public class txtBandera extends MovieClip { public var _acertar:MovieClip; // Declaramos las variables públicas inicioX e inicioY las que serán usadas por la clase Puntaje.as public var _inicioX:Number; // y que almacenan los valores iniciales de las coordenadas x,y public var _inicioY:Number; public function txtBandera ( ): void { this.addeventListener( MouseEvent.MOUSE_DOWN, arrastrarlo); this.addeventListener( MouseEvent.MOUSE_UP, soltarlo); } private function arrastrarlo (e: MousEvent) : void { this.startDrag ( ); private function soltarlo(e: MousEvent ) : void { this.stopDrag ( ); } } Otro aspecto a considerar es que estas variables recién declaradas, también se necesitan en la función pública Puntaje. El código luce asi: Pantalla de felicitaciones POO: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package { import flash.display.MovieClip; import flash.events.MouseEvent; import Ganar; public class Puntaje extends MovieClip private var miPuntaje = 0; public function Puntaje():void { txtChile_mc._inicioX = txtChile_mc.x; txtChile_mc._inicioY = txtChile_mc.y; txtEcuador_mc._acertar = Ecuador_mc; txtEcuador_mc._inicioX = txtEcuador_mc.x; txtEcuador_mc._inicioY = txtEcuador_mc.y; txtEmiratos_mc._acertar = Emiratos_mc; txtEmiratos_mc._inicioX = txtEmiratos_mc.x; txtEmiratos_mc._inicioY = txtEmiratos_mc.y; txtIndia_mc._acertar = India_mc; txtIndia_mc._inicioX = txtIndia_mc.x; txtIndia_mc._inicioY = txtIndia_mc.y; txtChile_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEcuador_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEmiratos_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtIndia_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); } private function resultado(e:MouseEvent):void { if (e.currentTarget.hitTestObject(e.currentTarget._acertar)) { removeChild (e.currentTarget._acertar); miPuntaje++; if (miPuntaje == 4) { var ganador:Ganar = new Ganar(); ganador.x = stage.stageWidth/2; ganador.y = stage.stageHeight/2; addChild(ganador); } } 44 45 46 47 48 49 50 51 else { e.currentTarget.x = e.currentTarget._inicioX; e.currentTarget.y = e.currentTarget._inicioY; } } } } POO: Aregando sonido Comentarios: Cuando el archivo se carga por 1ra vez, lo hace con los valores almacenados en las variables numéricas declaradas en la clase txtBandera.as (_inicioX, _inicioY). Por eso, si las coordenadas (x, y) del texto al comenzar la carga fueran (5, 8), estos serán los valores a los que volverá si no hay coincidencia con las banderas. De modo que: 19-20: Al correr el programa, los valores almacenados en las propiedades inicioX e inicioY de txtChile_mc (etc) se igualan (o cargan) con el valor registrado en las coordenadas x e y actual. 46-47: Si if retorna false se hace uso de este valor almacenado, y el texto vuelve a sus coordenadas originales. CREANDO UN JUEGO DE COINCIDENCIAS: agregando sonido. Finalmente agreguemos un sonido externo, el cual debe estar en la misma carpeta que el resto de los archivos: 01 02 03 04 05 06 07 08 09 10 11 package { import import import import import flash.display.MovieClip; flash.events.MouseEvent; Ganar; flash.media.Sound; flash.net.URLRequest; public class Puntaje extends MovieClip private var miPuntaje = 0; Aregando sonido POO: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 private var miSonido:Sound; public function Puntaje():void { txtChile_mc._inicioX = txtChile_mc.x; txtChile_mc._inicioY = txtChile_mc.y; txtEcuador_mc._acertar = Ecuador_mc; txtEcuador_mc._inicioX = txtEcuador_mc.x; txtEcuador_mc._inicioY = txtEcuador_mc.y; txtEmiratos_mc._acertar = Emiratos_mc; txtEmiratos_mc._inicioX = txtEmiratos_mc.x; txtEmiratos_mc._inicioY = txtEmiratos_mc.y; txtIndia_mc._acertar = India_mc; txtIndia_mc._inicioX = txtIndia_mc.x; txtIndia_mc._inicioY = txtIndia_mc.y; txtChile_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEcuador_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtEmiratos_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); txtIndia_mc.addEventListener(MouseEvent.MOUSE_UP, resultado); var solicitarSonido:URLRequest = new URLRequest (”blip.mp3”); miSonido = new Sound ( ); miSonido.load (solicitarSonido); private function resultado(e:MouseEvent):void { if (e.currentTarget.hitTestObject(e.currentTarget._acertar)) { removeChild (e.currentTarget._acertar); miPuntaje++; miSonido.play ( ); if (miPuntaje == 4) { var ganador:Ganar = new Ganar(); ganador.x = stage.stageWidth/2; ganador.y = stage.stageHeight/2; addChild(ganador); } } else { e.currentTarget.x = e.currentTarget._inicioX; e.currentTarget.y = e.currentTarget._inicioY; } 54 55 56 57 58 } } } POO: Agregando sonido Comentarios: 07-08: Ubicaciones de carpetas donde se encuentarn las clases que ocuparemos. 31: Para que el sonido se manifieste, declaramos la variable “solicitarSonido” y almacenamos un objeto de sonido dentro de ella. Dentro de la función constructora, que es la que ejecuta las acciones, creamos el objeto de sonido: Para ocupar un archivo externo, debemos crear una instancia para la clase URLRequest. Esta clase es usada cada vez que debamos acceder a una URL externa o archivo externo La manera de hacerlo es declarando la variable “solicitarSonido” del tipo URLRequest y creamos una nueva instancia u objeto la que va dentro de los paréntesis. 32: Creamos el objeto de sonido propiamente tal. En la línea 11 ya hemos almacenado el sonido en la variable “miSonido”; ahora la seteamos para crear una nueva instancia u objeto de sonido. 33: En la variable recién seteada, ocupamos el método load, y dentro de los paréntesis, invocamos el objeto solicitado. 40: Después de haber incrementado el valor del puntaje en 1, ubicamos la reproducción del sonido. Guía práctica Desarrollo de un juego de comparaciones con AS3 y paquetes Preparado por Fco. Rocco