Home > ActionScript

ActionScript Archive

extends Progression 拡張機能コンテストで Progression 開発者賞受賞

半年ほどブログ放置してすいません・・・

extends Progression 拡張機能コンテスト
http://extends.progression.jp/

Progression 開発者賞というものをいただいてしまいました!
感謝感激です。
しかも、予定にはなかった Progression 開発者賞という賞を新設していただくという暖かい配慮に心より感謝感謝です。
extends Progression 拡張機能コンテスト関係者の皆様本当にありがとうございました!

今回応募したものは LiquidLayout というライブラリです。
フルフラッシュサイトを作るときに、AS3になってからもっとやっかいになってしまった表示オブジェクト等の配置をカンタンにしちゃえというライブラリです。
ExDocument をルートシーンに使用していれば、LiquidLayout も利用可能という代物になっており、Progression に限らず利用できてしまうので、このあたりが Progression 開発者賞を新設していただけるという関係者の皆様を困惑させてしまう結果になってしまったようです。taka:niumさんすいません!

提出したプレゼン資料
(Slideライブラリ使用。左右キーで移動します)

サンプルデモ
(設定を変えたり、画面をぐりぐりリサイズしてご確認ください)

ソース一式
(サンプルデモで使用したクラスファイルも同梱しています)

Tweener を同じような構造をとっており、addLayout メソッドでオブジェクトとパラメータを追加して使用します。
数値で指定した画面位置からの距離、数値を文字列で渡すことによりパーセンテージでの指定が可能です。

Tweener ライブラリと大きく異なる部分は、Tweener は静的メソッドでアクセスするのに対し、LiquidLayout はインスタンスを生成して使用します。

var lauout:LiquidLayout = new LiquidLayout( document );
layout.addLayout( mc, { center: 0, middle: 0 } );

この部分は最後まで悩んだというか今現在でも悩んでいるのですが、オブジェクトの最大幅・最小幅・最大高さ・最小高さなどを、インスタンス本体が持っていることにあります。
もし、あるコンテンツだけ異なる設定をしたいとなった場合に、静的では対応が難しかったためインスタンス生成する形をとりました。

Flasherの方々はなにかしらのライブラリを使用されていると思いますが、もし LiquidLayout 気に入った!となれば使っていただけると幸いです。
また、こうしたほうがいいんじゃない?などご意見いただけるともっと幸いです。

Continue reading

ProcessEvent イベントフローについて(2)

前回のままだと、特殊な使い方すぎてイベントフローの検証になってないので、ユーザーイベントでの遷移に変更して検証。

前回のクラスに移動用ボタンを設置したIndexクラスを作成します。

package
{
	import jp.progression.casts.CastButton;
	import jp.progression.casts.CastDocument;
	import jp.progression.commands.AddChild;
	import jp.progression.commands.Goto;
	import jp.progression.commands.RemoveChild;
	import jp.progression.commands.Trace;
	import jp.progression.Progression;
	import jp.progression.scenes.SceneId;
	import jp.progression.scenes.SceneObject;

	public class Index extends CastDocument
	{
		public var prog:Progression;

		public function Index()
		{

		}

		protected override function _onInit():void
		{
			prog = new Progression( "index", stage );

			prog.onProcessStart     = function() { trace( "onProcessStart :" + prog.eventType ); }
			prog.onProcessScene     = function() { trace( "onProcessScene :" + prog.eventType ); }
			prog.onProcessComplete  = function() { trace( "onProcessComplete :" + prog.eventType + "\n------" ); }
			prog.onProcessEvent     = function() { trace( "onProcessEvent :" + prog.eventType ); }
			prog.onProcessError     = function() { trace( "onProcessError :" + prog.eventType ); }
			prog.onProcessInterrupt = function() { trace( "onProcessInterrupt :" + prog.eventType + "\n------" ); }

			var b1:CastButton = new CastButton();
			b1.graphics.beginFill( 0xFF0000 );
			b1.graphics.drawRect( 0, 0, 100, 100 );
			b1.sceneId = new SceneId( "/index/scene2" );

			var b2:CastButton = new CastButton();
			b2.graphics.beginFill( 0x0000FF );
			b2.graphics.drawRect( 0, 0, 100, 100 );
			b2.sceneId = new SceneId( "/index/scene3" );

			prog.root.addScene( new SceneObject( "scene1", {
				onLoad: function():void {
					this.addCommand(
						new Trace( "scene1 : onLoad" )
					);
				},
				onInit: function():void {
					this.addCommand(
						new Trace( "scene1 : onInit" ),
						new AddChild( prog.container, b1 )
					);
				},
				onGoto: function():void {
					this.addCommand(
						new Trace( "scene1 : onGoto" ),
						new RemoveChild( prog.container, b1 )
					);
				},
				onUnload: function():void {
					this.addCommand(
						new Trace( "scene1 : onUnload" )
					);
				}
			} ) );

			prog.root.addScene( new SceneObject( "scene2", {
				onLoad: function():void {
					this.addCommand(
						new Trace( "scene2 : onLoad" )
					);
				},
				onInit: function():void {
					this.addCommand(
						new Trace( "scene2 : onInit" ),
						new AddChild( prog.container, b2 )
					);
				},
				onGoto: function():void {
					this.addCommand(
						new Trace( "scene2 : onGoto" ),
						new RemoveChild( prog.container, b2 )
					);
				},
				onUnload: function():void {
					this.addCommand(
						new Trace( "scene2 : onUnload" )
					);
				}
			} ) );

			prog.goto( new SceneId( "/index/scene1" ) );
		}
	}
}

