Programando en AS3 desde Flash CS3 (II)

En el primer artículo de esta serie vimos que cuando escribimos código en el timeline o añadimos movieclips en el escenario éstos se acababan convirtiendo en parte de una clase.

Como esto sucede de manera transparente, quizá pensemos que no es necesario saber qué está pasando a nivel interno para utilizar AS3 desde Flash. Para los casos planteados en la primera parte esto se cumple, pero hay otros casos que ahora sí influyen en nuestra manera de trabajar.

Para ilustrar esta afirmación, vamos a centrarnos en uno de los casos más significativos: el por qué ha dejado de funcionar el parent en AS3.

El parent y el error 1119 en AS3

Utilicemos un ejemplo muy tonto para recrear este error. Tenemos dos animaciones en el escenario, cada una contenida en un movieclip, y queremos que al finalizar la primera ésta informe a la segunda para que se reproduzca. Algo así:

This movie requires Flash Player 9

Descargar ejemplo

Los nombres de los movieclips son bola_gris y bola_azul (que tiene un stop() en el primer frame para evitar que se reproduzca).  Para darle esta funcionalidad a la película nos metemos dentro de bola_gris y en el último frame de la animación añadimos el siguiente código:

[as3]stop();
parent.bola_azul.play(); //Ya no se utiliza más _parent
[/as3]

Algo tan simple, que llevamos toda la vida haciéndolo en anteriores versiones de flash, ahora en el compilador nos genera este críptico error:

1119: Access of possibly undefined property bola_azul through a reference with static type flash.display:DisplayObjectContainer.

Vamos con un poco de teoría que nos ayudará a entender qué está pasando.

Las clases dinámicas y las clases selladas

Una clase dinámica (dynamic class) es aquella que te permite añadir y modificar propiedades y métodos en tiempo de ejecución. Por el contrario, una clase sellada (sealed class) es aquella que sólo puede utilizar las propiedades y los métodos que se han definido en tiempo de compilación. Por ejemplo:

[as3]// MovieClip es una clase dinámica,
// no hay ningún problema en añadir ‘propiedad1’
var mc:MovieClip = new MovieClip();
mc.propiedad1 = “texto”;

// Sprite es una clase sellada, al intentar añadir
// ‘propiedad1’ el compilador nos dará un error
var sp:Sprite = new Sprite();
sp.propiedad1 = “texto”;
[/as3]

Un paseo por AS2

La clase dinámica más representativa es Object, que sirve precisamente para crear nuestro propio objeto y añadirle funcionalidades. En AS2, que es una fiesta, todas las clases son dinámicas, y no es extraño sabiendo que Object es la base de todas las clases de AS2.

A lo mejor te estás preguntando si realmente esto es cierto, ya que algunas dan errores si le añadimos alguna propiedad. Cojamos la clase Point de AS2 para hacer la prueba, que además tiene el atributo intrinsic (se encuentra definida en el flash player, Point.as sólo especifica las propiedades, métodos y datos para poder realizar la comprobación de tipos en el momento de la compilación):

[as3]import flash.geom.Point;
var a:Point = new Point(1,2);
a.propiedad1 = “texto”;
[/as3]

El compilador nos dice:

There is no property with the name ‘propiedad1’.

Ahora vamos a hacer lo mismo pero engañando al compilador:

[as3]import flash.geom.Point;
var a:Point = new Point(1,2);
a[propiedad1] = “texto”;
[/as3]

Además de dejarnos compilar, si probamos a hacer un trace(a[propiedad1]) nos devuelve “texto”.

Como ya comenté al final del primer artículo, AS2 no deja de ser un envoltorio de AS1 que añade sintaxis para trabajar de una manera más orientada a objetos. Las clases que nos dan error, como Point, simplemente comprueban a la hora de compilar, pero en tiempo de ejecución todas son tipadas dinámicamente.

Volvamos de nuevo a AS3 y al parent

