Archive for the ‘actionscript_3’ Category

Clase Delay para retardar acciones en Actionscript 3

Viernes, Febrero 19th, 2010

-
Es muy usual que debamos parar o ejecutar funciones con cierto delay, y si bien AS3 tiene una clase para eso (Timer), no es la cosa más eficiente, porque si quiero por ejemplo controlar un delay simple debo:

1. Crear el Timer
2. Asignar el Listener
3. Crear la función

Lo cual genera mucho código en algo que puede ser usado varias veces dentro de una pelicula. Para eso cree este clase Delay, que es sencilla pero ahorra algunos dolores de cabeza:

Código :

/*
Versión Estable 1.0
Clase Delay se encarga de crear un Evento Timer controlado para ejecutar cualquier función pasada como parametro. 
*/ 

/**
 * Clase Delay
 * @author Hernán
 */ 

 package clases {
   
   import flash.events.*;
   import flash.utils.Timer; 

   public class delay {
       
      private var launcher:Function;       
      private var time:Number;
      private var times:Number;       
      private var pause:Boolean;
      private var delayed:Timer; 

      function delay(Func:Function,Time:Number,Times:Number) {
         launcher = Func;
         time = Time;
         times = Times;
         pause = false;
         init();
      }
       
      private function init():void {
         delayed = new Timer(time,times);
         delayed.addEventListener(TimerEvent.TIMER, launch);
         delayed.start();   
      }
       
      private function launch(e:Event):void {
         if(!pause){
            launcher();
         }
      }
       
      public function stopTime():void {
         pause = true;
      }
       
      public function restartTime():void {
         pause = false;
      }
       
      public function cancel():void {
         delayed.removeEventListener(TimerEvent.TIMER, launch);
      }             
   }
}

Modo de Uso:

Código :

var delayer:delay = new delay(funcion,tiempo,cantidad_de_veces);

Además tenemos algunos métodos más que son:

stopTime() que nos permite pausar un rato el Timer
restarteTime() que nos permite volver a empezar (Quitar el stop)
cancel() que nos permite eliminar el Timer

Espero les sirva!

Saludos, Hernán . -

Enviar comentario

Clase de actionscript 3 para el manejo de fechas

Jueves, Octubre 8th, 2009

Esta es una pequeña clase que hice para manejar las fechas, saber la diferencia, cual es mayor y viceversa.

Código :

package
{
   public class dateUtils
   {      
      static public const SENCONDS:uint = 2;
      static public const MINUTES:uint = 3;
      static public const HOURS:uint = 4;      
      
      /**
       *
       * @param   date1 fecha a comparar
       * @param   date2 fecha a comparar
       * @param   unit Unidad en que se quiere devolver la diferencia.
       * @return  Number Con la diferencia en la unidad pedida
       */
      static public function dateDiff( date1:Date, date2:Date, unit:uint):Number
      {
         var _divisor : Number = 1000*(unit==dateUtils.SENCONDS?1:(unit==dateUtils.MINUTES?60:(unit==dateUtils.HOURS?(60*60):(60*60*60))));
         var _dif:Number = (date1.getTime()-date2.getTime()) / ( _divisor );
         return Math.abs(_dif);      
      }
      
      /**
       * Devuelve la menor de las fechas pasadas por parametro.
       * @param   ...args Arreglo que contiene todas las fechas pasadas por parametro
       * @return Fecha menor.
       */
      static public function minorDate( ...args ):Date
      {
         args.sort( _orderDatesFunction );
         return args[0];
      }
      
      /**
       * Devuelve la mayor de las fechas pasadas por parametro.
       * @param   ...args Arreglo que contiene todas las fechas pasadas por parametro
       * @return Fecha mayor.
       */
      static public function mayorDate( ...args:Array ):Date
      {
         args.sort( _orderDatesFunction );
         return args[ args.length-1 ];
      }
      
      /**
       * Funcion para comparar 2 fechas. 'Usado en funcion sort del array'
       * @param   date1
       * @param   date2
       * @return
       */
      static private function _orderDatesFunction( date1:Date, date2:Date ):Number
      {
         if( date1 > date2 ) return 1;
         else if(  date1 < date2 ) return 0;
         else return -1;
      }
   }
}

y para utilizarla es igual de simple.

Código :

/** Hallando la diferencia entre 2 fechas **/
var fecha1:Date = new Date("2009/2/2 2:01 PM");
var fecha2:Date = new Date("2009/2/2 3:31 PM");