まず /index/scene1 に行くと赤いボタンが配置され、クリックすると /index/scene2 に飛び青いボタンが表示され、そのボタンをクリックすると設定されていない /index/scene3 に遷移するよう要求しエラーを発生させます。

出力結果

onProcessStart :null
onProcessScene :load
onProcessEvent :load
onProcessEvent :descend
onProcessScene :load
onProcessEvent :load
scene1 : onLoad
onProcessEvent :init
scene1 : onInit
onProcessComplete :init
------
onProcessStart :init
onProcessEvent :goto
scene1 : onGoto
onProcessEvent :unload
scene1 : onUnload
onProcessScene :load
onProcessEvent :load
scene2 : onLoad
onProcessEvent :init
scene2 : onInit
onProcessComplete :init
------
onProcessStart :init
onProcessEvent :goto
scene2 : onGoto
onProcessEvent :unload
scene2 : onUnload
[ERROR] 移動先のシーンが存在しません, 目的地 = /index/scene3
onProcessError :load

scene2 の onInit イベント内で中断実行を行う処理に変更してみる。

onInit: function():void {
	this.addCommand(
		new Trace( "scene2 : onInit" ),
		function():void { this.interrupt(); },
		new Trace( "中断実行後" )
	);
}<

出力結果

onProcessStart :null
onProcessScene :load
onProcessEvent :load
onProcessEvent :descend
onProcessScene :load
onProcessEvent :load
scene1 : onLoad
onProcessEvent :init
scene1 : onInit
onProcessComplete :init
------
onProcessStart :init
onProcessEvent :goto
scene1 : onGoto
onProcessEvent :unload
scene1 : onUnload
onProcessScene :load
onProcessEvent :load
scene2 : onLoad
onProcessEvent :init
scene2 : onInit
中断実行後
onProcessInterrupt :init
------

interrupt メソッドに引数 true を渡して、強制中断実行に変更するとそのあとの処理は実行されず終了する。

function():void { this.interrupt( true ); }
  • onProcessStart で始まり正常終了した場合は onProcessComplete で終わる
  • onProcessEvent と SceneObject イベントは対で実行され、onProcessEvent が常に先に実行される
  • onProcessScene イベントはシーンに入る直前に実行される
  • 階層化されていれば通過シーン到達直前もすべて onProcessScene は実行される

ProcessEvent イベントフローについて(1)

Sceneでのイベントフローはいろんな方が書いておられるので非常に勉強になりますが、ProcessEventについてはあまり見つけられなかったので、試してみました。

Progressionインスタンスのイベントハンドラメソッドには

  • onProcessStart
  • onProcessScene
  • onProcessComplete
  • onProcessEvent
  • onProcessError
  • onProcessInterrupt

が用意されています。
これがSceneObjectのonLoadやonInitからみてどんなタイミングで実行されるのかテストするために、次のようなIndexクラスを作成します。

package
{
	import jp.progression.casts.CastDocument;
	import jp.progression.commands.Goto;
	import jp.progression.commands.Trace;
	import jp.progression.Progression;
	import jp.progression.scenes.SceneId;
	import jp.progression.scenes.SceneObject;
	
	public class Index extends CastDocument
	{
		public var prog:Progression;
		
		public function Index()
		{
			
		}
		
		protected override function _onInit():void
		{
			prog = new Progression( "index", stage );
			
			prog.onProcessStart     = function() { trace( "onProcessStart :" + prog.eventType ); }
			prog.onProcessScene     = function() { trace( "onProcessScene :" + prog.eventType ); }
			prog.onProcessComplete  = function() { trace( "onProcessComplete :" + prog.eventType ); }
			prog.onProcessEvent     = function() { trace( "onProcessEvent :" + prog.eventType ); }
			prog.onProcessError     = function() { trace( "onProcessError :" + prog.eventType ); }
			prog.onProcessInterrupt = function() { trace( "onProcessInterrupt :" + prog.eventType ); }
			
			prog.root.addScene( new SceneObject( "scene1", {
				onLoad: function():void {
					this.addCommand(
						new Trace( "scene1 : onLoad" )
					);
				},
				onInit: function():void {
					this.addCommand(
						new Trace( "scene1 : onInit" ),
						// scene2へ移動
						new Goto( new SceneId( "/index/scene2" ) )
					);
				},
				onGoto: function():void {
					this.addCommand(
						new Trace( "scene1 : onGoto" )
					);
				},
				onUnload: function():void {
					this.addCommand(
						new Trace( "scene1 : onUnload" )
					);
				}
			} ) );
			
			prog.root.addScene( new SceneObject( "scene2", {
				onLoad: function():void {
					this.addCommand(
						new Trace( "scene2 : onLoad" )
					);
				},
				onInit: function():void {
					this.addCommand(
						new Trace( "scene2 : onInit" ),
						// 強制的にエラーを発生させる
						new Goto( new SceneId( "/index/scene3" ) )
					);
				},
				onGoto: function():void {
					this.addCommand(
						new Trace( "scene2 : onGoto" )
					);
				},
				onUnload: function():void {
					this.addCommand(
						new Trace( "scene2 : onUnload" )
					);
				}
			} ) );
			
			prog.goto( new SceneId( "/index/scene1" ) );
		}
	}
}

出力結果

onProcessStart :null
onProcessScene :load
onProcessEvent :load
onProcessEvent :descend
onProcessScene :load
onProcessEvent :load
scene1 : onLoad
onProcessEvent :init
scene1 : onInit
onProcessStart :init
onProcessEvent :goto
scene1 : onGoto
onProcessEvent :unload
scene1 : onUnload
onProcessScene :load
onProcessEvent :load
scene2 : onLoad
onProcessEvent :init
scene2 : onInit
onProcessStart :init
onProcessEvent :goto
scene2 : onGoto
onProcessEvent :unload
scene2 : onUnload
[ERROR] 移動先のシーンが存在しません, 目的地 = /index/scene3
onProcessError :load
onProcessInterrupt :load
onProcessInterrupt :load

想像通りですが、SceneEvent が発生した際には onProcessEvent が実行されてから、シーンの各イベントハンドラが実行されるようです。
ただこの検証だとscene1に到達した瞬間のコマンドリスト内で強制的にscene2へGoToで飛ばしているので、中断実行であるonProcessInterrupt が入ると思っていたんですが、scene1 : onInitの次は onProcessStart :init が実行されてますね。
多分最後に2つ出力されているonProcessInterrupt のどちらかがこれに相当するのでしょうか?

scene1 の onGoto 内に new Wait( 1000 ) のようにWaitコマンドを挟むと、onProcessInterrupt がその時点で出力されます。もしかすると中断実行も非同期処理になっているのかな?

Continue reading

共有オブジェクトのリンケージクラスを生成するときの対策

共有オブジェクトとして書き出したリンケージクラスを、読み込み側SWFでインスタンス生成するときにはまったのでメモ。