En AS3 es justo al revés, por defecto todas las clases son selladas, aunque se pueden utilizar como dinámicas añadiendo el atributo dynamic en la definición.

Si buscamos en la referencia de AS3 información sobre parent, vemos que es una propiedad de DisplayObject y que devuelve un DisplayObjectContainer. Cuando utilizamos la propiedad parent en bola_gris, lo único que sabe el compilador sobre el padre de bola_gris es que es del tipo DisplayObjectContainer, puede que sea un sprite o un movieclip (como realmente es el caso), pero el compilador no tiene manera de saberlo, lo único que nos puede garantizar es que se trata de un DisplayObjectContainer ya que bola_gris es un hijo. DisplayObjectContainer es una clase sellada, de ahí que genere error cuando intentamos añadir una propiedad que el compilador desconoce.

1119: Access of possibly undefined property bola_azul through a reference with static type flash.display:DisplayObjectContainer.

Efectivamente, bola_azul no es una propiedad que defina la clase DisplayObjectContainer. Conocida la causa, el error ya no es tan críptico…

Como movieclip es una clase dinámica que sí permite añadir propiedades, y como nosotros sí que podemos garantizar que el padre de bola_gris es un movieclip, podemos ayudar al compilador indicándoselo de la siguiente manera:

[as3]MovieClip(parent).bola_azul.play();
[/as3]

¡Ahora sí nos deja! Esto es lo que se conoce en programación como casting, que es proveer al compilador de información de un objeto cuando no puede determinarla correctamente. También se utiliza el casting para forzar la conversión entre tipos primitivos. Podemos utilizar el casting de dos maneras, en este caso idénticas:

[as3]MovieClip(this.parent.parent).mc.alpha = .5;
// o también
(this.parent.parent as MovieClip).mc.alpha = .5;
[/as3]

El casting no sólo es necesario con el parent, también será necesario si queremos utilizar el root o al cargar un swf con el objeto Loader, para indicarle al compilador qué tipo estamos cargando (un sprite o un movieclip, por ejemplo). En general, siempre que queramos recorrer la DisplayList de manera manual nos hará falta.

Desactivando el modo estricto

Me gustaría hacer hincapié en el hecho que no hay ningún problema con el código: existe un padre movieclip y un hijo movieclip, con lo que hacer el parent es correcto. El único problema es que el compilador no lo sabe y no nos deja avanzar.

Flash CS3 tiene por defecto activado lo que se conoce como Strict Mode. En modo estricto, los errores de tipo se resuelven tanto en modo de compilación como en modo de ejecución. Si queremos volver a la flexibilidad de AS2 en este sentido, podemos desmarca la opción, que se encuentra en Publish Settings > ActionScript Settings:

AS3 Settings - Strict Mode

Una vez ignorada la comprobación de tipos en compilación, en runtime el código del principio no generará ningún error:

[as3]// Con Estrict Mode desactivado el código no falla
parent.bola_azul.play();
[/as3]

Evidentemente, es aconsejable no desactivar este modo, ya que nos provee de un mecanismo para detectar fallas en nuestro código, y al obligarnos a tipificar las variables y objetos aumenta el rendimiento del programa. Cuanta más información tenga el compilador, mejor.

Utilizando eventos para comunicarse

Siendo puristas, utilizando código tipo

[as3]Movieclip(parent.parent).miVariableInit = true;
Object(parent.parent.parent).miObjeto.hazEstaAccion();
[/as3]

estamos rompiendo uno de los principios básicos de la OOP: la encapsulación. Este código genera dependencias de ruta y en programas muy grandes puede llegar a ser muy difícil de mantener.

Para ello podríamos utilizar el nuevo modelo de eventos de AS3, dispachando eventos a llegar un punto concreto de la aplicación que otros objetos podrán escuchar para actuar en consecuencia.

Nota: no voy a profundizar en el modelo de eventos porque se podría alargar bastante, y el artículo ya está quedando algo extenso. De momentos nos basta con saber que podemos utilizar otra opción más.