// Diferencia en minutos
trace( dateUtils.dateDiff( fecha1, fecha2, dateUtils.MINUTES );
// Diferencia en horas
trace( dateUtils.dateDiff( fecha1, fecha2, dateUtils.HOURS );

/** Hallando la menor y la mayor de todas las fechas **/
var fecha3:Date = new Date("1982/9/9 2:00 AM");
var fecha3:Date = new Date("1972/9/9 4:00 PM");

var arreglo_de_fechas:Array = new Array( fecha1, fecha2, fecha3, fecha4 );
// La menor
trace( dateUtils.minorDate( arreglo_de_fechas ).toString() );
// La mayor
trace( dateUtils.mayorDate( arreglo_de_fechas ).toString() );

Espero le sirva de utilidad a mas de uno por ahí.

Enviar comentario

Obtener informaci�n de la direcci�n URL desde Actionscript 3

Miércoles, Mayo 27th, 2009

En este Tip les dejo una clase que encontr� el otro d�a perdida por el disco r�gido (y que justo andaba necesitando algo igual). Se trata de una clase que se encarga de proveer a Flex datos provenientes de la URL de la aplicaci�n, o sea, la url, el path relativo, el puerto, etc.

Código :

package com.alfathenus.core.utils
{
   import flash.external.ExternalInterface;

   import mx.core.Application;
   import mx.collections.ArrayCollection;
   import mx.controls.Alert;
   /**
    *
    * Clase que maneja el window del browser de la aplicacion
    *
    */
   public class HTTPUtil
   {
      //--------------------------------------------------------------------------
      // Constructor
      //--------------------------------------------------------------------------
      /**
       *
       * Construcgor
       *
       */
      public function HTTPUtil()
      {
      }
      /**
       *
       * Me devuelve la url de la p�gina
       *
       */
      public static function getUrl():String
      {
         return ExternalInterface.call( "window.location.href.toString" );
      }
      /**
       *
       * Me devuelve el host de la pag
       *
       * @example: http://www.flexpasta.com/?x=1&y=2 => me devuelve => www.flexpasta.com
       *
       */
      public static function getHostName():String
      {
         return ExternalInterface.call( “window.location.hostname.toString” );
      }
      /**
       *
       * Me devuelve el protocolo (http:, https:, etc)
       *
       */
      public static function getProtocol():String
      {
         return ExternalInterface.call( “window.location.protocol.toString” );
      }
      /**
       *
       * Me devuelve el puerto de la aplicaicon
       *
       */
      public static function getPort():String
      {
         return ExternalInterface.call( “window.location.port.toString” );
      }
      /**
       *
       * Me devuelve el path relativo a la aplicacion
       *
       * @example http://www.ejemplo.com/test?x=1&y=2 => devuelve => /test
       */
      public static function getContext():String
      {
         return ExternalInterface.call( “window.location.pathname.toString” );
      }
      /**
       *
       * Me devuelve el valor de un par�metro del QueryString
       *
       */
      public static function getParameterValue(key:String):String
      {
         var value:String;
         var uparam:String = ExternalInterface.call( “window.location.search.toString” );

         if(uparam == null)
         {
            return null;
         }
         var paramArray:ArrayCollection = new ArrayCollection( uparam.split( ‘&’ ) );
         for(var x:int = 0; x < paramArray.length ; x++)
         {
            var p:String = paramArray.getItemAt( x ) as String;
            if(p.indexOf( key + ‘=’ ) > -1)
            {
               value = (p.replace( (key + ‘=’) , ” )).replace( ‘?’ , ” );
               x = paramArray.length;
            }
         }

         return value;
      }
   }
}

Se aceptan como siempre sugerencias para su mejora.

Saludos!

Enviar comentario

Listar archivos de un directorio web con PHP y XML

Lunes, Octubre 27th, 2008

Este es un sencillo script de PHP que nos permite generar un archivo XML con el listado de todos los archivos y carpetas que esten contenidos dentro del directorio en el que hemos situado el archivo PHP.

Esto nos puede resultar útil para realizar galerías fotográficas en las que queremos acceder a todas las imágenes de un directorio y queremos poder subir o borrar archivos. El archivo XML siempre estará actualizado a los ficheros del servidor.

El archivo PHP es el siguiente. Lo llamaremos list.php y lo subiremos al servidor dentro de la carpeta que nos interese listar.

Código :

<?php
   $dir = "./";
   
   echo "<XML encoding='UTF-8' standalone='yes' version='1.0'>n";
   echo "  <folder>n";
   
   if (is_dir($dir))
   {
       if ($gd = opendir($dir))
      {
           while (($archivo = readdir($gd)) !== false)
         {
            if ($archivo != "." & $archivo != ".." & $archivo != "list.php")
            {
               if (is_dir($archivo))
               {
                     echo "    <folder>$archivo</folder>n";
               }
               else
               {
                     echo "    <file>$archivo</file>n";
               }
            }
           }
           closedir($gd);
       }
   }
   
   echo "  </folder>n";
   echo "</XML>n";
?>

El script lo único que hace es recorrer todos los elementos de la carpeta en la que está situado y a base de "echos" formatea el XML.

Si visualizamos este archivo desde el navegador y miramos el código resultante este sería un archivo XML de este estilo:

Código :

<XML encoding='UTF-8' standalone='yes' version='1.0'>
  <folder>
    <file>img_001.jpg</file>
    <file>img_002.jpg</file>
    <file>img_003.jpg</file>
    <file>img_004.jpg</file>
    <file>img_005.jpg</file>
    <folder>thumbs</folder>
    <folder>hires</folder>
  </folder>
</XML>

De esta manera si quisiésemos, por ejemplo, listar el contenido de imágenes disponibles de este directorio dentro de un componente List de Flash haríamos lo siguiente:

Código :

import fl.data.DataProvider;

var _URLLoader = new URLLoader();
var _URLRequest = new URLRequest();

_URLRequest.url = "/img/list.php";
_URLLoader.load(_URLRequest);
_URLLoader.addEventListener(Event.COMPLETE, onListComplete);

function onListComplete(e:Event):void
{
   var xml:XML = new XML(e.target.data);
   var xmlList:XMLList = xml.folder.file;
   var dataProvider = new DataProvider();
   for each (var item in xmlList)
   {
      dataProvider.addItem({label:item});
   }
   listado.dataProvider = dataProvider;
}

Cargamos el archivo PHP como si de un XML se tratase, y de ahí lo convertimos en el dataProvider del componente.

Enviar comentario

Abrir y guardar archivos de manera usable en AIR

Domingo, Octubre 12th, 2008

Una de las grandes virtudes de Adobe AIR es ser multiplataforma. Sin embargo, esto hace que cosas como el manejo de los diálogos del sistema de archivos tengan que adaptarse a Windows, Mac y Linux, y no ofrezcan tantas funciones como sería de esperar. Por ejemplo, cada vez que abrimos un diálogo para abrir o guardar un archivo, por defecto se abre en la localización en que dejamos la ventana anterior (si le damos un objeto File nuevo, y sin ruta definida), aunque eso no siempre sea lo más conveniente.

Por ejemplo, imaginemos que estamos haciendo algún tipo de editor, que cargue archivos fuente (digamos, por ejemplo, archivos txt), y genere algún tipo de archivo de salida (como un jpg en el que insertamos el texto que hemos cargado). No nos interesa que al abrir el diálogo de guardar archivo tengamos que encontrar la carpeta donde dejamos la salida (pongamos D:/trabajos/jpgtxt/ ), y cada vez que queremos cargar tengamos que buscar el directorio de las fuentes (que puede ser C:\Documents and Settings\usuario\Mis documentos\fuentes), desde el de las salidas.

Para solucionar esto, haremos una clase que se encargue de memorizar mediante SharedObjects las rutas en las que guardamos los diferentes archivos, y de paso, un par de clases que encapsulen el proceso de cargar y guardar datos desde los diálogos del sistema, ya que realmente lo único que nos interesa es cargar un ByteArray y guardar un ByteArray, ya que al fin y al cabo, son estos objetos los que contienen la información útil de los archivos. De esta manera, podríamos recordar tanto la ruta del archivo que guardamos, como la del que cargamos, y tenerla disponible cada vez que abrimos el programa. Además nos ahorraríamos preocupaciones sobre cómo se cargan y guardan archivos, y trabajaríamos únicamente con los byteArrays que contiene los datos.

Así pues, la clase PathSaver, sería algo así:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.net.SharedObject;
   
   /**Class to manage the file paths in a more usable way.*/
   public class PathSaver
   {
      
      private static var so:SharedObject;

      /** Returns a file from the location it was saved the last time.
       * @param id The id of the given file.
       * @param returnName if true, the function returns name of the file, else, it returns the folder.
       * @param newName if returnName is set to false, the new name of the file.
       * @return A File object, with the path it had the last time it was saved. */
            
      public static function getFile (id:String , returnName:Boolean = false , newName:String = ""):File
      {
         /*Obtenemos el SharedObject,
         y sacamos la ruta del archivo y la del directorio.*/
         so = SharedObject.getLocal(id + "$file");
         var path: String = so.data.path;
         var parent: String = so.data.parent;
         var file:File = new File ();
         
         /*Devolvemos un nuevo archivo según si existía el so,
          y según los parámetros.*/
         if (path && parent)
         {
            if (returnName)
            {
               file = file.resolvePath(path);
            }else if (newName != "")
            {
               file = file.resolvePath(parent).resolvePath(newName);
            }else {
               file = file.resolvePath(parent)
            }
         } else
         {
            file = File.desktopDirectory.resolvePath(newName);
         }
         
         /*Indicamos que cada vez que el archivo se guarde o abra,
         su nueva ruta quede almacenada en el so.*/
         file.addEventListener(Event.SELECT, savePath);
         
         return file;
      }
      
      
      /** This function saves the file path in a sharedObject. */
      private static function savePath (event:Event) :void
      {
         var file:File = event.target as File;
         so.data.path = file.nativePath;
         so.data.parent = file.parent.nativePath;
         so.flush();
      }

   }
}

Con esta clase, cada vez que necesitamos llamar a una archivo cuya ruta almacenó el usuario, en vez de crear el objeto File con new File (); usamos PathSaver.getFile ("IDDelArchivo");. Además, jugando con los otros parámetros, podemos conseguir distintas funcionalidades, como conseguir la ruta absoluta del archivo, o la de la carpeta que lo contiene, y obtener un nuevo nombre por defecto, que puede ser útil para guardar (como veremos en el ejemplo de abajo).

Veamos ahora una clase para cargar archivos desde el diálogo de Abrir, que hace uso de esta clase, y devuelve simplemente un ByteArray con los datos cargados:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.net.FileFilter;
   import flash.utils.ByteArray;
   
   /**Dispatched when the user selects a file from the fileSystem,
    * and the bytearray from the file is ready.*/
   [Event(name="bytesloaded", type="com.zigzah.utils.LoadFileEvent")]
   
   /** A utility class to get files easily from the filesystem  */
   public class FileLoader extends EventDispatcher
   {
      private var file :File
      private var filter:FileFilter
      
      /**The title of the open dialog window*/
      public var openDialog : String = "Open File";
      
      
      /**Creates a new FileLoader object, with the specified filefilter and
       * default path, in the open dialog
       * @param filter the filefilter object that will be used to browse for files in the filesystem.
       * @param fileID the id that PathSaver will to get a file.*/
      public function FileLoader(filter : FileFilter , fileID : String = "$defaultSaveFile")
      {
         file = PathSaver.getFile(fileID);
         this.filter = filter;
         file.addEventListener(Event.SELECT, makeBytes);
      }
      
      /** Opens the browse dialog. When the user chooses a file, a
       * LoadFileEvent will be dispatched, and it will contain the bytearray
       * retrieved by the file.
       * @see LoadFileEvent*/
      public function getBytes ():void
      {
         file.browseForOpen(openDialog ,[filter]);
      }
      
      /**Returns a ByteArray, for the LoadFileEvent.*/
      private function makeBytes (event:Event) :void
      {
         var fs:FileStream = new FileStream ();
         fs.open(event.target as File, FileMode.READ);
         var ba:ByteArray = new ByteArray ();
         fs.readBytes(ba, 0, fs.bytesAvailable);
         fs.close();
         var ev:LoadFileEvent = new LoadFileEvent();
         ev.bytes = ba;
         ev.fileName = file.name;
         this.dispatchEvent (ev);
      }

   }
}

