Strona wykorzystuje ciasteczka by usprawnić komfort z jej korzystania. Korzystając ze strony akceptujesz naszą Politykę Ciasteczek. X

Od AS2 do AS3: duplicateMovieClip() w ActionScript 3 [zaawansowany]

Programiści którzy postanowią przejść na AS3 z AS2 prędzej czy później na pewno zauważą kompletny brak odpowiednika funkcji duplicateMovieClip() w najnowszej iteracji ActionScript'a. Może wydawać się to nieco dziwne, w końcu trudno sobie wyobrazić by Adobe nie było wstanie zaimplementować takiej funkcji - powodem ten decyzji jest prawdopodobnie chęć zabicia złych nawyków w użytkownikach AS3. W każdym razie, czasem po prostu trzeba zduplikować jakiś MovieClip dlatego poniżej prezentuje listę możliwych rozwiązań:

1. Własna klasa.
Myślę, że około 90% ludzi którzy szukają alternatywy dla duplicateMovieClip() powinni być zadowoleni z tego rozwiązania. W skrócie chodzi tutaj o stworzenie klasy podrzędnej MovieClip'a i powiązanie jej z grafiką którą chcemy powielić. Dla przykłady powiedzmy, że tworzymy aplikacje która w tle będzie miała spadające gwiazdy i oczywiście by uzyskać ten efekt stworzymy MovieClip jednej gwiazdy a następnie zaczniemy ją kopiować tyle razy ile jest to potrzebne. W tym celu należy udać się do właściwości MovieClip'a owej gwiazdy i zaawansowanych opcjach zaznaczyć "Eksportuj dla ActionScript".
Eksporotwanie dla ActionScript
Od teraz klasa "Star" zawiera grafikę gwiazdy i można ją duplikować do woli pisząc:

var mc:MovieClip = new Star();
addChild(mc);
I tyle.

2. Skorzystanie z funkcji loadBytes() w klasie Loader.
Więc metoda #1 jest całkiem przyjemna ale co jeśli chcemy zduplikować plik wczytany z zewnętrznego źródła? Zobaczmy jak może w takim wypadku wyglądać ładowanie grafiki:

import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;

lLoad = new Loader();
lLoad.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
lLoad.load(new URLRequest("your_file.swf"));

function onLoaded(e:Event):void {
	addChild(lLoad.content);
}
Cóż, klasa Loader nie ma zbyt wielu funkcji i po załadowaniu pliku SWF możemy go praktycznie tylko dodać na scenę albo załadować inną grafikę. Oczywiście ponowne wczytanie grafiki z serwera odpada (aczkolwiek jeśli użytkownik ma włączone przechowywanie obiektów w pamięci podręcznej Flash skorzysta właśnie z niej zamiast nawiązywać połączenie jeszcze raz) dlatego duplikacji dokonamy wykorzystując loadBytes(). Trik polega na tym, że loadBytes() jest w stanie wczytać dowolny, obsługiwany przez load(), obiekt zapisany w ByteArray. Pytanie tylko jak uzyskać ByteArray wczytanego MovieClip'a? Okazuje się, że rozwiązanie jest bardzo proste:
function onLoaded(e:Event):void {
	addChild(lLoad.content);
	lLoad.loadBytes(lLoad.content.loaderInfo.bytes);
}
Wartość lLoad.content.loaderInfo.bytes przechowuje wczytaną animację jako ByteArray, więc wystarczy ją wrzucić do funkcji loadBytes by ponownie wywołać zdarzenie onLoaded i dostać kolejny MovieClip bez nawiązywania jakichkolwiek dodatkowych zewnętrznych połączeń.
Jest tylko jeden problem, zdarzenie onLoaded nie zostanie wywołane natychmiast... co oznacza, że wczytany MovieClip zostanie zduplikowany dopiero po kilku lub kilkunastu milisekundach (zależnie od wartości FPS).

3. Skorzystanie z wartości constructor w obiekcie wczytam przez Loader.
Alternatywnym rozwiązaniem do duplikowania SWF wczytanych z zewnątrz jest wykorzystanie wartości constructor dostępnej we wszystkich klasach Flash'a. Bez owijania w bawełnę korzysta się z niej w następujący sposób:

function onLoaded(e:Event):void {
	var d:DisplayObject = new lLoad.content["constructor"]();
	addChild(d);
}
(Zmienna constructor jest dynamiczna więc to jedyny sposób by się do niej dostać. Ponadto nie zapomnij o słowie kluczowym new!)
Niestety, nie jest to idealne rozwiązanie. Kod zdziała tylko wtedy gdy wczytana animacja będzie miała niestandardową "Klasę Dokumentu" (może to być cokolwiek, byle tylko pole "Klasa Dokumentu" nie było puste), co w praktyce oznacza, że to ty musisz być twórcą wczytywanego pliku.

By zaoszczędzić trochę na pracy zaimplementowałem te rozwiązania w jedną klasę - powinna być w stanie zduplikować każdy MovieClip (i nie tylko): foras_clone.zip
A korzysta się z niej w następujący sposób:

import foras.utils.ForCloner;
import flash.display.DisplayObject;
if(stage==null) return;
var fas_Clone:ForCloner = new ForCloner();
var d:DisplayObject = fas_Clone.cloneMovie(my_mc,onClone);
if(d!=null) {
	d.x = stage.stageWidth*Math.random();
	d.y = stage.stageHeight*Math.random();
	addChild(d);
}

function onClone(d:DisplayObject):void {
	d.x = stage.stageWidth*Math.random();
	d.y = stage.stageHeight*Math.random();
	addChild(d);
}
Ponieważ klasa ForCloner również korzysta z metody loadBytes() (o której pisałem w punkcie 2) potrzebna jest funkcja która zajmie się zdarzeniem wczytania pliku i właśnie dlatego cloneMovie() jej wymaga. Oczywiście może się zdarzyć, że wcale nie będzie potrzebna i cloneMovie() natychmiast zwróci zduplikowany MovieClip. Najczęściej jednak będzie to null i wtedy kopia animacji trafi do wybranej funkcji (w tym przykładzie onClone).
Jeszcze jedna uwaga na koniec, linijka if(stage==null) return; zatrzymuje kod przed ponowny wywołaniem - jeśli wszystko inne zawiedzie zduplikowana zostanie cała aplikacja, a to oznacza wczytanie również kodu drugi raz (z całości zostanie wyciągnięty tylko docelowy MovieClip, a reszta zostanie usunięta).