起こったこと。

  1. MovieClipクラスを継承したSheardMovieClipクラスを作成します
  2. Flash上でムービークリップを作成し、いくつかのムービークリップを配置します。
    そしてそれらにインスタンス名をつけます。(ここではmc1,mc2とします。)

    SheardMovieClip (クラス名:SheardMovieClip、ランタイム共有用に書き出し)
    ├mc1 (インスタンス名:mc1)
    └mc2 (インスタンス名:mc2)
  3. 共有ライブラリSWFを書き出し
  4. このSheardMovieClipを読み込み側Flashにコピーして、プロパティでランタイム共有用に読み込みとなるよう設定します。
  5. そして読み込み側でインスタンス生成します。
    new SheardMovieClip();
    

として読み込み側SWFを書き出すとReferenceErrorが発生!

ReferenceError: Error #1056: SheardMovieClip のプロパティ mc1 を作成できません。

共有ライブラリSWFを書き出すときには何もなかったのに、共有ライブラリとして読み込むと、参照できないというか中のムービークリップの名前(プロパティ)を作成できないと言われる。
SheardMovieClipクラスに参照プロパティを作ってみる。

public var mc1:MovieClip;
public var mc2:MovieClip;

そして読み込み側SWFを再度書き出し。

お、何も言われない。

と思って、今度は共有ライブラリSWFを書き出してみると

1151: 定義 mc1 (名前空間 internal) にコンフリクトが存在します。

を怒られる。。
まぁそりゃそうだわな~と思いながら10分ほど悩む。

共有ライブラリFlashのパブリッシュ設定で「ActionScript設定」→「ステージのインスタンスを自動宣言」のチェックをはずすことで解決しました。

もしくはSheardMovieClip クラスをdynamicクラスにして、インスタンスを取得するときはgetChildByNameで参照する方法もあり。

PreloaderとListenコマンド(2)

前回に引き続き、もう少しだけListenコマンドを触ってみる。

コンテンツに入る前にイントロムービーを流したいなどはよくある話だと思います。
メインコンテンツを長々読み込んだあとに、またイントロ用のローディングなんてのはあまりふつくしくないので、Preloaderでメインコンテンツを読み込む前に流してしまおうというテストです。

ムービーが再生終了した後にメインコンテンツを読み込むようにします。
しかしイントロムービーの場合往々にしてスキップボタンも必要になってくることが多いと思います。
そのどちらものイベントでコマンドを抜けるようにすればいいので、ほかのイベントと干渉しないよう新たにイベントクラスを作ってしまうほうが確実で簡単かと思います。

public class PreloadEvent extends Event
{
	
	public static const PRELOAD_COMPLETE:String = "preloadComplete";
	
	public function PreloadEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
	{
		super(type, bubbles, cancelable);
		
	}
	
	public override function clone():Event
	{
		return new PreloadEvent(type, bubbles, cancelable);
	}
	
	public override function toString():String
	{
		return formatToString("PreloadEvent", "type", "bubbles", "cancelable", "eventPhase");
	}
	
}

と、完了イベントを持つイベントクラスを用意して2つのイベントが起こった際に、preloadCompleteを送出するようにします。

private function _introComplete( e:Event ):void
{
	// PreloadEvent.PRELOAD_COMPLETEイベントを送出
	dispatchEvent( new PreloadEvent( PreloadEvent.PRELOAD_COMPLETE ) );
}

protected override function _onCastLoadStart():void {
	
	var player:FLVPlayback = FLVPlayback( getChildByName( "introPlayer" ) );
	player.addEventListener( VideoEvent.COMPLETE, _introComplete );
	
	skip.addEventListener( MouseEvent.CLICK, _introComplete );
	
	player.play( "intro.flv" );
	
	addCommand(
		new Listen( this, PreloadEvent.PRELOAD_COMPLETE, { timeOut: 0 } ),
		new Trace( "イントロが終了しました" ),
		new RemoveChild( this, player ),
		new RemoveChild( this, skip )
	);
}

{ timeOut: 0 }は忘れずに。(よく忘れる・・・オデ)

んでテキトーにイントロで使ったものを削除すれば、このあとメインコンテンツのローディングが開始されます。