Ésta clase también requiere LoadFileEvent, que no es más que:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.utils.ByteArray;

   public class LoadFileEvent extends Event
   {
      public var bytes : ByteArray;
      public var fileName : String;
      
      public static const BYTES_LOADED: String = "bytesloaded";
      
      
      public function LoadFileEvent(/*type:String, bubbles:Boolean=false, cancelable:Boolean=false*/)
      {
         super(LoadFileEvent.BYTES_LOADED, false, false);
      }
      
   }
}

Y ahora, otra clase para guardar ByteArrays, mediante el diálogo:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.utils.ByteArray;
   
   /**A simple class that lets the user save a byteArray in a file,
    * and uses PathSaver to save the file location.*/
   public class FileSaver
   {
      private var ba:ByteArray
      
      /**The title of the save dialog window.*/
      public var dialog : String = "Save File";
      
      /**Constructor.
       * @param bytes The byteArray that will be saved.*/
      public function FileSaver(bytes:ByteArray)
      {
         this.ba = bytes;
         
         
      }
      
      /**Opens a save dialog.
       * @param name the default name of the file.
       * @param fileID the id that will be used by PathSaver.*/
      public function save (name:String , fileID:String = "$defaultFile"):void
      {
         var file:File = PathSaver.getFile(fileID , false , name);
         file.addEventListener(Event.SELECT,saveBytes);
         file.browseForSave(dialog);
         
      }
      
      /**Writes the given bytes in the fileSystem.*/
      private function saveBytes (event:Event):void
      {
         var file:File = event.target as File;
         var fs:FileStream = new FileStream();
         fs.open(file , FileMode.WRITE)
         fs.writeBytes(ba);
         fs.close();
      }

   }
}

Para probarlo, podríamos hacer un simple editor de texto en Flex/AIR. Fíjense en que recuerda la ruta tanto de los archivos que cargan como de los que guardan (para eso, pruébenlo varias veces):

Código :

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Script>
   <![CDATA[
      import com.zigzah.utils.*;
      
      [Bindable]
      private var text:String = "";
      
      private function load (event:Event):void
      {
         var ff:FileFilter = new FileFilter ("txt" , "*.txt");
         var fldr:FileLoader = new FileLoader (ff , "loadedTXT");
         fldr.getBytes();
         fldr.addEventListener(LoadFileEvent.BYTES_LOADED,loadHandler);
      }
      
      private function loadHandler (event:LoadFileEvent):void
      {
         
         var ba:ByteArray = event.bytes;
         text = ba.readUTFBytes(ba.length);
      }
      
      private function save (event:Event):void
      {
         var ba:ByteArray = new ByteArray();
         ba.writeUTFBytes(ta.text);
         var fsaver:FileSaver = new FileSaver (ba);
         fsaver.save("newtxt.txt","savedTXT");
      }
   ]]></mx:Script>
   
      
   <mx:Button label="Load txt" click="load(event)" top="80" left="30"/>
   <mx:Button label="Save txt" click="save(event)" right="30" top="80"/>
   <mx:TextArea id="ta" text="{text}" right="30" left="30" bottom="10" top="120"/>
   
</mx:WindowedApplication>

Aquí pueden descargar los archivos del proyecto:


[Archivos]

Enviar comentario

Asignar código actionscript en clases a fotogramas concretos

Miércoles, Octubre 8th, 2008

Cuando trabajamos con Flash es probable que montemos la película colocando cada pantalla de la aplicación en un fotograma de la linea de tiempo principal, y que vayamos saltando de fotograma a fotograma para cambiar de pantalla.

Evidentemente cada fotograma tendrá sus gráficos y botones que deberán ejecutar unas funciones. En principio estos códigos deberíamos colocarlos en los fotogramas correspondientes. Quizás para el código no nos interese tener que ir colocándolo fotograma a fotograma sino que queramos trabajarlo a través de clases, y mantener el .fla limpio de código.

Mucha gente al hacer esto se encuentra con el problema que no sabe como escribir y ejecutar funciones a medida vas cambiando de fotograma en la película, o que les salta el típico error de ejecutar las acciones antes de que el fotograma esté disponible.

En este tip mostraré una serie de clases que me sirvieron para hacer esto.

El resultado es que en la clase de la película principal tengo esté código:

Código :

package
{
   import flash.display.MovieClip;
   import com.zguillez.core.TimelineScript;
   //--------------------------------------------------------------------------------
   public class Main extends MovieClip
   {
      private var _timelineScript:TimelineScript;
      //----------------------------------------------------------------------------
      public function Main()
      {
         _timelineScript = new TimelineScript(this);
         _timelineScript.setFrame( 1, new Frame1(this) );
         _timelineScript.setFrame( 2, new Frame2(this) );
         _timelineScript.setFrame( 3, new Frame3(this) );
         /* etc.. */
         stop();
      }
      //----------------------------------------------------------------------------
   }
}

Como se ve, hay una clase que le llamo TimelineScript que me controla que código hay en cada fotograma, y a la que se le agragan a través de la función setFrame() una serie de clases (Frame1, Frame2, Frame3, etc..) que son las clases que contienen el código para cada fotograma independientemente.

Un ejemplo de clase para manejar un fotograma podría ser este:

Código :