Al final, ¿qué manera es la mejor?

Nunca hay una manera necesariamente mejor, siempre hay que buscar un equilibrio entre el nivel del desarrollador y los requisitos. Yo distinguiría entre tres tipos de situaciones:

  1. Un programador de cualquier nivel trabajando con Flash CS3 debería intentar siempre “castear” la información.
  2. Si el proyecto es sencillo y el peso del programa debe correr a cargo de un diseñador/animador, podemos considerar desactivar el Strict Mode. La flexibilidad de flash es lo que ha hecho grande a este programa, tampoco hay que penalizar ahora a los que no programan.
  3. Si es un proyecto grande y corre a cargo de programadores experimentados, en muchas situaciones puede ser conveniente crearse un modelo de eventos en vez de recorrer manualmente la DisplayList. Aunque al principio es más costoso, los beneficios en mantenimiento lo compensan sobradamente.

Resumiendo

Las clases en AS3, por defecto, son selladas. Con el modificador dynamic podemos cambiar esto.

Flash CS3 utiliza por defecto un modo estricto que comprueba las variables tanto en tiempo de compilación como en tiempo de ejecución. Se puede desactivar pero no es recomendable.

Con este nuevo modo, para poder recorrer la DisplayList manualmente como lo veníamos haciendo hasta ahora, debemos hacer un cast de los objetos, así evitaremos los errores de compilación.

El hecho de que un objeto esté “casteado”, no implica que sea correcto. Al hacer un casting le decimos al compilador que nosotros asumimos el tipo del objeto. Sino lo hacemos correctamente, obtendremos un error en tiempo de ejecución.

Información del artículo

Post publicado el 01 de December de 2007 a las 22:01 por llops

Categorias: Artículos

Etiquetas: , , , ,

Comparte

