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

Wysyłanie danych z preloader'a do wczytanego SWF'a [podstawowy]

Kontynuują temat interfejsów z poprzedniego wpisu, wspomniałem tam o scenariuszach w których ich wykorzystanie jest niezastąpione i jednym z takich przykładów jest komunikacja z plikiem SWF wczytanym do aplikacji, najczęściej spotykanym w preloaderach właśnie.
Zacznijmy od standardowego przykładu preloadera:
var loadLoader:Loader = new Loader();
loadLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
loadLoader.load(new URLRequest("MyAnimation.swf"));
		
function onLoaded(e:Event):void {
	addChild(loadLoader.content);
}
W tym wypadku plik MyAnimation.swf i jego zawartość zostanie wrzucony na scenę jak tylko preloader go w pełni załaduje. Jest to najprostsze rozwiązanie nie dające nam praktycznie żadnej kontroli nad tym kto i jak może wczytać ową animację, dlatego powiedzmy że chcielibyśmy aby ta odtwarzała się jedynie wtedy gdy preloader poda prawidłowe hasło. W tym celu należy przejść do pliku animacji i utworzyć dla niej nową klasę dokumentu:

W taki sposób aby animacja nie startowała automatycznie oraz aby nie dało się tego wymusić z zewnątrz:
public class MyAnimation extends MovieClip {
	public function MyAnimation() {
		super.gotoAndStop(1);
	}
public override function gotoAndStop(frame:Object,scene:String=null):void {
		return;
	}
	public override function gotoAndPlay(frame:Object,scene:String=null):void {
		return;
	}
	public override function play():void {
		return;
	}

}
Teraz nawet my nie będziemy w stanie odtworzyć danej animacji, dlatego czas przygotować obejście z którego tylko my będziemy mogli skorzystać, oczywiście wykorzystując do tege interfejs. Niech wygląda on tak:
public interface IMyInterface {
	function playUsingPassword(pass:String):void;
}
A zaimplementowany w naszej klasie MyAnimation:
public class MyAnimation extends MovieClip implements IMyInterface {
	public function MyAnimation() {
		super.gotoAndStop(1);
	}
	public function playUsingPassword(pass:String):void {
		if(pass == "MyPassword") super.play();
	}
public override function gotoAndStop(frame:Object,scene:String=null):void {
		return;
	}
	public override function gotoAndPlay(frame:Object,scene:String=null):void {
		return;
	}
	public override function play():void {
		return;
	}

}
(Nie zapomnij o implements!)
Napisanie super.play() spowoduje, że pominięta zostanie nasza implementacja a wykorzystany zostanie "prawdziwy" play() z klasy MovieClip. Pozostaje nam już tylko to prze testować w preloaderze:
var loadLoader:Loader = new Loader();
loadLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
loadLoader.load(new URLRequest("MyAnimation.swf"));
		
function onLoaded(e:Event):void {
	addChild(loadLoader.content);

	var main:IMyInterface = loadLoader.content as IMyInterface;
	if(main != null) main.playUsingPassword("MyPassword");
}
W efekcie dostając:
(Plik animacji znajduje się tutaj: MyAnimation.swf - każdy kto szuka wyzwania może spróbować wczytać ten plik samemu)
Źródła: PreloaderInterface.zip

Interface'y we Flash'u [podstawowy]

Interfejsami w programowaniu nazywamy abstrakcyjne definicje metod które sam z siebie nic nie robią po za narzucaniem implementującym je klasą pewnej składni. Innymi słowy nazwę funkcji i jej argumenty definiujemy w jednym miejscu a w drugim implementujemy (docelowej klasie) jej działanie. Nie wątpliwie są szybsze i łatwiejsze sposoby by tworzyć funkcje we Flash’u jednak interfejsy mają jedną szczególna cechę powodującą, że są wręcz nie zastąpione w specyficznych scenariuszach. Tą cechą jest możliwość rzutowania obiektów do implementowanych przez nich klas, a co za tym idzie będziemy mogli się odwołać do definiowanych metod bez dokładnej znajomości całego obiektu.
Myślę, że najlepiej będzie to przedstawić na przykładzie, zaczynając od samego początku: powiedzmy, że tworzymy grę w której informacje przekazujmy przez wyskakujące okienka zawierające przycisk “OK”.
public class MyPopup extends MovieClip {
	private var myButton:OKButton;
	public function MyPopup() {
		myButton = new OKButton(this);
		addChild(myButton);
	}

	public function close():void {
		this.visible = false;
	}
}
public class OKButton extends MovieClip {
	private var myPopup:MyPopup;
	public function OKButton(popup:MyPopup) {
		myPopup = popup;
		this.addEventListener(MouseEvent.CLICK,onMouse);
	}
	private function onMouse(e:MouseEvent):void {
		myPopup.close();
	}
}
Z początku wszystko może wydawać się w porządku, ale jeśli tylko zaczniemy rozbudowywać nasz grę o kolejne okienka, ograniczenia powyższego kodu staną się oczywiste - co zrobić gdy dodamy MyPopup2 ale będziemy chcieli wykorzystać ten sam przycisk? Czy dopisanie do konstruktora kolejnego argumentu ( OKButton(popup:MyPopup, popup2:MyPopup2) {} ) wystarczy? A co jeśli później dodamy kolejne okienka? Jednym rozwiązaniem jest na pewno utworzenie klasy podrzędnej wspólnej dla wszystkich okienek, odwołując się do niej zamiast wszystkich nadrzędnych klas z osobna. Jednak klasa bazowa może przysporzyć we Flash’u kłopotów których nie będę tutaj opisywał. Myślę, że w tym momencie jest już jasne do czego zmierzam - najlepszym rozwiązaniem będzie wykorzystanie interfejsów. Tak więc stwórzmy osobny plik AS:
public interface IPopup {
	function close():void
}
A istniejące klasy przeróbmy w następujący sposób:
public class MyPopup extends MovieClip implements IPopup{
	private var myButton:OKButton;
	public function MyPopup() {
		myButton = new OKButton(this);
		addChild(myButton);
	}

	public function close():void {
		this.visible = false;
	}
}
public class OKButton extends MovieClip {
	private var myPopup:IPopup;
	public function OKButton(popup:IPopup) {
		myPopup = popup;
		this.addEventListener(MouseEvent.CLICK,onMouse);
	}
	private function onMouse(e:MouseEvent):void {
		myPopup.close();
	}
}
Różnica jest niewielka, ale możliwości o wiele większe. Od teraz MyButton może się komunikować ze wszystkimi stworzonymi okienkami bez potrzeby poznania ich klasy, wystarczy że implementują ten sam interfejs.