package
{
   import flash.display.MovieClip;
   import flash.events.MouseEvent;
   import com.zguillez.core.FrameScript;
   //--------------------------------------------------------------------------------
   public class Frame1 extends FrameScript
   {
      //----------------------------------------------------------------------------
      public function Frame1(ruta:MovieClip)
      {
         super(ruta);
      }
      //----------------------------------------------------------------------------
      override protected function actions():void
      {
         var boton:MovieClip = _ruta.getChildByName("bot1") as MovieClip;
         boton.buttonMode = true;
         boton.addEventListener(MouseEvent.CLICK, function()
         {
            gotoFrame(2)
         });
         /* resto del código para este fotograma */
      }
      //----------------------------------------------------------------------------
   }
}

El código para este fotograma lo colocaremos dentro de la función actions(). En este caso únicamente hacemos que un clip llamado "bot1" nos salte la película hasta el fotograma 2. En principio tendríamos una clase como esta para cada fotograma.

Hay que fijarse que esta clase extiende de otra clase llamada FrameScript. Esta clase contiene métodos comunes para todas las clases de fotogramas como la clase actions(), que deberemos sobreescribir desde las clases concretas, y la función pública gotoFrame() que es la que nos cambia de fotograma y que deberemos utilizar en lugar de un gotoAndStop().

Tanto la clase FrameScript como la TimelineScript las tendremos guardadas dentro de nuestro package de clases para reutilizarlas en diferentes proyectos ya que no necesitaremos editarlas para ningún proyecto concreto.

Aquí las vemos:

Código :

package com.zguillez.core
{
   import flash.display.MovieClip;
   import flash.events.Event;
   import com.zguillez.core.TimelineScript;
   //--------------------------------------------------------------------------------
   public class FrameScript
   {
      protected var _ruta:MovieClip;
      private var _timelineScript:TimelineScript;
      //----------------------------------------------------------------------------
      public function FrameScript(ruta:MovieClip)
      {
         _ruta = ruta;
      }
      //----------------------------------------------------------------------------
      internal function set timelineScript(t:TimelineScript):void
      {
         _timelineScript = t;
      }
      //----------------------------------------------------------------------------
      public final function init():void
      {
         _ruta.stage.addEventListener(Event.ENTER_FRAME, iniActions);
      }
      //----------------------------------------------------------------------------
      private final function iniActions(e:Event):void
      {
         if (_ruta.numChildren > 0)
         {
            if (_ruta.getChildAt(0) != null)
            {
               _ruta.stage.removeEventListener(Event.ENTER_FRAME, iniActions);
               actions();
            }
         }
         else
         {   
            _ruta.stage.removeEventListener(Event.ENTER_FRAME, iniActions);         
            actions();
         }
      }
      //----------------------------------------------------------------------------
      protected function actions():void { /* override */ }
      //----------------------------------------------------------------------------
      protected final function gotoFrame(f:uint):void
      {
         _ruta.gotoAndStop(f);
         _timelineScript.update();
      }
      //----------------------------------------------------------------------------
   }
}

Como vemos esta clase, que es de la que extienden todas las clases concretas de fotogramas, contiene una referencia de la ruta en la que están los gráficos y una referencia de la clase TimelineScript que es la que contiene todas las clases concretas de fotogramas.

Esta clase contiene un método actions() que esta vacío ya que este método será sustituido a través de un override desde las clases concretas de fotograma, ya que evidentemente cada clase tendrá sus propias funciones a ejecutar.

También tiene el método gotoFrame() que como he dicho antes es el que nos hace saltar de fotograma. Vemos que esta función contiene el gotoAndStop() y además ejecuta el método update() de la clase TimelineScript.

Las acciones que incluyamos en la función actions() han de ser inicializadas desde el método init(), más concretamente desde el método iniActions().

Como podemos ver hemos utilizado un ENTER_FRAME para realizar la llamada a la función actions(). Esto es debido a que cuando cambiamos de fotograma y ejecutamos un código, este código se ejecuta antes de que los elementos gráficos de dicho fotograma estén disponibles, con lo que cualquier referencia a ellos desde el código (como por ejemplo el getChildByName("bot1")) nos daría el error de que no existen. Con el ENTER_FRAME generemos un bucle de llamadas hasta que reconoce los elementos gráficos del fotograma, en ese momento ejecuta la acción que les asigna el código.

Vista esta clase, nos queda la clase TimelineScript:

Código :

