Comunidad de diseño web y desarrollo en internet online

Compartir bibliotecas SWF externas con Actionscript y JSFL

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
    • 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.

Descargar Archivo

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?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate