[AS3] Commandライブラリを作って非同期処理をする 3

2010/12/3

こんにちは。きんくまです。
今回はSerialCommandとParallelCommandです。

SerialCommand > 一連のコマンドを登録した順に、ひとつずつ実行する
ParallelCommand > 一連のコマンドを同時に実行を開始する

SerialCommand


package kinkuma.command
{
	import flash.events.Event;
	/**
	 * ...
	 * @author KinkumaDesign
	 */
	public class SerialCommand extends Command
	{
		private var _commands:Array;
		private var _currentCommand:ICommand;
		
		public function SerialCommand() 
		{
			_commands = [];
		}
		
		public function addCommand(command:ICommand):void
		{
			_commands.push(command);
		}
		
		override public function execute():void
		{
			if (_commands.length == 0) {
				dispatchCompleteEvent();
			}else {
				_currentCommand = _commands.shift();
				_currentCommand.addEventListener(CommandEvent.COMMAND_COMPLETE, currentCommandCompleteHD);
				_currentCommand.execute();
			}
		}
		
		private function currentCommandCompleteHD(e:Event):void 
		{
			_currentCommand.removeEventListener(CommandEvent.COMMAND_COMPLETE, currentCommandCompleteHD);
			_currentCommand = null;
			execute();
		}
	}

}

コマンドを保持する配列を作って、addCommandで登録、excuteすると一番古いものから取り出して実行。
そのコマンドが終わったら、次のコマンドを取り出して実行。、、とつづけて、取り出すコマンドがなくなったら終了イベントを発行します

ParallelCommand.as

package kinkuma.command
{
	import flash.events.Event;
	/**
	 * ...
	 * @author KinkumaDesign
	 */
	public class ParallelCommand extends Command
	{
		private var _commands:Array;
		private var _completeCount:int;
		
		public function ParallelCommand() 
		{
			_commands = [];
		}
		
		public function addCommand(command:ICommand):void
		{
			_commands.push(command);
		}

		override public function execute():void
		{
			if (_commands.length == 0) {
				allCommandsCompleteHD();
			}else {
				_completeCount = 0;
				var i:int, len:int;
				len = _commands.length;
				var command:ICommand;
				for (i = 0; i < len; i++) {
					command = _commands[i];
					command.addEventListener(CommandEvent.COMMAND_COMPLETE, oneCommandCompleteHD);
					command.execute();
				}
			}
		}
		
		private function oneCommandCompleteHD(e:Event):void 
		{
			_completeCount++;
			if (_completeCount == _commands.length) {
				allCommandsCompleteHD();
			}
		}
		
		private function allCommandsCompleteHD():void
		{
			_commands = [];
			dispatchCompleteEvent();
		}
	}

}

内部にコマンドたちを保持する配列を作って、executeすると配列から全てのコマンドを取り出して、同時に実行開始。
それぞれのコマンドたちが実行が終わった時点で、全てのコマンドの実行が終了したかチェック。
実行が終了していれば、ParallellCommandとして終了イベントを発行

テストしてみる

package 
{
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import kinkuma.command.CommandEvent;
	import kinkuma.command.FunctionCommand;
	import kinkuma.command.ParallelCommand;
	import kinkuma.command.SerialCommand;
	import kinkuma.command.TweenerCommand;
	import kinkuma.command.WaitCommand;
	
	/**
	 * ...
	 * @author KinkumaDesign
	 */
	public class Main extends Sprite 
	{
		
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			var sp:Sprite = new Sprite();
			var g:Graphics = sp.graphics;
			g.beginFill(0xff0000, 1);
			g.drawCircle(0, 0, 100);
			g.endFill();
			addChild(sp);
			sp.x = 200;
			sp.y = 200;
			sp.alpha = 0;
			
			var sc:SerialCommand = new SerialCommand();
			sc.addCommand(new TweenerCommand(sp, { alpha:1, x:300, time:0.8, transition:"easeinoutquad" } ));
			sc.addCommand(new FunctionCommand(function():void { trace("Hello SerialCommand !"); } ));
			sc.addCommand(new WaitCommand(1));
			
			var pc:ParallelCommand = new ParallelCommand();
			pc.addCommand(new TweenerCommand(sp, { y:400, time:0.5, transition:"easeinquad" } ));
			pc.addCommand(new FunctionCommand(function():void { trace("Hello ParallelCommand !" )}));
			pc.addCommand(new WaitCommand(2));
			
			sc.addCommand(pc);
			sc.addEventListener(CommandEvent.COMMAND_COMPLETE, compHD);
			sc.execute();
		}
		
		private function compHD(e:Event = null):void 
		{
			trace("comp");
		}
	}
	
}

非同期処理っぽく書けました。
自分的にはこんなもんでも実用に耐えるかなと思ってます。
必要に応じて都度機能は追加していってもいいと思うし、コードヒントが出やすいように
書き換えるのもいいかもしれませんね。このあたりはお好みで。

これから高機能にしていくとしたら、Cancel機能をつけたり、
各コマンドに違う書き方で処理内容を登録できたりするようにしたり、といったところでしょうか。
あとは、カスタムコマンドクラスを追加して、キャラできまった動きをするものをコマンドとしてまとめてしまって、それを各キャラに適用してみたりとか。

まとめ

すごい人がすでに作ったナイスなライブラリがあるのに、なんで機能を削ったようなライブラリを自分で作ってんのかといいますと、

・単なる思い付きで深い意味はない
・ライブラリの機能を全て使うわけではないので、軽くてメンテしやすい自分ライブラリがあってもいいかも
・自分で作ってみると、他言語でも似たようなものを作ることができる

特に最後の他言語でもうんぬんのところは、結構役にたっています。
以前にTweenエンジンを自分で作ってみたことがあります。
>> [AS3]俺俺TweenエンジンOreOreTweenを作ってみよう1

そんでこの経験が生きて、ここからまた改良してObjective-C, C/C++でもTweenを自作することができました。

あとは、ライブラリとしてパッケージングしてしまうと、どうしてもいろんな人のニーズにこたえるためコードが長くなりがちだと思います。
いや、もちろん各ライブラリはよく利用させていただいているので、ライブラリそのものを否定しいるんじゃなくて、俺俺ライブラリで俺しか使わないけど、メンテが楽なんていうのも、アリかなと思ったりした次第であります。

最後に今回のソースをアップしました。興味のある方はみてみてください。

では!


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る