2009 年 5 月 のアーカイブ

CSS Nite in OSAKA, Vol.16 出演します

2009 年 5 月 28 日 木曜日 正宗
CSS Nite in OSAKA, Vol.15

CSS Nite in OSAKA, Vol.16

6月9日、18:30~20:30
アップルストア心斎橋 2F
CSS Nite in OSAKA, Vol.16に出演します。

詳細はバナーリンクか、こちらのページでご確認下さい。
CSS Nite in OSAKA, Vol.16

僕のしゃべる内容としては、「これから始めるFlashActionScriptツボのツボ」と題しまして、今までActionScriptに触れたことがない方を対象として「こう書けばすぐ動く」スクリプトをご紹介したいと思います。

僕は今までも大阪でFPSというイベントを行ったりしてきましたが、FPSはもともとFlash好きが集まる前提で開催されますが今回CSSNiteというWEB標準技術を追求する方々の前でお話しさせて頂くと言うこともあり、皆さんが全員Flashに精通されてる方ばかりでもないと思い、FlashActionScriptのかなり初歩的なところの紹介になるかと思います。

ActionScriptを駆使すれば高度なモーションやWEBアプリケーションの作成が可能になることは既に皆さんもご存じですが、今回は「WEBサイトに必要なActionScript」に絞ってボタンインタラクション(ボタンを押したときどう動かすか)だったり、デザインやモーションに必要なXY座標とWidth(幅)Height(高さ)のコントロールについてズバリそのもののScriptをご提供します。

もしかしたら既にActionScriptを学ばれてる方にとっては退屈かもしれませんが、これから挑戦したいという方にとっては、先ずは書いて動かして覚えられる良い機会になれば幸いです。

そのほか、AS2までを学ばれてる方にとって最低これだけは覚えておけばいいAS3との違いなどもまとめる予定です。こちらは以前このブログにまとめておこうと思ってほったらかしにしていた部分なので今回が蔵出しになります。

それでは当日お会いできることを楽しみに準備を進めていきます。お時間のある方はどうぞお越し下さい。
(もちろん懇親会も出る予定です。というかセミナーは出なくても飲み会には必ず顔を出す派)

Progressionでコンポーネントスタイルにクラスを連携させる

2009 年 5 月 25 日 月曜日 正宗

こんにちは。最近引っ越しましたが取引先にまだ挨拶を済ませていない正宗です。前回に引き続きProgressionの話題をしたいと思います。

Progressionではスクリプトを一切使わない「コンポーネントスタイル」やタイムラインスクリプト派の「タイムラインスタイル」、クラスでわしわし書きたい人向けの「クラススタイル」など制作者のスタイルにあわせたとても柔軟な開発スタイルが用意されていますが、今回はその中でも「コンポーネントスタイル」と「クラススタイル」を連携させた開発手法についてまとめてみました。

(続きを読む...)

ProgressionのCommandが良い感じ

2009 年 5 月 21 日 木曜日 正宗

こんにちは。
正宗です。
今日は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マスター!

URLRequestでキャッシュさせない方法

2009 年 5 月 6 日 水曜日 正宗

こんにちは。正宗です。
今日はURLRequestのキャッシュの話をしたいと思います。

よくURLLoaderクラスなんかでキャッシュをさせない方法としてURLRequestのURLの末尾にランダムな数字を入れるという方法が紹介されています。
こんなやつ

 
new URLRequest("http://exsample.com/?rnd=" + int(Math.random() * 1000));
 

バナーとかサイトトラッキングなんかのクエリ発行でもよく使われていますね。

これは、通常キャッシュはURLをそのままファイル名として保存しているため、アクセスする際に毎回末尾にランダムな数字をパラメーターとして与えることで、キャッシュファイルを別々のファイルとして保存させておくことで有効な方法ですが、少しだけ不安が残ります。

それはキャッシュ対策として int(Math.random() * 1000)などの0~999などの整数値を使用した場合、1000回に1回は同じ数字を生成する可能性があるので、そのときにキャッシュを拾ってきてしまうんじゃないかという不安です。
整数じゃなくてそのままMath.random()とすることも出来ますが、結局同じ数字を生成する可能性をコントロールできていない以上根本的な解決になっていないかもしれません。

URLRequestHeaderでpragma:no-cacheを指定するという方法もありますが、IEなんかでどうもキャッシュしてしまったりで、こちらもなかなか使いづらいものとなっています。
(何より実験でRSSリーダーなんかをチャチャッと作るときとか面倒臭いし…)

そこで僕がいつも使っているキャッシュをさせない方法を紹介したいと思います。

 
new URLRequest("http://exsample.com/?rnd=" + (new Date).time);
 

これだと、末尾の数値に1970 年 1 月 1 日 0 時のミリ秒が付加されますので、1000分の一秒以内に2回以上アクセスを行わない限りはキャッシュを読み込まないことになります。

キャッシュさせない方法としてランダムな数字を使うより時間の数値を使う方が有効なのは、結構簡単なことですがあんまり紹介されてないので、よかったら参考にしてみて下さい。