Antes de empezar este tutorial ten en cuenta que abarca muchos aspectos distintos de trabajo con Flash, como DataBinding, trabajo con XML y creación de comandos JavaScript para flash.
El objetivo final es crear una separación completa entre los elementos gráficos y el código. Y que se puedan ir añadiendo distintos items sin tener que cambiar una sóla línea de código.
En este tutorial crearemos un caballero al que se le puedan añadir todas las espadas en una mano y los escudos en la otra. Estos serán símbolos que estén en la biblioteca de un archivo externo. Lo haremos guardando en un xml los datos de la biblioteca de flash, y posteriormente recuperándolos desde la aplicación base.
Creando un comando para flash
Crear extensiones para Flash no es difícil si sabes ActionScript. Se utiliza Javascript para crearlas y las clases Array, Object y String se manejan casi igual (aunque le faltan algunas cosas, como Array.sortOn), las estructuras de condiciones y funciones son idénticas, aunque no existe el "strict data typing" (lo de definir una variable poniendo :Tipo al final). Para realizar acciones sobre el IDE de Flash, no tenemos más que mirar la referencia que viene en la ayuda de este programa (al presionar F1), en el libro llamado "Ampliación de Flash". Allí vemos que fl.getDocumentDOM() se refiere al documento en el que estamos trabajando actualmente, library.items es el Array que contiene todos los elementos de la biblioteca (objetos del tipo Item), y FLfile.write es la acción para escribir en el disco duro.
También hay que decir que Item.name devuelve una cadena como carpeta1/carpeta2/nombreSimbolo, siendo carpeta1 y carpeta2 las carpetas, objetos Item de tipo (itemType) "folder", en las que está contenido el símbolo. Esto no nos interesa para un XML donde podemos definir nuestras propias "carpetas".
Una vez dicho esto, aquí está el código con la explicación en los comentarios:
//Función para reemplazar cadenas de texto function strReplace(busca, sust, cadena) { return cadena.split(busca).join(sust); } // // //Función para ordenar el array por el parámetro name de los objetos, ya que no hay //una función sortOn function nameSort(i, j) { //Estas funciones tienen que tener una sintaxis como esta en la que se devuelva //-1 si un objeto se considera menor que otro y 1 si se considera mayor. // ">" y "<" se refieren al orden alfabético cuando comparan cadenas. return (i.name<j.name ? -1 : 1); } // // // //Cojo la ruta del archivo, convierto los \ en /, para que se pueda leer var URI = strReplace("\\", "/", fl.getDocumentDOM().path); //le doy nombre y añado file:/// para que pueda pasarse como parámetro de Fl.write URI = "file:///"+URI.slice(0, URI.lastIndexOf("."))+"_LibraryData.xml"; fl.trace(URI); // // // //Empiezo a formar el xml // "\n" es el carácter que indica salto de línea var str = '<?xml version="1.0" encoding="utf-8" ?>\n<libraryItems>'; // // // //Calculo la cantidad de objetos en la biblioteca var ln = fl.getDocumentDOM().library.items.length; //Copio la biblioteca a otro array para poder trabajar mejor var lib = fl.getDocumentDOM().library.items; //Ordeno el array por los nombre, con la función que definí arriba lib.sort(nameSort); var currentFolder = ""; var folders=new Array(); // // // //Con cada item de la biblioteca... for (i=0; i<ln; i++) { var indSmb = lib[i]; //Creo un array con la ruta (las carpetas van separadas por /) y el nombre var pat = indSmb.name.split("/"); var patLen = pat.length; //Si ya no estamos en la misma carpeta que en la recursión anterior... if (currentFolder != pat[patLen-2] && currentFolder != "") { //cerramos la etiqueta de la carpeta anterior str += '\n</'+currentFolder+'>'; //la carpeta anterior es la carpeta padre folders.pop(); currentFolder=folders[folders.length-1]; } //Si el símbolo es una carpeta... if (indSmb.itemType == "folder") { //Se convierte en la carpeta actual currentFolder = pat[patLen-1]; folders.push(currentFolder); //Abrimos la etiqueta str += '\n<'+currentFolder+'>'; // // // //Si no... } else { //A cada elemento parseo lo atributos que interesan str += '\n<item name="'+pat[patLen-1]+'" type="'+indSmb.itemType+'" linckageName="'+indSmb.linkageIdentifier+'" />'; } } //Cierro las etiquetas que faltan for(i=folders.length;i>0;i--){ str += '\n</'+folders[i-1]+'>'; } //Cierro el nodo y escribo la cadena en el archivo str += '\n</libraryItems>'; FLfile.write(URI, str);
Guardamos el código con el nombre de XML-Lybrary.jsfl en la carpeta de comandos de Flash, que por defecto es:
C:\Documents and Settings\USUARIO\Configuración local\Datos de programa\Macromedia\Flash 8\es\Configuration\Commands
Para probarlo, abrimos cualquier archivo de flash con una biblioteca ya hecha vamos al menú Comandos>XML-Lybrary.
Saldrá la ruta del xml en el panel de Salida. La introducimos en cualquier navegador y deberiamos ver un xml que representa todos los símbolos de la biblioteca.
Creando el archivo de la biblioteca
Conviene leer este apartado, pero si no quieres andar dibujando, bájate el fla aquí
Lo único que tendremos que hacer es crear un fla con el escenario vacío, y una biblioteca con los clips de distinto tipo separados en carpetas. Así crearemos una carpeta "assests", donde colocaremos todo lo que necesitemos para dibujar, una carpeta "swords" y otra "shields" para las espadas y los escudos, que tendrán que tener nombre de vinculación y estar exportados para AS. La última carpeta será "character", donde pondremos al caballero, cuya estructura mínima será la siguiente:
- Clip principal
- brazo1
- mano
- brazo1
- brazo2
- mano
El caballero bien podría ir en otra biblioteca o incluso en el archivo principal, ya que no es nada extensible.
Una vez tengas esto, guarda el archivo como graphics.fla, exporta el archivo a swf y ejecuta el comando XML-Lybrary.
Si en la carpeta en la que lo guardaste hay un archivo llamado graphics_lybraryData.xml entonces todo estará bien.
Mostrando la biblioteca con el archivo output.fla
Se trata de que aparezca el caballero centrado y con un par de comboboxes se puedan elegir la espada y el escudo que se desea mostrar. El primer inconveniente con el que nos encontraremos es el de acceder a la biblioteca del archivo cargado. El comando attachMovie sólo busca clips en el archivo desde el cual se le ha llamado. No obstante hay una manera de solvenar esto, crear una función que realice el attachMovie desde el propio clip. Y para hacer esa función en todos los clips que haya tenemos MovieClip.prototype Así que crearemos un archivo llamado "carga.as" en el que simplemente colocaremos este código:
//Función que permite attachear clips de las bibliotecas de sws cargados en otro principal MovieClip.prototype.attachClip = function(name, newName, depth) { this.attachMovie(name, newName, depth); };
Una vez hecho esto, ya podemos crear la interfaz principal, que constará únicamente de dos instancias de ComboBox swords_cb y shields_cb. Para alimentar los combos, podemos crear una función para parsear el xml, pero es mucho más eficaz usar un XMLConnector, que arrastramos al escenario y le damos el nombre de xConn. Como parámetro URL ponemos "graphics_LibraryData.xml" y en direction "receive". Arrastramos también un DataHolder, para cargar al caballero y lo llamamos dtHolder.
Ahora vamos con las vinculaciones. Abrimos el panel de Inspector de Componentes (Alt+F7), seleccionamos el XMLConnector, y en la pestaña "Esquema" hacemos click en results y en el botón de arriba a la derecha, que dice "Importar esquema de Muestra". Seleccionamos el xml de los gráficos graphics_LibraryData.xml y hacemos click en Abrir. Ahora deberíamos ver algo como esto:
Vamos al DataHolder y en "Esquema", damos al segundo botón de la izquierda "Crear un campo bajo el seleccionado". En "field name" ponemos "character" y en "data type", Array
Volvemos al XMLConnector y vamos a la pestaña Vinculaciones. Creamos tres de la siguiente manera:
Vincular | Bound To |
---|---|
results.lybraryItems.swords.item | swords_cb:DataProvider |
results.lybraryItems.shields.item | shields_cb:DataProvider |
results.lybraryItems.character.item | dtHolder:data.character |
Y ahora, en el primer fotograma colocamos este código:
//incluimos la función de carga #include "carga.as" //Creamos un objeto MovieClipLoader y un clip centrado donde cargamos el caballero var topClip:MovieClip = this.createEmptyMovieClip("topClip", this.getNextHighestDepth()); topClip._x = Stage.width/2; topClip._y = Stage.height/2; var mcLoader:MovieClipLoader = new MovieClipLoader(); //Creamos un objeto detector var listener:Object = new Object(); mcLoader.addListener(listener); // // // //Decimos a los comboboxes que muestren la propiedad name de sus dataProvider swords_cb.labelField = shields_cb.labelField="name"; //Las variables que apuntan a los clips donde se cargarán los objetos var rightHand:MovieClip; var leftHand:MovieClip; //definimos un listener swords_cb.addEventListener("change", listener); shields_cb.addEventListener("change", listener); // // // //Cargamos el xml con el componente XMLConnector xConn.trigger(); //Función que se ejecutará al cargarse el xml var xResult:Function = function (comp) { //Cargamos el clip principal mcLoader.loadClip("graphics.swf", topClip); }; xConn.addEventListener("result", xResult); // // // //Cuando ya hemos cargado el xml y el clip... listener.onLoadInit = function() { //Cargamos el caballero de la biblioteca char = dtHolder.data.character.attributes; topClip.attachClip(char.name, char.linckageName, 0); //Creamos diversos apuntadores rightHand = topClip[char.name].brazo1.mano; leftHand = topClip[char.name].brazo2.mano; sword = swords_cb.selectedItem.linckageName; shield = shields_cb.selectedItem.linckageName; //cargamos los clips rightHand.attachClip(sword, sword, 0); leftHand.attachClip(shield, shield, 0); }; // // // //Cuando cambiamos algún combo... listener.change = function(cb) { //Dependiendo de si cambiamos el de espadas o escudos, se attachea un clip u otro switch (cb.target) { case swords_cb : rightHand.attachClip(cb.target.selectedItem.name, cb.target.selectedItem.linckageName, 0); break; case shields_cb : leftHand.attachClip(cb.target.selectedItem.name, cb.target.attributes.linckageName, 0); } };
Conclusiones
Flash ofrece una herramienta única para muchos profesionales dedicados a distintos ámbitos, por lo que, separar el diseño y el desarrollo es fundamental, y ésta es una buena manera de conseguirlo. Además, probablemente la mayor utilidad sea en cualquier tipo de proyecto que requiera ir añadiendo elementos gráficos paulatinamente, como un motor de juegos.
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
Publica tu comentario
El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro
Entra al foro y participa en la discusión
o puedes...
¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?
Inicia sesión
¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?
Registrate