22 comentarios

  • Aunque cambiaría la nomenclatura de los movieclips para evitar confusión (bola_gris por bolagris_mc) está muy bien explicado, sobre todo el casting.
    Espero que sigas con esta serie.

  • Hola Comet, la verdad es que siempre utilizo la nomenclatura “_mc”, pero aquí mira…
    Creo que aún escribiré algún artículo más de esta serie ;)

    Un saludo!

  • j.mat

    La verdad es que está bien explicado, pero no acabo de entender cómo es posible que el compilador no sepa que estás utilizando un movieclip y tengas que decírselo con el casting…

  • El compilador lo único que hace es evaluar código, no es consciente si tú has cogido un objeto y lo has convertido en MovieClip mediante F8 o lo has convertido en Sprite a través de una instrucción. Sólo puede asegurarnos que pertenece a la clase DisplayObjectContainer por tener contenido.

    Además, en as3 es posible reparentar cualquier objeto en la DisplayList (sin tener que destruirlo y crearlo de nuevo, sólo moviéndolo). ¿Qué pasaría si limitara los objetos directamente a MovieClip? No se podría reparentar un Sprite, por ejemplo, ya que generaría error.

  • :D muchas gracias por estos articulos son de mucha ayuda, estaré esperando los próximos muy atento!

  • Billy Rippe

    No te imaginas lo que luche con este error, llevaba dos dias sin saber que pasaba y estaba muy perdido… no sabia por donde abordarlo… fue tanta la frustracion que cuando sali de la oficina los ojos se me aguaron…
    cuando mande la pelicula y funciono… logre respirar cosa que no habia hecho desde ayer por la manana… no se como sobrevivi… Mil gracias

  • Felicidades Llops. De verdad.
    Gracias por el tiempo dedicado a escribir.

  • Estaré atento a un futuro artículo sobre el modelo de eventos de AS3.
    Saludos! ;)

  • Eliseo

    Sólo como comentario a variables de la película principal desde una clase.
    Si tenemos algo como
    [as3]
    //Clase Pelota que no funciona
    package {
    public class Pelota extends MovieClip {
    function Pelota() {
    MovieClip(parent).variable=5
    }
    }
    }
    [/as3]
    NO va a funcionar, si lo que queremos es añadir “Pelotas” con algo como
    [as3]
    //Nuestra película principal
    package {
    public class Main extends MovieClip {
    public variable:int
    function Main() {
    for (var i:int=0;i<30;i++){
    var pelota:Pelota=new Pelota();
    addChild(pelota);
    }
    }
    }
    }
    [/as3]
    Debido a que la Pelota se crea cuando hacemos el new y todavía no tiene padre. Aunque sí funcionará si no estamos añadiendo dinámicamente los Objetos.

    Para evitar esto, debemos usar el evento ADDED_TO_STAGE
    [as3]
    //Clase Pelota
    package {
    public class Pelota extends MovieClip {
    function Pelota() {
    addEventListener(Event.ADDED_TO_STAGE,initPelota,false,0,true);
    }
    public function initPelota(){
    MovieClip(parent).variable=5
    }
    }
    }
    [/as3]

    NOTA:Gracias por estos aportes que has hecho

  • Hola Eliseo, tienes toda la razón.
    Escribí sobre el tema (pero enfocado a la inicialización de eventos) en este artículo:
    2008/02/18/accediendo-al-stage-antes-de-tiempo/

    Un saludo!

  • k_pqW

    No m… Jajaja, wow! ke alegria… llevaba tres dias (literal) tratando de solucionar ese error. Uffff… ¡¡Te amooooo!!!

  • Varias veces eh tenido problemas de este tipo trabajando en AS3, además ahy poca información sobre casteo. Muy buen artículo!!!…me es más cómodo programar con el Flash develop :)

  • Bribon

    Hola,
    este tutorial me ha parecido muy interesante. Yo soy programador, pero no tengo mucha experiencia en AS3, pero si que la tengo en Java. Entonces, quiero aprovechar mis capacidades. ¿No sabras algun tutorial donde explique bien la manera de moverte por el DisplayList a traves de eventos? Da igual en ingles, que en español…

    Un saludo

  • Hola Bribon,
    el siguiente artículo te puede ayudar. Está muy muy bien.
    http://developer.yahoo.com/flash/articles/display-list.html

  • Mil gracias por esta Informacion, creo que lo mas importante es lo del strict mode, ya que los disenadores y animadores que trabajamos con flash no nos interesan tantos formalismo del codigo.
    [as3]gotoAndPlay(“aprender”);[/as3]

  • aabbcc

    a parte del tema de tener que adaptarse ade AS2 a As3 (que a primera vista parece más eficiente) existe algun problema con compativilidad de navegadores, pluggin flash,….. del As3 frente al AS2?

  • No tendrás problemas de compatibilidad entre navegadores, pero sí que necesitarás una versión específica de plugin para que puedas correr AS3 (a partir del player 9), pero hace tanto que salió que ya no supone ningún inconveniente.

    Lo más “problemático” con las nuevas versiones del player es que cada vez son más restrictivas con los temas de seguridad y ciertas tareas ya no son tan fáciles como antes.

  • Marcnexus

    Enhorabuena por el artículo. Me ha aclarado una situación que se me da con frecuencia y de la que desconocía su porqué. Te estoy muy agradecido.

    Un saludo

  • GRACIAS por los articulos el anterior esta muy bien este tambien, voy a checar el III de nuevo muchas gracias por este tipo de articulos, espero una vez apredido algo interesante sobre AS3 me gustaria colaborar montanto un Articulo si es posible ya lo checare mas tarde….

  • Edna

    4 años después de publicado el artículo y ha salvado a alguien más: a mí! jejeje
    Mil gracias por la información. Ojalá los que hacen la documentación de Adobe lo hicieran tan simple.
    Saludos!!

  • Gracias Edna. Mola leer comentarios como este después de tanto tiempo :)

  • fercho

    todo era mas facil y rapido en as2 … si si si .. lo se….