ムービー再生中にバックグラウンドでメインコンテンツを読み込んだらもっとカコいいけどね。

PreloaderとListenコマンド(1)

PreloaderクラスはProgression2.0ではExPreloaderを継承していたものが、Progression3.0になって新たにCastPreloaderを継承することとなり大きく変わりましたね。

イベントの流れとしては

_onInit (読み込み完了 / ステージの設定など)
  ↓
_onCastLoadStart (ロードが開始される直前)
  ↓ ←登録されたCommandListの全てのコマンドの処理が
  ↓  完了すれば次へ
[メインSWFの読み込み開始]
  ↓
_onProgress (プログレスバーの表示など)
  ↓
_onCastLoadComplete
  ↓ ←登録されたCommandListの全てのコマンドの処理が
  ↓  完了すれば次へ
[メインSWFをドキュメントの表示リストに追加]

という流れらしい。
なので以前はロードが開始して読み込み終わるまで止めることのできなかったので、本当にローディングの処理しかできなかったけれど、ロード開始直前とロード完了直後のCommandListにより、幅広い表現ができますね。

たとえば、
「このコンテンツには音声が含まれます。ヘッドフォンのボリューム上げてね」みたいなアテンションとか、「HTML版」「Flash版」みたいな振り分けページがFlashページでできる。
早く見たいときに散々メインコンテンツ読み込んでからでないとHTML版の表示ができないのは本当にストレスですからねぇ。。。

そんなとき使えるのがListenコマンド!
Preloaderとか、ページ遷移の前に規約を読ませるみたいなわざわざScene作るのがめんどくさい時くらいしか使い道が思いつかないListenコマンドw
でも使い出すとすごく便利かもしれない。

Listenコマンドはイベントを送出するdispatcherと受け取りたいイベントの種類の名前を渡すことで、そのイベントが起こるまで待機してくれます。
flashButtonとhtmlButtonというインスタンスがあるとして

protected override function _onCastLoadStart():void {
	htmlButton.addEventListener( MouseEvent.CLICK, function():void { navigateToURL( new URLRequest( "html.html" ) ); } );
	addCommand(
		[
			new AddChild( this, flashButton ),
			new AddChild( this, htmlButton )
		],
		new Listen( flashButton, MouseEvent.CLICK, { timeOut: 0 } ),
		new Trace( "Flash版ボタンがクリックされました" ),
		[
			new RemoveChild( this, flashButton ),
			new RemoveChild( this, htmlButton )
		]
	);
}

とすれば、Flash版のボタンを押されたときにのみメインコンテンツのロードを行います。
Listenコマンドを使用する際は、第3引数でtimeOutを”0″にしておかないとデフォルト15秒でCommandTimeOutErrorエラーが発生しコマンドリストが終了します。
(ただ終了しても問題なく動くように見えるんですけどね。。。でもエラーが出るのはいやなので設定必須)

次は複数のlistenerを管理する場合のことについて書いてみます。

SceneObjectのaddSceneFromXMLについてのメモ

Progression3都物語で勉強してきたことやけど、よく忘れそうなので今になってメモ。

protected override function _onLoad():void {
	// 実行したいコマンドを登録します。
	addCommand(
		// XML ファイルを読み込みます。
		new LoadURL( new URLRequest( "index.xml" ) ),
		// 匿名関数を実行します。
		function():void {
			// 読み込んだ XML ファイルから子シーンを作成します。
			addSceneFromXML( new XML( this.latestData ) );
		}
	);
}

とする際、XMLのcls属性のString型からgetDefinitionByNameでクラスの参照を生成する際に、「クラス名が定義されてないよ」とReferenceErrorを投げ返してきます。

なので、コンストラクタなどで

public function IndexScene( name:String = null, initObject:Object = null ) {
	// スーパークラスを初期化します。
	super( name, initObject );

	// クラスが SWF ファイルに含まれるように参照しておきます。
	MyScene1;
	MyScene2;
	MyScene3;
	MyScene4;
}

という感じで、ただ参照するためにクラス名のみを書いておくとエラーなく動作します。

ホーム > ActionScript

Search
Feeds
Meta

Return to page top