package com.zguillez.core
{
   import flash.display.MovieClip;
   import com.zguillez.core.FrameScript;
   //--------------------------------------------------------------------------------
   public class TimelineScript
   {
      private var _ruta:MovieClip;
      private var _frameScript:Array = new Array();
      //----------------------------------------------------------------------------
      public function TimelineScript(ruta:MovieClip)
      {
         _ruta = ruta;
         initArray();
      }
      //----------------------------------------------------------------------------
      private function initArray():void
      {
         for (var i:uint = 0; i <= _ruta.totalFrames; i++)
         {
            _frameScript.push(void);
         }
      }
      //----------------------------------------------------------------------------
      public function setFrame(n:uint, s:FrameScript):void
      {
         s.timelineScript = this;
         _frameScript[n] = s;
         if (n == _ruta.currentFrame)
         {
            _frameScript[n].init();
         }
      }
      //----------------------------------------------------------------------------
      public function update():void
      {
         _frameScript[_ruta.currentFrame].init();
      }
      //----------------------------------------------------------------------------
   }
}

Esta clase contiene un Array al que le introducimos tantos elementos como fotogramas tiene la línea de tiempo principal.

A este Array le añadiremos las clases de código concreto para cada fotograma en la posición que les corresponda, a través del método setFrame(), tal como vimos en la clase de la película.

Esta clase también contiene una función update() que lo que hace es ejecutar la función init() de la clase que corresponda al fotograma actual y de esta manera ejecutar las funciones correspondientes para dicho fotograma, y como hemos visto anteriormente esta función update() se ejecuta cada vez que cambiamos de fotograma en la película.

Espero que se entienda el proceso y les sea útil Guiño

Enviar comentario

Seleccionar item de un combobox a través del nombre

Domingo, Octubre 5th, 2008

Recién tuve un problema cuando intentaba seleccionar el item de un combobox mediante el nombre y no como comúnmente se hace con el index. Exactamente buscaba algo tipo:

Código :

combobox.selectedItem = 'item1'

Pero lamentablemente eso no existe. Así que tuve que recurrir a otros métodos:

Código :

//Obtengo la longitud de mi AC
var len:int = miAC.length;
//Recorro el arraycollection
for (var i:int = 0; i < len; i++)
{
   //Comparo los objetos de mi AC con el nombre del item que buscaba
   if (miAC.getItemAt(i).cat == nombredeitem)
   {
      //Si encuentra el item le asigno su index a mi combo
      micombobox.selectedIndex = i;
      break;
   }
}

Donde "nombredeitem" puede ser:

Código :

nombredeitem = "item1";

O en caso de que sea dinámico tal y como yo quería:

Código :

nombredeitem = otroAC.getItemAt(0).item;

Bueno eso es todo, lo dejo quizás sea de utilidad para otros Thumbs up

Enviar comentario

Acceder a funciones en MovieClips de películas .swf externas

Martes, Septiembre 30th, 2008

Al crear proyectos complejos en Flash, es común tener varios archivos SWF. En estos, podemos tener MovieClips con funciones externas que queremos usar en la película principal. En Actionscript 3, logramos esto tras cargar nuestros .swf con un Loader. En este tip explicaré cómo acceder a las funciones públicas de la clase asignada a un MovieClip de la librería de un archivo .swf externo desde la película principal.

Para mostrar el proceso utilizaré un ejemplo muy simplificado. Tendremos una película swfClipA.fla en cuya biblioteca tendrá un MovieClip al que le hemos asignado una clase, en este caso la clase se llama ClipA, que estará dentro del package clases.

Para simplificar al máximo este ejemplo únicamente haremos que la clase de este clip le coloque un angulo de rotación aleatorio y coloque este valor dentro de una variable, y contendrá una función publica que devuelve dicho valor.

Código :

package clases
{
   import flash.display.MovieClip;

   public class ClipA extends MovieClip
   {
      private var angulo:uint;
      
      public function ClipA()
      {
         this.rotation = Math.round(Math.random() *360);
         angulo = this.rotation;
      }
      public function getAngulo():uint
      {
         return angulo;
      }
   }
}

Ahora bien, imaginemos que tenemos una película principal en la que cargamos este archivo swfClipA.swf. Esta sería la clase asignada a la película principal:

Código :

package clases
{
   import flash.display.Loader;
   import flash.display.MovieClip;
   import flash.net.URLRequest;
   import flash.events.Event;

   public class Main extends MovieClip
   {
      private var _swf:MovieClip;

      public function Main()
      {
         var loader:Loader = new Loader();
         var request:URLRequest = new URLRequest("swfClipA.swf");
         loader.load(request);
         loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCargado);
      }
      private function onCargado(e:Event):void
      {
         _swf = addChild(e.target.content) as MovieClip;
      }
   }
}

