ProgressionのCommandが良い感じ

こんにちは。
正宗です。
今日はProgressionのCommandのお話をしたいと思います。
毎度ながら僕個人の備忘録となっているので、誰も役に立たない情報ですが僕が便利だと感じるならそれでいいんです。

Flashサイト制作の上でもはや手放せないフレームワーク、その名も「Progression
Scene管理も秀逸ですが僕がProgressionを使ってて何よりも惚れ惚れするのがCommandがよく練られていてとっても使いやすいところなのです。
「こういうこともCommandで出来ないかな?」とリファレンスを読み読みしてると必ず上手いやり方が用意されていて、どんどん使っていくうちにいわゆる「Progression的な」コードの書き方に身が染まっていって毎日新しい発見があってコードを書くのがとても楽しいです。
(もちろん、Progression的な書き方を強制されるわけでは無くって、我流の書き方も完全に許容されているところもこのフレームワークを好きな理由の一つです)
ということで、今まで勉強してきたCommandの使い方の途中段階までをまとめておきたいと思います。

基本のキホン

まずCommandがどこで実行されるかですが、一番簡単なのは
SceneObjectでしたら_onLoad、_onInit、_onGoto、_onUnloadの関数の中に直接addCommandしておく方法。
CastSpriteやCastMovieClipなどは_onCastAddedや_onCastRemovedなんかに直接addCommandしておきます。
このへんのシーン管理やイベント管理もとっても面白いんでいろいろとまとめたいところなんですが、今日のまとめはあくまでCommandだけにとどめておきたいと思います。

SerialListとParallelList

  • SerialListは順番にコマンドを実行する
  • ParallelListは一度に同時にコマンドを実行する
  • SerialListに配列としてコマンドをaddCommandすればその配列内のコマンドはParallelListとして実行される
  • 逆にParallelListに配列としてコマンドをaddCommandすればその配列内のコマンドはSerialListとして実行される
  • _onInitなんかの関数の中は最初はSerialList

こんなところでしょうか。
使い方はこんな感じ。

private function test():void {
	var list:SerialList = new SerialList();
	list.addCommand(
		new LoadURL(new URLRequest("hoge.xml")),//XMLの読み込みを開始
		function():void {
			trace(new XML(this.latestData));//読み込んだXMLを出力
		},
		[
			new DoTweener(front, { alpha:0, time:1 } ),//前面のMCを1秒かけて透明に
			new DoTweener(back, { alpha:1, time:1 } )//背面のMCも同時に1秒かけて不透明に
		]
	);
	list.execute();//SerialListを実行
}

最初にSerialListで実行させているので、XMLの読み込みを待って完了したら自動的に次の処理に移ります。
その後配列で登録してあるのでクロスフェードの処理はParallelListとして同時に実行されることになります。
そうめんもそうなんですが、もうaddEventListenerだらけで「えっと、ここが完了したら次こうなって…」と悩まされなくて済むようになりました。

外部swfのクラスを使いたい

こんだけイケてるCommandなのですから、シーンの読み込みの際に外部swfを取得してそこからクラスを参照するやり方も何か用意されているのではないかと思って調べてたら、やっぱりありました。
LoadObjectといって、CastLoaderを監視するコマンドがありました。
さすがtaka:nium先生!もう俺は一生Progressionについていくぜー!

package
{
	import flash.display.Sprite;
	import jp.progression.casts.*;
	import jp.progression.commands.*;
	import jp.progression.commands.display.LoadObject;
	import jp.progression.events.*;
	import jp.progression.loader.*;
	import jp.progression.*;
	import jp.progression.scenes.*;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.ApplicationDomain;
	public class Test extends CastSprite 
	{
		private var _loader:CastLoader;
		private var _context:LoaderContext;
		public function Test( initObject:Object = null ) 
		{
			super( initObject );
			_loader = new CastLoader();
			_context = new LoaderContext();
			_context.applicationDomain = ApplicationDomain.currentDomain;
		}
		protected override function _onCastAdded():void 
		{
			addCommand(
				new DoTweener(this, { alpha:0 } ),
				new LoadObject(_loader, new URLRequest("hoge.swf"), {
					context:_context,
					onCastLoadComplete:function():void {
						var myDomain:ApplicationDomain = _loader.contentLoaderInfo.applicationDomain;
						var myClass:Class = _myDomain.getDefinition("onigiri") as Class;
						var mc:Sprite = new myClass();
						addChild(mc);
					}
				}),
				new DoTweener(this, { alpha:1, time:1 } )
			);
		}
	}
}

実際にはLoadObjectのonCastLoadCompleteに登録しなくっても、そのままシリアルリストに次の処理を書いていってもよさげ。

だけどここで少しばかり問題が。
上に挙げたサンプルはCastなにがしで処理を行っているので問題ないんだけれど、SceneObjectでこれをやろうとするとCommandの参照の問題が出てくるような気がする。

例えばある特定のScene以下からは”hoge.swf”を読み込んでその階層以下からのSceneでは”hoge.swf”を使い回したい場合

package
{
	import jp.progression.casts.*;
	import jp.progression.commands.*;
	import jp.progression.events.*;
	import jp.progression.loader.*;
	import jp.progression.*;
	import jp.progression.scenes.*;
	import jp.progression.commands.display.LoadObject;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.ApplicationDomain;
	import HogePage;
	
	public class HogeScene extends SceneObject 
	{
		private var _page:HogePage;
		private var _singleton:Singleton;
		private var _loader:CastLoader;
		private var _context:LoaderContext;

		public function HogeScene( name:String = null, initObject:Object = null ) {
			super( name, initObject );

			title = "hogehogeシーン";

			_loader = new CastLoader();
			_context = new LoaderContext();
			_context.applicationDomain = ApplicationDomain.currentDomain;
			
		}
		
		private function init():void {
			var myDomain:ApplicationDomain = _loader.contentLoaderInfo.applicationDomain;
			_page = new HogePage(myDomain);

			removeAllScenes();
			var fugaHelp:DressHelpScene = new fugaHelpScene(myDomain);
			fugaHelp.name = "help";
			addScene(fugaHelp);
		}
		
		protected override function _onLoad():void 
		{
			addCommand(
				new LoadObject(_loader, new URLRequest("dressshop.swf"), {
					context:_context
				}),
				init,
				new AddChild(progression.container, _page)
			);
		}

		protected override function _onInit():void 
		{
			addCommand(
			);
		}
		
		protected override function _onGoto():void 
		{
			addCommand(
			);
		}
		
		protected override function _onUnload():void 
		{
			addCommand(
				new RemoveChild(progression.container, _page),
				function():void {
					_loader.unload();
				}
			);
		}
	}
}

こんなふうに書くと動かなくなる。
何故これがだめなのかは

package
{
	import jp.progression.casts.*;
	import jp.progression.commands.*;
	import jp.progression.events.*;
	import jp.progression.loader.*;
	import jp.progression.*;
	import jp.progression.scenes.*;
	import jp.progression.commands.display.LoadObject;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.ApplicationDomain;
	import HogePage;
	
	public class HogeScene extends SceneObject 
	{
		private var _page:HogePage;
		private var _singleton:Singleton;
		private var _loader:CastLoader;
		private var _context:LoaderContext;

		public function HogeScene( name:String = null, initObject:Object = null ) {
			super( name, initObject );

			title = "hogehogeシーン";

			_loader = new CastLoader();
			_context = new LoaderContext();
			_context.applicationDomain = ApplicationDomain.currentDomain;
			
		}
		
		private function init():void {
			var myDomain:ApplicationDomain = _loader.contentLoaderInfo.applicationDomain;
			_page = new HogePage(myDomain);

			removeAllScenes();
			var fugaHelp:DressHelpScene = new fugaHelpScene(myDomain);
			fugaHelp.name = "help";
			addScene(fugaHelp);

			trace(this, _page);
		}
		
		protected override function _onLoad():void 
		{
			addCommand(
				new LoadObject(_loader, new URLRequest("dressshop.swf"), {
					context:_context
				}),
				init,
				new Trace(this),
				new Trace(_page),
				new AddChild(progression.container, _page)
			);
		}

		protected override function _onInit():void 
		{
			addCommand(
			);
		}
		
		protected override function _onGoto():void 
		{
			addCommand(
			);
		}
		
		protected override function _onUnload():void 
		{
			addCommand(
				new RemoveChild(progression.container, _page),
				function():void {
					_loader.unload();
				}
			);
		}
	}
}

こう書いてみるとなんとなくわかるんだけれど、じゃあどうすればいいかというと僕はこうしました。

package
{
	import jp.progression.casts.*;
	import jp.progression.commands.*;
	import jp.progression.events.*;
	import jp.progression.loader.*;
	import jp.progression.*;
	import jp.progression.scenes.*;
	import jp.progression.commands.display.LoadObject;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.ApplicationDomain;
	import HogePage;
	
	public class HogeScene extends SceneObject 
	{
		private var _page:HogePage;
		private var _singleton:Singleton;
		private var _loader:CastLoader;
		private var _context:LoaderContext;

		public function HogeScene( name:String = null, initObject:Object = null ) {
			super( name, initObject );

			title = "hogehogeシーン";

			_loader = new CastLoader();
			_context = new LoaderContext();
			_context.applicationDomain = ApplicationDomain.currentDomain;

			_page = new HogePage(myDomain);
		}
		
		private function init():void {
			var myDomain:ApplicationDomain = _loader.contentLoaderInfo.applicationDomain;
			//_page = new HogePage(myDomain);
			_page.myDomain;

			removeAllScenes();
			var fugaHelp:DressHelpScene = new fugaHelpScene(myDomain);
			fugaHelp.name = "help";
			addScene(fugaHelp);

			trace(this, _page);
		}
		
		protected override function _onLoad():void 
		{
			addCommand(
				new LoadObject(_loader, new URLRequest("dressshop.swf"), {
					context:_context
				}),
				init,
				new Trace(this),
				new Trace(_page),
				new AddChild(progression.container, _page)
			);
		}

		protected override function _onInit():void 
		{
			addCommand(
			);
		}
		
		protected override function _onGoto():void 
		{
			addCommand(
			);
		}
		
		protected override function _onUnload():void 
		{
			addCommand(
				new RemoveChild(progression.container, _page),
				function():void {
					_loader.unload();
				}
			);
		}
	}
	public class HogePage extends CastSprite
	{
		private var _myDomain:ApplicationDomain;
		public function set myDomain(value:ApplicationDomain):void 
		{
			_myDomain = value;
		}
	}
}

他にもどうしても_onLoadのときに動的にクラスを生成しなければならなかったりした場合はもうちょっと違う書き方をするんだけど、僕の書いたどれもが根本的にCommandで処理できる流れになってなくて、ものすごい気持ち悪いです。
ひとえに僕がCommand内のthisの参照をよくわかっていないからだとは思いますが、もうちょっとCmmandだけで完結できる方法を勉強してみたいと思います。
目指せ!ProgressionのCommandマスター!

コメントを残す

メールアドレスが公開されることはありません。