Y ahora desde esta película principal acceder a la función pública del MovieClip interno del archivo externo. Lo único que tenemos que hacer es buscar la ruta de esa clase dentro del clip cargado.

Para ello realizaremos una búsqueda dentro del clip cargado y seleccionaremos cual de ellos está creado con la clase que nos interesa interactuar. Una vez encontrada podremos acceder sin problema a sus funciones públicas.

Código :

for (var i:uint = 0; i < _swf.numChildren; i++)
{
   var clip:MovieClip = _swf.getChildAt(i) as MovieClip;
   if (clip.constructor == ClipA)
   {
      var _clipA:MovieClip = clip;
   }
   trace(_clipA.getAngulo());
}

Nota: Este no es un buen ejemplo de uso en cuando a POO se refiere, ya que rompe totalmente la encapsulación de este módulo externo. Lo mejor hubiese sido que esta película externa tuviese en su clase principal toda una serie de funciones publicas, que controlasen las funciones de sus clips interiores, y toda una serie de dispatcher para comunicarse con la película principal tal como si de un componente se tratase, en lugar de andar buscando referencias a clips internos. Pero bueno, en caso de apuro tenemos este recurso.

Enviar comentario

Cómo capturar múltiples webcams con Actionscript 2 y 3

Jueves, Septiembre 18th, 2008

Hay momentos en la vida en los que las circunstancias exigen poder visualizar múltiples webcams mediante una aplicación Flash. Pues bien, antes de que ese día llegue, habrá que estar preparado.

Primero se hará el procedimiento en ActionScript 2, usando Flash 8. Los pasos en general son los mismos:

  1. Capturar las cámaras conectadas al sistema
  2. Crear un listado de las cámaras obtenidas
  3. Visualizar la cámara seleccionada

¿ Cómo reproducir lo anterior en ActionScript 2 ?

Capturar las cámaras conectadas al sistema
Flash Player nos permite usar cualquier cámara de cualquier tipo conectada al PC, siempre y cuando ésta se encuentre debidamente configurada. Para conseguir el listado completo, bastará con usar la propiedad names:

Código :

Camera.names

Crear un listado de las cámaras obtenidas
El código anterior retornará un Array con nuestra información, así que lo visualizamos a través del bonito componente List:

Código :

//capturando listado de cámaras conectadas al sistema
camaras_array = Camera.names;
if(camaras_array.length > 0){
   //recorriendo el vector con los nombres de las cámaras
   for(var c in camaras_array){
      //agregando la cámara al listado del componente
      camaras.addItem({data:c, label:camaras_array[c]});
   }
}

Visualizar la cámara seleccionada
Una vez tenemos el índice de la cámara seleccionada, se inicia la captura y puesta en escena del video:

Código :

   //el componente List se llama camaras
   var camara_cam:Camera = Camera.get(camaras.selectedItem.data);
   //vinculando la cámara seleccionada al componente de video en el escenario
   video.attachVideo(camara_cam);

Ahora en ActionScript 3 con Flash CS3

En ActionScript 3 el proceso es el mismo respetando la debida sintaxis requerida para cada paso, sin embargo los cambios específicos para cumplir el objetivo son dos:

Componente video
Para este tip se ha creado el componente vídeo de manera dinámica y se ha agregado al escenario a través de un Sprite:

Código :

   var video      :Video       = new Video();
   //definiendo el tamaño
   video.width             = 320;
   video.height             = 240;
   //contenedor para el video
   var contenedor:Sprite = new Sprite();
   //se agrega el video al contenedor
   contenedor.addChild(video);
   //se agrega el contenedor al escenario
   addChild(contenedor);
   //se ubica el contenedor dentro del escenario
   contenedor.x             = 12;
   contenedor.y             = 50;

Captura del video
Mientras que en AS2 se hacía uso del método get, en AS3 su nombre es getCamera:

Código :

   //cargando el video de la cámara seleccionada
   var camara_cam:Camera = Camera.getCamera(camaras.selectedItem.data);

Y bien, si estas porciones de código no son suficientes, aún pueden descargar los fuentes para cada versión de AS y desbaratarlos:
WebCam con AS2
WebCam con AS3

Nota :

Importante: Si se trata de webcam’s, o en general de cámaras USB, habrá que entender que cada dispositivo USB tiene un ancho de banda limitado. Por lo tanto, inicialmente no se podrá visualizar al mismo tiempo más de 1 cámara de este tipo

Enviar comentario