Progression 4の最近のブログ記事

Progression Home

|

Progressionはこちら
Progression Forumはこちら

Progressionのドキュメントはこちら
「タイムラインスタイルガイド」はちょっとわかりにくいですね。

要はムービークリップの中に今まで通りにタイムラインのアニメーションを作り、そのシンボルをスクリプトでインスタンス化して管理していくわけです。
Progressionでは1ページ分のアニメーションを1シーンとして数えるので、1ページごとの登場から退場までのエフェクト付きのムービークリップを作ります。

タイムラインスタイルでは、シンボルのインスタンス化やイベント処理のスクリプトはユーザーが記述する必要があります。スクリプトについては、サンプルファイルをダウンロードして、それとWebページのドキュメントを読み比べると混乱が少ないかもしれません。
スクリプト自体はProgression独自のものを使いますが、AS3の基礎知識がそのまま必要です。クラス定義の知識は必要としませんが、AS3を理解していないとハードルが高いかもしれません。

面倒でも先に「コンポーネントスタイルガイド」を試しておくと理解が早いです。コンポーネントスタイルでは、ムービークリップの登場と退場のエフェクトも含め、ステージへの配置もコンポーネントが管理します。

Progression4 Beta

|

Progression4 Betaがリリースされました。
ダウンロードと資料は「Progression 4 Public Beta」にあります。

Progression3を使っている人は、アンインストールが必要です!
アンインストールの方法はProgression3のセットアップガイドにあります。

Progression4のDevToolsでProgression用ライブラリテンプレートを作る方法が紹介してあるブログ。

northprintさん。Is It So Easy?
Progression4パブリックベータ開始!そんでDevToolsでProgression用ライブラリテンプレートを作る

Progression 4のコンポーネントスタイル、タイムラインスタイル、クラススタイルの基本機能を確認するためにProgression 3の公式スタイルガイドのサンプルをProgression 4に移植してみたんですが、いろいろひっかかりまくりです。

Progression 3の公式スタイルガイド

基本的にProgression 4のプロジェクトパネルの新規プロジェクトで最初のファイル構成を作り、そこからスタートしています。

Flash007.jpg

ぼくなりに移植したかっこうなので、公式(最適なスタイル)ではないので、そこんところよろしくお願いします。それぞれ、どこにハマってどう解決するかについてはもう少しわかってから書きます。
以下のファイルは、とりあえず、結果オーライっていう感じかな?


コンポーネントスタイル
Safari001.jpgswfを試す

完成形のサンプルファイル一式→component_styleguide_prog4beta.zip


タイムラインスタイル
タイムラインスタイルは、まだハマったままです。完成に至ってません。
試行錯誤の結果、index.fla単独では動くものの(ちょっと怪しいとこある)、preloaderから呼ぶとコケます。Progressionの初期化のタイミングに問題がありそう。
フレームアクションとコンポーネントの併用は意外と難しいのか?
ちょっとしたことだとも思うのだが・・・

Flash003.jpg

とりあえずのサンプルファイル一式→timeline_styleguide_prog4beta.zip


クラススタイル
ある意味、クラススタイルが一番すっきりしている気もするけど、まだちょっと慣れないなあ。
prog4_class.jpgswfを試す

完成形のサンプルファイル一式→class_styleguide_prog4beta.zip"

昨日暫定的にアップしたサンプルファイルですが、さらにいろいろ試して解決を見ました。解決と言ってもしっくりこない部分もあるんですけど・・

コンポーネントスタイル
コンポーネントスタイルガイドのサンプルは昨日のまま変更ありません。

完成形のサンプルファイル一式→component_styleguide_prog4beta.zip
swfを試す

テンプレートスタイル
テンプレートスタイルガイドのサンプルは、昨日の物はpreloaderから移動できないというものでしたが、なんとか解決です。これでいいんでしょうか??
スクリプト的には間違ってないと思いますが、Progressionのスタイルとしてこれでいいのか?と疑問があります。もっとスマートな方法があるような気がします。
新規プロジェクトで生成されるテンプレートを元に作っているから悩むということもあるわけですが・・・・

というわけで、webで動くサンプルができました。
timelinesample.jpg
swfを試す

完成形のサンプルファイル一式→ timeline_styleguide_prog4beta_v2.zip


クラススタイル
クラススタイルガイドのサンプルは一応動作してたけど、class_styleguide_prog4.jsflからプロジェクトを開くとプロジェクトパネルでうまく管理されないという不具合がありました。

この原因が判明しました。新規プロジェクトからクラススタイルを選ぶとsrcフォルダにflaとasファイルが作られますが、これをクラススタイルガイドのサンプルに合わせてasファイルをmyprojectパッケージに入れたことに原因がありました。

パッケージに合わせてclass_styleguide_prog4.jsflのほうを更新するのがスジのような気もしますが、やり方がわからないので最初から作られるasファイルは元の階層に戻しました。

完成形のサンプルファイル一式→class_styleguide_prog4beta_v2.zip"
swfを試す

Progression 4ではCastDocumentクラスのコンストラクタがProgression 3から変更があって、以前とは違う開発手法が使える。CastDocumentを継承したIndexクラスのインスタンスが作られた時点でProgressionインスタンスが作られる。
つまり、あらためてProgressionインスタンスを作らず、25行目のsuper()でCastDocumentコンストラクタに引数を渡す方法で初期化する。

CastDocument(managerId:String = null, rootClass:Class = null, config:Configuration = null, initObject:Object = null)

[:script:]Index.as
package {
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.config.*;
	import jp.progression.data.*;
	import jp.progression.debug.*;
	import jp.progression.events.*;
	import jp.progression.scenes.*;
	
	/**
	 * ...2009.09.28
	 * @author ...oshige
	 */
	 //ドキュメントクラスなのでCastDocumentを継承
	public class Index extends CastDocument {
		
		public function Index() {
			// CastDocumentコンストラクタがProgression インスタンスの初期設定を行う。
			// 生成されたインスタンスにアクセスする場合には manager プロパティを参照する。
			//Progression id="index"になる。IndexSceneインスタンスname="index"が作られる。
			super( "index", IndexScene, new WebConfig() );
		}
		
		/**
		 * SWF ファイルの読み込みが完了し、stage 及び loaderInfo にアクセス可能になった場合に送出されます。
		 */
		protected override function atReady():void {
			// 開発者用に Progression の動作状況を出力します。
			//Debugger.addTarget( manager );
			
			// 外部同期機能を有効化します。
			//manager.sync = true;
			// 最初のシーンに移動します。
			//trace(manager.syncedSceneId); //  "/index"
			manager.goto( manager.syncedSceneId );
		}
	}
}

Indexクラスの25行のsuper()の第1引数で"index"を指定すると、Progression idが"index"になり、第2引数で指定したIndexSceneシーンのnameプロパティも"index"になる。第3引数のnew WebConfig()はWebサイト用の環境設定を行う。この時点でIndexSceneシーンが生成され、addScene()された状態になっている点にも注意が必要だ。
WebConfigクラスのコンストラクタ関数は次のとおり。

WebConfig(useSWFWheel:Boolean = true, useSWFSize:Boolean = true, useHTMLInjector:Boolean = true)

[:script:]IndexScene.as
package {
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.*;
	import jp.progression.events.*;
	import jp.progression.executors.*;
	import jp.progression.scenes.*;
	import flash.display.Sprite;
	
	/**
	 * ...2009.09.28
	 * @author ...oshige
	 */
	 //シーンなのでSceneObjectクラスを継承
	public class IndexScene extends SceneObject {
		private var box:Sprite;
		//
		public function IndexScene() {
			// シーンタイトル -- ブラウザのウィンドウタイトルとして表示される。
			title = "HelloWorld_1";
			box = new Sprite();
			box.graphics.beginFill(0xFF0000);
			box.graphics.drawRect(0,0,50,50);
			box.graphics.endFill();
			box.x = 100;
			box.y = 50;
		}
		
		//atSceneLoad > atSceneInitの順に発生。
		protected override function atSceneLoad():void {
			addCommand(
			);
		}
		protected override function atSceneInit():void {
			addCommand(
					   //boxスプライトの追加
					   new AddChild(container, box)
			);
		}
	}
}

7種類のテンプレート

|

Progression 4のプロジェクトパネルでクラススタイルを選んで新規プロジェクトを作ると、templatesフォルダに7つのasファイルと1つのxmlファイルが作られます。この8つのファイルは、Progression 4の考え方を理解する上で重要なヒントになりそうです。

テンプレートとして用意される7種類のasファイル

MyCastButton.as、MyCastMovieClip.as、MyCastSprite.as、MyCommand.as、MyIndex.as、MyPreloader.as、MySceneObject.as


7種類のスーパークラス
これらの使い分け、その機能の違いは、継承しているスーパークラスで判断できます。

  • プリローダーのドキュメントクラス
    MyPreloader.as --- CastPreloaderクラス
    プロジェクトにはPreloader.asが作ってある。

  • indexファイルのドキュメントクラス
    MyIndex.as --- CastDocumentクラス
    プロジェクトにはIndex.asが作ってある。

  • シーンを作る
    MySceneObject.as --- SceneObjectクラス

  • ムービークリップのスーパークラス
    MyCastMovieClip.as --- CastMovieClipクラス
    リンケージ書き出ししたムービークリップシンボルのスーパークラスとして指定する。シーン内のページとして使う。

  • スプライトを作る
    MyCastSprite.as --- CastSpriteクラス
    表示オブジェクトコンテナとしての利用。グラフィックを描く。シーン内のページとして使う。

  • ボタンを作る
    MyCastButton.as --- CastButtonクラス
    ボタンの機能を実装する。ボタンイメージとハイライトの動作もここで実装?

  • カスタムコマンドを作る
    MyCommand.as --- Commandクラス

テンプレートファイルにはありませんが、SceneObjectのサブクラスであって、swfをシーンとしてロードするSceneLoaderクラスも要チェックです。

また、CastTextFieldクラス、CastBitmapクラスもその名前が示すようにCastMovieClipクラスやCastSpriteクラスと同じような位置づけにあるようなので、それらのテンプレートファイルのコードや使いどころが参考になると思います。

シーンすなわちSceneObjectはコンテンツ階層構造の概念であって、表示オブジェクトコンテナではない。したがって、シーンに直接表示オブジェクトをaddChild()することはできない。

シーンに何かを表示するには、各シーンのcontainerプロパティに表示オブジェクトを追加する。

親シーンは子シーン、子シーンは孫シーンと階層を作ることができ、シーン間の移動が可能。しかし、シーンが移動したからと言って表示が変化するわけではなく、シーン移動イベントに応じてページの追加や削除を行う必要がある。

indexとIndexSceneは混乱を招く存在なので整理が必要だ。

新規プロジェクトを作成するとindex.flaとIndex.asが自動的に作られ、Indexクラスはindex.flaのドキュメントクラスに設定してある。

IndexクラスはCastDocumentクラスを継承したクラスで、インスタンス生成と同時にProgressionインスタンスも生成し、初期化する。

このとき、先のエントリーでも書いたようにsuper( "index", IndexScene, new WebConfig() )によって、Progressionのidが"index"になり、また、IndexSceneのシーン名も"index"に設定される。

indexに何かを表示しようと言うとき、それがシーンindexを指しているのならばIndexSceneクラスの中でcontainerプロパティにaddChild()する。

Indexクラスに話を戻すとIndexクラスのスーパークラス であるCastDocumentクラスはMovieClipクラスを継承していることから、Indexクラスにも表示オブジェクトを追加することができる。しかし、これは「できる」ということであって、Indexクラスに表示オブジェクトを追加する場合に、そこは"index"シーンではないことを理解していることが大事だ。"index"シーンは、IndexクラスにIndexSceneとして追加されたクラスである。

それからもう1つ。Progressionシーンエディタを使うと自動的に1番上位のシーンがindexになっている。そして、シーンを挿入するとindexシーンの子シーンとしてscene01が挿入され、indexと同じ階層に新しいシーンを追加するコマンドはない。また、indexシーンのタイトル(ブラウザのウインドウタイトル)は、初期値で"トップページ"と付けられている。
これにわかるように、Progressionではindexシーンを最上位と位置づけたツリー構造を想定している。

sceneEditor_index.jpg
トップページというタイトルでindexシーンが最初から作られている。

sceneEditor_scene0.jpg
indexにシーンを挿入すると子シーンとしてscene0が作られる。indexと同じ階層にシーンを挿入することはできない。

Progression 3のCastDocumentコンストラクタ関数
3.1 APIリファレンスより

public function CastDocument(initObject:Object = null)
新しい CastMovieClip インスタンスを作成します。

Parameters
initObject:Object (default = null) --
設定したいプロパティを含んだオブジェクトです。

Progression 4のCastDocumentコンストラクタ関数
Public Beta 1.0 APIリファレンスより

public function CastDocument(managerId:String = null, rootClass:Class = null, config:Configuration = null, initObject:Object = null)
新しい CastDocument インスタンスを作成します。

Parameters
managerId:String (default = null) --
自動的に生成される Progression インスタンスの識別子です。 省略した場合には SWF ファイル名が自動的に割り当てられます。

rootClass:Class (default = null) --
自動的に生成される Progression インスタンスのルートシーンに関連付けたいクラスの参照です。

config:Configuration (default = null) --
自動的に生成される Progression インスタンスの初期化情報として使用したい Configuration インスタンスです。

initObject:Object (default = null) --
設定したいプロパティを含んだオブジェクトです。

Progression 4にはmanagerプロパティが追加されている点にも注意。

manager:Progression [read-only]
関連付けられている Progression インスタンスを取得します。
次のスクリプトは、indexシーンにムービークリップシンボルのoshigeLogoを登場させる。シンボルはoshigeLogoのクラス名でリンケージ書き出しの設定をしておく。

oshigeLogoのインスタンスはIndexSceneコンストラクタで作っておきlogo変数に入れておく。これをatSceneInit()でcontainerに追加する。atSceneInit()はシーンの初期化イベントで呼ばれるハンドラである。

[:script:]IndexScene.as
package {
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.*;
	import jp.progression.events.*;
	import jp.progression.executors.*;
	import jp.progression.scenes.*;
	//Tweenerをimportする
	 import caurina.transitions.Tweener;

	public class IndexScene extends SceneObject {
		private var logo:oshigeLogo;
		public function IndexScene() {
			//nameはIndexクラスのsuper()で"index"が付けられる。
			// シーンタイトルを設定
			title = "IndexScene AddChild Logo";
			//index.flaでoshigeLogoをリンケージ書き出ししておく。
			logo = new oshigeLogo();
		}
		protected override function atSceneLoad():void {
			addCommand(
			);
		}
		//indexシーンにlogoを登場させる
		protected override function atSceneInit():void {
			logo.x = -logo.width;
			logo.y = 5;
			container.addChild(logo);
			Tweener.addTween( logo, { x:10, time:1 } );
		}
	}
}

このスクリプトのatSceneInit()は次のようにaddCommand()を使って書くこともできる。addCommand()の中に2行のステートメントがあるように見えるが、実際には,で区切った2個の引数である。

[:script:] addCommand()を利用してキャストの追加とTweenerを実行する。
		//indexシーンにlogoを登場させる
		protected override function atSceneInit():void {
			logo.x = -logo.width;
			logo.y = 5;
			addCommand(
					new AddChild(container, logo),
					new DoTweener(logo, {x:10, time:1})
			);
		}
ここではaddCommand()を使っても使わなくても違いが出ないが、次の例のようにoshigeLogoがCastMovieClipを継承していると、Progressionのイベントを受け取れるようになる。したがって、次のようにlogoを追加するコマンドだけを実行し、追加後のlogoの動作はCastMovieClipクラスを継承したoshigeLogoクラスに記述できるようになる。

[:script:]IndexSceneクラスではキャストの追加のみを行う。
		//indexシーンにlogoを登場させる
		protected override function atSceneInit():void {
			addCommand(
					new AddChild(container, logo)
			);
		}

CastMovieClipクラスを継承したoshigeLogoクラスのatCastAdded()で、シーンへの追加後の動作を設定する。

[:script:]CastMovieClipクラスを継承したoshigeLogoクラス
package {
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.*;
	import jp.progression.events.*;
	import jp.progression.scenes.*;
	
	/**
	 * ...2009.09.30
	 * @author ...oshige
	 * シンボルoshigeLogoと関連付けて利用する
	 */
	public class oshigeLogo extends CastMovieClip {
		public function oshigeLogo( initObject:Object = null ) {
			super( initObject );
			x = -width;
			y = 5;
		}
		//入場
		protected override function atCastAdded():void {
			addCommand(
				new DoTweener(this, {x:10, time:1})
			);
		}
		//退場
		protected override function atCastRemoved():void {
			addCommand(
				 new DoTweener(this, {x:-width, time:1})
			 );
		}
	}
}
HelloWorld_CastMovieClip.jpg
完成形のサンプルファイル一式→ HelloWorld_CastMovieClip.zip
swfを試す
SceneObjectクラスのgroupプロパティの使い方がわからないなあ、どこかにサンプルないかなあと思っていたら、Progression 4が試せるようになったWonderflでnorthprintさんが外部イメージ読み込みでgroupを使っていた。
探していたSceneObjectクラスのgroupプロパティとは違うけど、これがヒントになりそうだ。サンキュー!

    protected override function atSceneLoad():void {
        manager.root.stage.addEventListener(MouseEvent.CLICK, clickHandler);
        addCommand(
            new LoaderList(null,
                new LoadBitmapData(new URLRequest("http://assets.wonderfl.net/images/related_images/3/3e/3e3e/3e3e81e2e3ebefcc115e41ee5b95c2bb171e8019"), {context:new LoaderContext(true), group:"imageset"} ),
                new LoadBitmapData(new URLRequest("http://assets.wonderfl.net/images/related_images/6/62/6266/6266679ebb9179e0f434ccceed4cf0857bb7f8c9"), {context:new LoaderContext(true), group:"imageset"} )
            ),
            function():void {
                var count:int = int (getResourcesByGroup( "imageset" ).length);
                for (var i:int = 0; i < count; i++) {
                    var imageScene:ImageScene = new ImageScene("image" + i);
                    imageScene.title = "image" + String(i + 1);
                    imageScene.bitmapdata = getResourcesByGroup( "imageset" )[i].toBitmapData();
                    addScene(imageScene);
                }
            }

        );
    }

Progressionのページとは

|
Progressionのシーンエディタで[キャストを挿入する]を選ぶとシーンにIndexPageやScene0Pageといったページが挿入される。

page.jpg
[キャストを挿入する]を選ぶとシーンの下にページが挿入される

このことから、Progressionにはシーンの下位階層としてページがあると考えられる。シーンはSceneObjectクラスで作ることから、ページはPageObjectクラスで作り、ページを操作するメソッドやプロパティなどが揃っていると予想するだろう。
ところが、ProgressionにはPageObjectクラスはなく、したがってページを操作するメソッド類もない。しかし、スクリプトではpageを生成している次のようなステートメントをよく目にする。
page = new IndexPage();
やはりキャストとは別にページという扱いがあるのでは?と思うわけだが、IndexPageクラスはCastSpriteクラスを継承したクラスというように、ページの正体はCastSpriteやCastMovieClipなどのクラスであって、ページとキャストの区別が見えない。シーンの中にページがあって、その中にキャストを置くという構造ではなさそうだ。
public class IndexPage extends CastSprite
シーンエディタでも[キャストを挿入する]という名のメニューでページを挿入しており、シーンエディタのツールメニューの[シンボルに書き出す]を実行すると、ページと同じ名前のムービークリップシンボルが自動的に生成されることからも、ページはシーンに表示するムービークリップにすぎない。


sceneEditor_tool.jpg
ツールメニューの[シンボルに書き出す]を実行する

page_mc.jpg
シーンに挿入したキャストと同名のムービークリップシンボルが作られ

1シーンに複数のキャストを追加すると、それらは同時に表示されるオブジェクトになってしまってそのままではページのようにナビゲートする形になっていない。その意味ではむしろ、シーンこそがいわゆるページの概念に近いのではないかと思うのだがどうだろう?
前のエントリーで書いたようにgroupプロパティというものがページ管理に一役買いそうなので要チェックかな。

シーン移動

|
先のエントリーでページのことを書きましたが、Progressionのサンプルを見ると「1シーンには1ページしか作らない」という暗黙の了解の元に作られているようなので、そういうふうに取り決めてしまえば急に楽になりました。ページの中に複数の表示オブジェクトを追加することはあっても、シーン内でのページング(めくり)はナシです。慣例にならって、ぼくもページという言葉を使うことにします。

さて、Progression 4はシーン移動が肝なので、繰り返し同じようなサンプルを作っています。Progressionではシーンやシーンコンテンツ毎にクラスファイルを作るのでクラスファイルがすぐにたくさんできてしまいます。そうなるとパッケージングをどうすればいいか?と悩むわけですが、とりあえず決めましたよ。scenes、pages、uiに切ることにしました。

注: IndexScene.asをscenesパッケージに入れてない理由は、プロジェクト作成時に作られるjsflファイルを更新せずにIndexScene.asを移動させるとプロジェクトを開いたときに警告が出るため。更新方法がわからない。

どこでシーンを作るかということも決めました。最初に読み込まれるIndexクラスで作れば?という気もしますが、これも慣例にならってIndexSceneクラスで作ることに決定です。

[:script:] IndexSceneクラス
package {
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.*;
	import jp.progression.events.*;
	import jp.progression.executors.*;
	import jp.progression.scenes.*;
	//
	import flash.display.Sprite;
	import scenes.*;
	import pages.*;
	import ui.*;
	/**
	 * ...
	 * @oshige ...
	 */
	public class IndexScene extends SceneObject {
		private var scenePage:IndexPage;
		private var uiPanel:CastSprite;
		public function IndexScene() {
			// このシーンタイトル
			title = "Hello_Scenes";
			//このシーンの中身
			scenePage = new IndexPage();
			//子シーンを追加する
			var sceneA:SceneA = new SceneA();
			sceneA.name = "sceneA";
			addScene(sceneA);
			var sceneB:SceneB = new SceneB();
			sceneB.name = "sceneB";
			addScene(sceneB);
			//シーンナビゲートパネル
			uiPanel = new UIPanel();
			uiPanel.x = (stage.stageWidth- uiPanel.width)/2;
			uiPanel.y = stage.stageHeight + 50;
		}
		//indexシーンは1度しか読み込みが行われないので最初の1回しか実行されない
		protected override function atSceneLoad():void {
			//共通のuiを追加する
			addCommand(
					   new AddChild(container, uiPanel),
					    new DoTweener( uiPanel, { y:stage.stageHeight - 60, time:1 } )
			);
		}
		//シーンに入る enterScene
		protected override function atSceneInit():void {
			//シーンにコンテンツを追加する
			addCommand(
					   new AddChild(container, scenePage)
			);
		}
		//シーンを抜ける exitScene
		protected override function atSceneGoto():void {
			//シーンからコンテンツを取り除く
			addCommand(
					    new RemoveChild(container, scenePage)
			);
		}
		//indexシーンからは子シーンへ動くだけで外へ出ないので1度も実行されない。
		protected override function atSceneUnload():void {
			addCommand(
			);
		}
	}
}

hello_scenes.jpg
完成形のサンプルファイル一式→ Hello_Scenes.zip
swfを試す。


このサンプルで悩んだことの1つにボタンを常に最前面に表示しておく方法です。いったいどこにaddChild()すればいいのか?いろいろ試したところ、とりあえずの結論はaddChildAt()を使って(ProgressionコマンドではAddChildAt())各シーンのページをコンテナの0に追加するという方法をとりました。なんかしっくり来ないですけど、uiコンテナとpageコンテナを別に用意すればいいのではないか?と思うわけですが・・・

そんなことを思っていると今月のwonderflのCHECKMATEの出題者がniumさんでもちろんお題はProgression 4。Professionalのお題を見るとナビゲートボタンが最前面にあります。コレどうやってんの?と研究です。そして次に作ろうと思っていたシーンの相対移動もこのサンプルで使ってあります。ラッキー。

uiを最前面にする

|
niumさんのCHECKMATE課題でナビボタンを最前面にしている方法わかりました。stageに直接addChild()しています。stageはDisplayObjectContainerなので、addChild()できるのです。昔のFlashに_levelっていう考え方がありましたが、あれに近いです。
確かにこれなら最前面になりますが、wonderflの制限からこの手を使ったのか、これがProgressionの定石なのかちょっと謎。

[:script:]最前面になるようにstageに直接addChild()している。
_target.stage.addChild( frameBorder );
_target.stage.addChild( nextButton );
_target.stage.addChild( previousButton );

uiをstageに追加

|
先のサンプルのシーン移動ボタンをシーンページの前面に表示する方法を、CHECKMATEのnium式を真似てuiをstageに追加するスクリプトに変更してみました。SceneObjectにstageプロパティがあるのでそれを使います。これでいいのかという気もするけど、とりあえず、これでいいんじゃないの?

[:script:]IndexSceneクラス
	protected override function atSceneLoad():void {
		//共通のuiを追加する
		addCommand(
			new AddChild(stage, uiPanel),
			new DoTweener( uiPanel, { y:stage.stageHeight - 60, time:1 } )
		);
	}
これによって、子シーンでのページ追加はAddChildAt(container, scenePage, 0)ではなく、普通にnew AddChild(container, scenePage)でよくなりました。

完成形のサンプルファイル一式→ Hello_Scenes2.zip
swfを試す。
今日のテーマは「次のシーンへ移動」『手前のシーンへ移動』のように相対関係でシーンを移動する方法。あれこれ調べてたら、その名もNextButtonクラス、PreviousButtonクラスというものがあって、これを継承したボタンを作ればよろしいということが判明。

[:script:] NextButtonクラスを継承したNextSceneButtonクラス
public class NextSceneButton extends NextButton {
	var myButton:Button;
	public function NextSceneButton( initObject:Object = null ) {
		// 親クラスを初期化します。
		super( initObject );
		//ナビゲーションのための設定
		managerId  = "index"
		useTurnBack = true;

こりゃ楽ちんということでこれまで作ったサンプルを元に試したところ、なかなか思うように移動してくれない。あれこれ原因を探ってハタと気付いてしまいました。indexシーンを表示している状態で「次へ」「手前へ」とやっても、次も手前もシーンが存在しないのだった。そう。indexシーンの子シーンに移動してから、子シーンの間を横に行き来するわけですよ。というわけで、indexシーンではステージをクリックしたら1番目の子シーンへ移動するようにした。

[:script:] IndexSceneクラス
	//クリックで最初の子シーンへ移動
	private function mouseDownHandler(eventObj:MouseEvent):void {
		manager.goto(self.scenes[0].sceneId);
	}

next_scenes.jpg
swfを試す。
完成形のサンプルファイル一式→ Next_Scenes.zip

お気付きのように、次への移動と手前への移動のエフェクトが同じっていうのは、どうもいただけない。これ、スマートに解決したいところ。

と、それはそうとスクリプトが細切れにあちこちに分散しているのがどうにも限界のような気がしてきている。これがProgressionスタイルなのだろうか・・・いや、違うよね。
「次のシーンへ移動」『手前のシーンへ移動』において、移動方向によってエフェクトを変更するのが予想外に難しく、おかげていろいろ調べる結果になったのはよかったのだけど謎は増えてしまった。

あれこれ試行錯誤して、とりあえずの結果は出たのでアップしてこの件は終了。次のテーマに進んで別のことをやっているうちに、もっと簡単な方法に行き当たるかもしれないしね。

とりあえずどのように解決したのかと言えば、manager.departedSceneIdとmanager.destinedSceneIdというのを利用する。これはシーン移動する際に移動前と移動先のシーンを示すsceneIdを示すプロパティだ。これを比較して「次のシーンへ移動」なのか「手前のシーンへ移動」なのかを判断することにした。

sceneIdを見ただけではどっちが手前かなんてことはわからないので、manager.root.scenesでシーンの配列を調べ、これと照らし合わせることにした。しかし、scenesの配列の値はSceneObjectでsceneIdではないため、そのままでは比較できない。そこでSceneObjectからsceneIdを調べて比較すればと試したところ、なぜか比較がうまくいかない。では逆にsceneIdからSceneObjectに変換は?と調べたところ方法が見つからず。オブジェクトは同じ値のようでも内部で複製してある場合もあるわけで、比較が失敗する理由はそれかもしれないと予想し、最終的にsceneIdをストリングに変換して比較することにした。汎用化に成功していないのだけど、この段階で汎用化はまだ100年早い状況。

それにしても謎なのはSceneObjectのインスタンス。SceneObjectクラスを継承したSceneAクラスのインスタンスを作ったとき、SceneAクラスで拡張したプロパティにどうしてもアクセスできない。SceneAクラスでnoプロパティとかもてば、それを簡単に比較できると思ったのだけど・・・・。この件、保留だ。

[:script:] ScenePageクラスで移動前と移動先のシーンのインデックス番号を比較する
private function getSceneIds(item:*, index:int, array:Array):String {
	return item.sceneId.toString();
}

private function isForward():Boolean {
	var sceneIds:Array = manager.root.scenes.map(getSceneIds);
	var prevIndex:int = sceneIds.indexOf(manager.departedSceneId.toString());
	var nextIndex:int = sceneIds.indexOf(manager.destinedSceneId.toString());
	return nextIndex>=prevIndex;
}


next_scenes.jpg
swfを試す。
完成形のサンプルファイル一式→ Next_Scenes3.zip

jp.progression.casts

|

Cast〜っていう名前のクラスは、基本的にシーンに表示する表示オブジェクトのスーパークラスとして使うクラス。あるいはそれらのクラスで使用する定数を定義してあるクラス。その多くは標準の表示オブジェクトを拡張して作ってある。

jp.progression.casts

CastBitmap ----- Bitmapの拡張
CastButton ----- MovieClipの拡張
CastButtonState ----- CastButtonの状態を示す定数定義
CastButtonWindowTarget ----- CastButton.navitateTo()のtargetの定数定義
CastDocument ----- ドキュメントクラスのスーパークラスとして使う
CastImageLoader ----- イメージ読み込みに特化したLoaderの拡張
CastImageLoaderAlign ----- CastImageLoaderで使用する定数定義
CastImageLoaderRatio ----- CastImageLoaderで使用する定数定義
CastLoader ----- Loaderの拡張
CastMovieClip ----- MovieClipの拡張
CastPreloader ----- プリロード処理に特化した表示オブジェクト
CastSprite ----- Spriteの拡張
CastTextField ----- TextFieldの拡張

jp.progression.commands

|

これまた探すので一覧表示。自然と覚えますように。
*理解が進んだ時点で加筆修正してます。

jp.progression.commands

Break ----- 実行中の処理を中断する。
Command ------ 全てのコマンドのスーパークラス。
AddChild, Break, CommandList, DoExecutor, DoSound, DoTransition, DoTween, DoTweener, DoTweenFrame, Func, Goto, Jumpto, LoadCommand, NavigateToURL, Prop, RemoveAllChildren, RemoveChild, Return, SendToURL, Stop, Trace, Wait
CommandInterruptType ----- Command.interrupt()の中断方法を示す定数定義 。
CommandList ----- コマンドリストのParallelList, SerialListのスーパークラス。
DoExecutor ----- 引数で指定したExecutorObjectを実行する。
Func ----- 引数で指定した関数を実行する。
Prop ----- 複数のプロパティを設定する。
Return ----- 実行中の処理を中断する。
Stop ----- LoopList、ShuttleListのループ処理を停止させる。
Trace ----- trace()を実行する。
Wait ----- 指定ミリ秒数だけ処理を停止させる。new Wait(3)を数値だけの3に省略可能。


jp.progression.commands.display

AddChild ----- 表示リストに表示オブジェクトを追加する。
AddChildAt ----- 指定の重なり(不連続可)に表示オブジェクトを追加する。
AddChildAtAbove ----- 指定位置の上の重なりに表示オブジェクトを追加する。
RemoveAllChildren ----- 表示リストからすべての子表示オブジェクトを取り去る。
RemoveChild ----- 表示リストから子表示オブジェクトを取り去る。
RemoveChildAt ----- 表示リストから指定位置の子表示オブジェクトを取り去る。
RemoveChildByName ----- 名前で指定した子表示オブジェクトを取り去る。
RemoveChildFromParent ----- Parentの子だから自分と同じ階層の兄妹表示オブジェクトを取り去るかな?


jp.progression.commands.lists

LoaderList ----- 複数のLoadコマンドをSerialListで実行する。
LoopList ----- SerialListのサブクラスで複数のコマンド実行を繰り返す(回数指定可。0回指定でStop()までループ)。
ParallelList ----- 複数のコマンドを並行して実行する。
SerialList ----- 複数のコマンドを順に終了を待って実行する。
ShuttleList ----- 複数のコマンドを順に実行し終了したら逆順で繰り返す。往復する。(片道を1回と数えて回数指定。0回指定でStop()までループ)。
TweenList ----- 複数のコマンドを開始タイミングをずらしながら並行して実行する。タイミングはfl.motion.easing.*で指定する。

*ParallelListの中でコマンドリストを[]で囲むとその中はSerialListになる。
*SerialListの中でコマンドリストを[]で囲むとその中はParallelListになる。
*addCommand([[com1,com2],com3],com4)のように入れ子にすると[]の中でさらに反転。


jp.progression.commands.managers

Goto ----- 指定のシーンへ移動する。
Jumpto ----- 指定の孫シーンへ直接移動する。親シーン通過イベントが発生しない。


jp.progression.commands.media

DoSound ----- 指定したSoundオブジェクトを再生する。


jp.progression.commands.net

DownloadFileRef ----- ファイルをダウンロードする。
LoadBitmapData ----- 外部イメージファイルのビットマップデータを読み込む。
LoadCommand ----- このパッケージのLoad関連コマンドのスーパークラス。
LoadScene ----- SceneLoaderを使ってProgression使用のSWFを読み込む。
LoadSound ----- MP3を読み込む。
LoadSWF ----- Loaderを使ってSWFや画像を読み込む。
LoadURL ----- URLLoaderを使ってテキストデータを読み込む。
NavigateToURL ----- ブラウザでURLを開く。
SendToURL ----- データを送信する。
UploadFileRef ----- ファイルをアップロードする。

Tweener Online Document & Reference

|

addCooand()の中でTweenerを利用する。

DoTweener(target:Object, parameters:Object, initObject:Object = null)

ex.
new DoTweener( this, { alpha:1, time:1 } )
new DoTweener( sp, { scaleX:1, time:1 } )
new DoTweener( this, { x:20, time:1, transition:"easeinoutback"} )

Tweener Documentation and Language Reference
http://hosted.zeh.com.br/tweener/docs/en-us/

jp.progression.loader

|

そうそう、PRMLLoaderもチェック。
jp.progression.loader

EasyCastingLoader ----- PRMLLoaderのサブクラス。
PRMLLoader ----- 読み込んだPRML形式のXMLを読み込みProgressionを生成する。

jp.progression.events

|

イベントは大事。こんなクラスがある。
とくに、ProcessEventとSceneEventはちゃんと見なきゃね。

jp.progression.events

CastEvent ----- AddChildやRemoveChildなどのイベント。
CastMouseEvent ----- CastButtonで発生するMouseEvent。
DataProvideEvent ----- ?
ExecuteErrorEvent ----- Commandオブジェクトのエラーイベント。
ExecuteEvent ----- Commandオブジェクトの実行、完了、中断などのイベント。
ManagerEvent ----- manager(progressionインスタンス?)の状態変化のイベント。
ProcessEvent ----- SceneManagerの実行、完了、中断などのイベント。
PROCESS_CHANGE, PROCESS_COMPLETE, PROCESS_EVENT,
PROCESS_SCENE, PROCESS_START, PROCESS_STOP

SceneEvent ----- シーンフローの移動、シーンのロード関連イベント。
SCENE_ADDED, SCENE_ADDED_TO_ROOT,
SCENE_REMOVED, SCENE_REMOVED_FROM_ROOT,
SCENE_ASCEND, SCENE_DESCEND,
SCENE_GOTO, SCENE_INIT, SCENE_INIT_COMPLETE,
SCENE_LOAD, SCENE_POST_UNLOAD, SCENE_PRE_LOAD, SCENE_UNLOAD
SCENE_QUERY_CHANGE, SCENE_TITLE_CHANGE

Progression4とは直接は関係ない(こともない)けど、同梱されているパッケージには便利なクラスがあるので、手軽に使えるところをピックアップ。

jp.nium.collections

UniqueList ----- 重複するオブジェクトは追加されない配列。


jp.nium.utils

ArrayUtil ----- 配列比較、検索、複製、ストリング変換など。
ClassUtil ----- クラス名取得、クラスパス取得、パッケージ取得、属性チェック。
DateUtil ----- 月の日数を取得。日時の定数。
GraphicUtil ----- 基本図形を描画したSpriteを返すクラスメソッド。
MathUtil ----- 周期数値、偶数チェック、パーセント、レンジの関数。
MovieClipUtil ----- 関数を1フレーム遅れで実行、指定フレームが存在するか。
NumberUtil ----- 数値の桁数を0で揃える。1000桁ごとにカンマを付ける。
ObjectUtil ----- オブジェクトの複製、プロパティ一括設定。
StageUtil ----- ドキュメントクラスを調べる。左右上下のマージンを調べる。
StringUtil ----- 指定文字を改行コードに変換。文字を繰り返す。先頭を大文字。
URLUtil ----- 絶対パスに変換。拡張子を抽出。フォルダまでのパスを抽出。
Version ----- バージョン情報を扱うオブジェクト。バージョン比較。
XMLUtil ----- XMLListをObject表現(プロパティアクセス)に変換。

get_ByGroup()とget_ById()

|

getInstancesByGroup()やgetSceneById()のような特別な関数がある。

jp.progression.casts
getInstancesByGroup(group:String, sort:Boolean = false):Array

jp.progression.commands
getCommandById(id:String):Command

jp.progression.data
getResourceById(id:String):Resource

jp.progression.scenes
getSceneById(id:String):SceneObject

LoadSWFやらSerialListやら

|
見た目はいままでのサンプルと同じなんだけど、中身は大きく変わってます。
LoadSWFやSerialListとか試したり、シーンやページを定義する場所もいろいろ変えて、ファイル数もぐっと減ってます。ただ、いろいろダメな箇所があるのですよ。
それはうすうすわかっているのですが、先に進まないので問題ありファイルとして記録することに。

next_scenes.jpg
swfを試す。
完成形のサンプルファイル一式→ LoadSWF_pages1.zip

まず、シーンとして表示するコンテンツをシンボルからインスタンスを作って貼るんじゃなくて、SWFを読み込む方法をテストしたかったのです。シーンロードではなく、単純にページロードです。それをLoadSWFでやったんですが、読み込み中にシーンを表示するとエラーにならないのか?プログレスバーは?みたいな課題がそのままです。

[:script:]シーンのページを作るThePageクラス
public class ThePage extends CastMovieClip {
	public function ThePage( initObject:Object = null ) {
		// 親クラスを初期化します。
		super( initObject );
		//pageコンテンツをURLから読み込む(swf、画像)
		var list:SerialList = new SerialList();
		list.addCommand(
				     new LoadSWF(new URLRequest(initObject.url)),
						 function():void{
						 var loader:Loader = Loader( this.latestData );
						 addChild(loader);
					 }
		);
		list.execute();
	}

シーンを作るTheSceneクラスは次のとおり。

[:script:] TheSceneクラス
public class TheScene extends SceneObject {
	private var page:ThePage;
	public function TheScene( name:String = null, initObject:Object = null ) {
		// 親クラスを初期化する
		super( name, initObject );
		// このシーンタイトル
		title = initObject.title;
		//このシーンの中身
		page = new ThePage(initObject);
	}
	//シーンが読み込まれる
	protected override function atSceneLoad():void {
		//シーンにコンテンツを追加する
		addCommand(
				    new AddChildAt(container, page,0)
		);
	}
で、IndexSceneでまとめて3つのシーンを作成してます。でも、このやり方はいかんですね。各シーンで表示するSWFが大きいときは困りますね。ThePageのコンストラクタでSWFを読み込んでいるから、TheSceneクラスではシーンが読み込まれた時点でpageを作るのがいいのかなあ・・・謎。

[:script:] IndexSceneクラス
public class IndexScene extends SceneObject {
	private var uiPanel:CastSprite;
	private var logo:MovieClip;
	
	public function IndexScene() {
		// このシーンタイトル
		title = "IndexScene_Logo";
		//このシーンの中身
		logo = new oshigeLogo();
		//子シーンを追加する
		var sceneA:TheScene = new TheScene("sceneA",{title:"pageA", url:"pages_swf/pageA.swf"});
		var sceneB:TheScene = new TheScene("sceneB",{title:"pageB", url:"pages_swf/pageB.swf"});
		var sceneC:TheScene = new TheScene("sceneC",{title:"pageC", url:"pages_swf/pageC.swf"});
		addScene(sceneA);
		addScene(sceneB);
		addScene(sceneC);

あと、外部同期機能(swfaddressを使ったディープリンク)をテストしたところ、シーンをまたがって表示しているUIの出し入れなどがうまく動作してないのがわかりました。なんとなく原因はわかりますが、あちゃーって感じ。

LoaderSWFとSerialListつづき

|
LoaderSWFの書式は次のとおりなんだけど、

LoadSWF(request:URLRequest, loader:Loader = null, initObject:Object = null)

SerialListでは、なぜ、次のように書けばいいのか? 第2引数のloaderは要らないのか? 読み込み完了待ちはどうなってるのか?
結果から言えば、SerialListは、Loaderの読み込み完了を待つんですね。そして、読み込んだloaderはthis.latestDataプロパティに入ります。

var list:SerialList = new SerialList();
list.addCommand(
  new LoadSWF(new URLRequest("pageA.swf")),
  function():void {
      var loader:Loader = Loader( this.latestData );
      addChild(loader);
  }
)
list.execute();
明示的にloaderを作ると次のようになりますよ。

var list:SerialList = new SerialList();
var loader:Loader = new Loader();
list.addCommand(
  new LoadSWF(new URLRequest("pageA.swf"),loader),
  new AddChild(this, loader)
)
list.execute();
loadSWF()を使ってswfをシーンのページコンテンツとして読み込む試みの続きです。

前回はswfの読み込みをコンストラクタで行っていたので、サイズが大きなswfだった場合にはページ表示に間に合わないことがあるだろうという懸念がありました。また、大きなswfをいくつも読み込んだままにしておく手法には限界があるという問題もあります。

そこで今回はページを表示する度にswfを読み込み直し、読み込みが終わった時点で表示するようにしました。タイミングはatCastAdded()です。次のように読み込みとトゥイーン表示をaddCommand()を使ってシリアルコマンドとして連続します。

[:script:]キャスト表示イベントでswf読み込みと表示を行う
  protected override function atCastAdded():void {
  	addCommand(
  	   new Prop(this,{x:20,y:-stage.stageHeight}),
  	   //swf読み込み
  	   new Func(loadSWF),
  	   new DoTweener( this, { y:20,time:1, transition:"easeinoutsin"} )
  	);
  }

ここでswf読み込みはFunc(loadSWF)のようにすることで、loadSWF関数をaddCommand()の引数として渡せるようにします。loadSWF関数は次のように定義します。わざわざ別関数にしたのは、プログレス処理を組み込みたかったからです。

[:script:]swfを読み込む
  private function loadSWF():void {
  	//pageコンテンツをURLから読み込む(swf、画像)
  	var ldswf:LoadSWF = new LoadSWF(new URLRequest(_url));
  	ldswf.onProgress = function():void {
  	 	trace(ldswf.bytesLoaded + " / " + ldswf.bytesTotal + " (" + ldswf.percent + "%)" );
  	};
  	ldswf.onComplete = function():void{
  	   	 addChild(this.loader);
  	};
  	ldswf.execute();
  }

LoadSWF_pages2.jpg
swfを試す。
完成形のサンプルファイル一式→ LoadSWF_pages2.zip


さて、この方法でページ表示でswfの読み込みを待ってswf表示ができるようになりましたが、このままではページ表示のたびに繰り返し同じswfを読み込んで追加してしまいます。そこでシーンを移動したならばページの表示リストからswfを取り除きます。

*もしかすると、取り除いたswfを明示的にメモリから消去する必要があるかもしれませんが、よくわかりません。このままでもいいのかな?


[:script:]ページを消すさいにはswfも消す
  protected override function atCastRemoved():void {
  	addCommand(
  	  new DoTweener( this, { y:stage.stageHeight, time:1, transition:"easeinoutsin"} ),
  	  //読み込んだswfを消すために行う
  	   new RemoveAllChildren(this)
  	);
  }


ところで、前回ディープリンクを試したところ、下層ページのURLを直接開くとindexページ(今回からタイトルをtopに変更)のロゴマークが表示されたままでUIパネルが出てこないという状況でした。今回、これを解決しています。

ありゃ!!解決してない。 orz


しかし「index.html/#/sceneA」となって欲しいところが「index.html#/sceneA」のように/が1つ抜けています。原因は調査中です。

linkbug.jpg
↑ この結果が仕様どおりだということがわかりました。
問題ですらありませんでした。(^ ^;;;

「/#/sceneA」こうしたい場合には、htmlを呼び出す際に「http://sample.com/hoge/」のように/で終わるURLでリンクするってことですね!
swfを試す。
ディープリンクしたときに、UIパネルなどが正しく表示されるようになりました。

sceneBを直接開く

解決方法は、子シーンが直接開かれた場合に発生する親シーンの通過イベントatSceneDescendでもロゴの退場、パネルの登場を行うことでした。

[:script:]ディープリンクで子シーンを直接開く場合に必要となる
 protected override function atSceneDescend():void {
  stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  //シーンからロゴ退場、パネル登場
  addCommand(
    new DoTweener(logo,{x:30, y:30, scaleX:1, scaleY:1,time:1}),
   new DoTweener( uiPanel, { y:stage.stageHeight - 60, time:1 } )
  );
 }

これで今回のテストの機能はすべて満たしたのですが、実は内部的に??な部分がまだ残っていて、それを確認中です。すべてが解決した時点でファイルをアップします。

ディープリンクしたときに、UIパネルなどが正しく表示されるようになったバージョンのファイル一式をアップします。
アップを見合わせていた「内部的に??な部分」は、NextButton(initObject)のinitObjectでプロパティを設定したときに正しく機能しないという問題だったのですが、これはProgression側のバグだと確認がとれました。このサンプルで行っているように問題回避方法はあるので、とりあえず一件落着です。この件については、Progressionの公式フォーラムにやりとりがあります。

swfを試す。
sceneCを直接開く。

サンプルファイル一式→ LoadSWF_pages3.zip

addCommandとinsertCommand

|
LoadSWFがらみでSerialListのinsertCommand()をnorthorintさんに教えてもらいました。northorintさんのblogに記事がありますが(1年前にこれやってるんですね)、これを参考にaddCommandとinsertCommandの実行順をテストしてみました。なるほど!

[:script:]addCommandとinsertCommandの実行順をテストする
var list:SerialList = new SerialList();
list.addCommand(  
 new Trace("コマンドスタート"),  
 function ():void{  
    list.addCommand(new Trace("addCommand1のタイミング"));
    list.insertCommand(new Trace("insertCommandのタイミング"));  
    list.addCommand(new Trace("addCommand2のタイミング")); 
 },  
    new Trace("コマンド終わり!")  
) 
trace("executeの前----------");
list.execute();
trace("executeの後----------");

出力結果は次のようになります。
executeの前----------
コマンドスタート
insertCommandのタイミング
コマンド終わり!
addCommand1のタイミング
addCommand2のタイミング
executeの後----------

さらにLoadSWF()が続く

|
しつこく、まだやってます (^ ^;;
読み込んだloaderの座標をtweenせずに、ページ全体をtweenさせるのならば、loaderのページ内での座標さえフィックスできればいいというわけで、スクリプトが少し簡単になりました。

package pages{
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.net.URLRequest;
	/**
	 * ...2009.10.27
	 * @author ...oshige
	 */
	public class ThePage extends CastMovieClip {
		private var _url:String;
		private var page:CastMovieClip;
		public function ThePage(initObject:Object=null) {
			// 親クラスを初期化します。
			super(initObject);
			_url = initObject.url;
			page = this;
		}
		
		protected override function atCastAdded():void {
			var list:SerialList = new SerialList();
			list.addCommand(
				new Prop(page,{y:-stage.stageHeight}),
				new LoadSWF(new URLRequest(_url)),
				function():void {
					var loader:Loader = Loader( this.latestData );
					list.insertCommand(
						new Prop(loader, {x:(stage.stageWidth-loader.width)/2,y:(stage.stageHeight-loader.height)/2}),
						new AddChild(page, loader)
					)
				},
				new DoTweener( page, { y:0,time:1, transition:"easeinoutsin"} )
			)
			list.execute();
		}
		
		protected override function atCastRemoved():void {
			addCommand(
		 	 	new DoTweener( page, { y:stage.stageHeight, time:1, transition:"easeinoutsin"} ),
		  		//読み込んだswfを消すために行う
		  		 new RemoveAllChildren(this)
			);
		}
	}
}

で、残った問題は読み込み中のプログレスバーの表示とかいろいろです。
スクリプトがだいぶ変わってしまいますが、次のようにしてみました。いまココです。

package pages{
	import jp.progression.casts.*;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.net.URLRequest;
	/**
	 * ...2009.10.27
	 * @author ...oshige
	 */
	public class ThePage extends CastMovieClip {
		private var _url:String;
		private var page:CastMovieClip;
		public function ThePage(initObject:Object=null) {
			// 親クラスを初期化します。
			super(initObject);
			_url = initObject.url;
			page = this;
		}
		
		private function loadSWF():void {
			//pageコンテンツをURLから読み込む(swf、画像)
			var ldswf:LoadSWF = new LoadSWF(new URLRequest(_url));
			ldswf.onProgress = function():void {
			 	trace(ldswf.bytesLoaded + " / " + ldswf.bytesTotal + " (" + ldswf.percent + "%)" );
			};
			ldswf.onComplete = function():void{
				var ld:Loader = this.loader;
				 page.addChild(ld);
				ld.x=(stage.stageWidth-ld.width)/2;
				ld.y=(stage.stageHeight-ld.height)/2;
			};
			ldswf.execute();
		}

		protected override function atCastAdded():void {
			addCommand(
			   new Prop(page,{y:-stage.stageHeight}),
			   //swf読み込み
			   new Func(loadSWF),
			   new DoTweener( page, {y:0,time:1, transition:"easeinoutsin"} )
			);
		}
		
		protected override function atCastRemoved():void {
			addCommand(
		 	 	new DoTweener( page, { y:stage.stageHeight, time:1, transition:"easeinoutsin"} ),
		  		//読み込んだswfを消すために行う
		  		 new RemoveAllChildren(this)
			);
		}
	}
}
niumさんから最適解をもらいました。こういうやり方がよいということです。
ステージリサイズには対応してませんが、これでステージ中央に表示されます。プログレスバーにも対応できますね。

[:script:] ThePageクラス
package pages{
  import jp.progression.casts.*;
  import jp.progression.commands.display.*;
  import jp.progression.commands.lists.*;
  import jp.progression.commands.net.*;
  import jp.progression.commands.tweens.*;
  import jp.progression.commands.*;
  import flash.display.Loader;
  import flash.net.URLRequest;
  
  /**
   * ...2009.10.27b
   * @author ...oshige
   */
  public class ThePage extends CastMovieClip {
    private var _url:String;
    
    public function ThePage(url:String, initObject:Object=null) {
      _url = url;
      // 親クラスを初期化します。
      super(initObject);
    }
    
    protected override function atCastAdded():void {
      addCommand(
         new Prop(this,{y:-stage.stageHeight}),
         //swf読み込み
        new LoadSWF(new URLRequest(_url), null, {
          onProgress:function():void {
            trace(this.bytesLoaded + " / " + this.bytesTotal + " (" + this.percent + "%)" );
          },
          onComplete:function():void{
            var ld:Loader = this.loader;
            self.addChild(ld);trace("self=",self);
            ld.x=(stage.stageWidth-ld.width)/2;
            ld.y=(stage.stageHeight-ld.height)/2;
          }
        }),
         new DoTweener( this, {y:0,time:1, transition:"easeinoutsin"} )
      );
    }
    
    protected override function atCastRemoved():void {
      addCommand(
          new DoTweener( this, { y:stage.stageHeight, time:1, transition:"easeinoutsin"} ),
          //読み込んだswfを消すために行う
          new RemoveAllChildren(this)
      );
    }
  }
}

この変更にともなって、ThePageクラスを呼び出すTheSceneクラスとIndexSceneクラスを少し変更。

swfを試す。

サンプルファイル一式→ LoadSWF_pages4.zip

リキッドレイアウトに対応

|

リキッドレイアウトに対応したバージョンを作りました。なんか結果オーライのような感じですが、今日の所はこれでOKということで、改めてスマートな方法を考えたいと思います。そろそろ次のテーマに移ります。

swfを試す。

サンプルファイル一式→ LiquidLayout_1.zip

なお、このバージョンはProgression 4.0.1 Public Beta1.2で作ってあります。1.1ではNextSceneボタンとPreviousSceneボタンが正しく動作しません。

そうそう、シーンを進めると一瞬だけ上に現れるバーはプログレスバーなんですけどね。ちゃんと動いていいるのかな??って感じです。

親シーンからの到着、親シーンへの移動で発生するイベント
fig03_01-02.jpg



子シーンからの到着、子シーンへの移動で発生するイベント
fig03_01-03.jpg



現在のシーンを通過する場合に発生するイベント
fig03_01-04.jpg



同じ親シーンをもつ子シーンの間で移動する場合に発生するイベント
親シーンではイベントが発生しない点に注意
fig03_01-05.jpg



「従兄弟→叔父→親→現在」と移動する場合に発生するイベント
祖父シーンではイベントが発生しない点に注意
fig03_01-06.jpg



「現在→親→叔父→従兄弟」と移動する場合に発生するイベント
祖父シーンではイベントが発生しない点に注意
fig03_01-07.jpg

fig05_01-04a.jpg

swfを試す。

 キャストオブジェクトはインスタンスidとインスタンスグループで参照と抽出ができます。インスタンスidはidプロパティ、インスタンスグループはgroupプロパティで設定します。

 次のサンプルでは、BallCastSpriteクラスで円形のキャストスプライト、BoxCastSpriteクラスでは四角形のキャストスプライトを作り、合計20個のキャストスプライトをステージに交互に並べています。このとき、BallCastSpriteクラスで作ったキャストスプライトにはグループidとして"ball"を付け(22行目)、BoxCastSpriteクラスで作ったキャストスプライトにはグループidとして"box"を付けます(28行目)。同時に個々を区別する"sp0"〜"sp19"のインスタンスidも付けます(21、27行目)。

 続いてキャストスプライトにアニメーションを付けるコマンドリストを作成します。まず1つ目のアニメーションとして、getInstancesByGroup("ball")でインスタンスグループballのキャストスプライトを配列ballListに参照を取り出し(40行目)、1個ずつ順に弾むように拡大するアニメーションをDoTweenerコマンドで作りシリアルリストのanimeList1に追加します。

 同様に2つ目のアニメーションはgetInstancesByGroup("box")でインスタンスグループboxのキャストスプライトを配列boxListに参照を取り出し(47行目)、すべてが同時にぐるんと90度回転するアニメーションをDoTweenerコマンドで作りパラレルリストanimeList2に追加します。

 3つ目のアニメーションでは、今度はランダムに選んだidのキャストスプライトをgetInstanceById("sp"+no)を使って1個だけ取り出し(54行目)、そのキャストスプライトが拡大縮小を繰り返すアニメーションをループリストanimeList3を使って作ります。

 3種類のアニメーションの準備ができたので、最後にこれらを順に実行します(59行目)。実行はシリアルリストになるので、animeList1が完了したならばanimeList2、最後にanimeList3という順で再生されます。

[:script:]グループとインスタンスidでインスタンスを選択してアニメーションする
package {
 import jp.progression.commands.display.*;
 import jp.progression.commands.*;
 import jp.progression.scenes.*;
 import jp.progression.casts.getInstanceById;
 import jp.progression.casts.getInstancesByGroup;
 import jp.progression.commands.tweens.DoTweener;
 import jp.progression.commands.lists.*;
 import pages.*
 
 public class IndexScene extends SceneObject {
  public function IndexScene() {
   //ブラウザのタイトル。
   title = "Top";
   //コンテンツ
   var castSp:*;
   for (var i:int=0;i < 20;i++){
    if(i%2){
     //偶数番のキャストを作る
      castSp = new BallCastSprite();
      castSp.id = "sp"+i; //インスタンスid
      castSp.group = "ball";//インスタンスグループ
      castSp.scaleX=castSp.scaleY=0.4;
     }else{
      //奇数番のキャストを作る
      castSp = new BoxCastSprite();
      castSp.id = "sp"+i;//インスタンスid
      castSp.group = "box";//インスタンスグループ
     }
     //キャストをコンテナに並べる
     castSp.x = i%5*100+40;
     castSp.y = int(i/5)*90+40;
     (new AddChild(container, castSp)).execute();
   }

  }
  //このシーンをロードしたらアニメーションを開始する
  protected override function atSceneLoad():void {
   //インスタンスグループballのアニメーションのコマンドリストを作る
   var ballList:Array = getInstancesByGroup("ball");
   var animeList1:SerialList = new SerialList();
   for (var i:int=0;i < ballList.length;i++){
    animeList1.addCommand( new DoTweener(ballList[i],{scaleX:1,scaleY:1,time:0.5,transition:"easeOutElastic"}));
    animeList1.addCommand( new DoTweener(ballList[i],{scaleX:0.4,scaleY:0.4,time:0.5,transition:"easeOutElastic"}));
   };
   //インスタンスグループboxのアニメーションのコマンドリストを作る
   var boxList:Array = getInstancesByGroup("box");
   var animeList2:ParallelList = new ParallelList();
   for (var j:int=0;j < boxList.length;j++){
    animeList2.addCommand( new DoTweener(boxList[j],{rotation:90,time:1,transition:"easeOutElastic"}));
   }
    //インスタンスidを1個選んでアニメーションをループさせる
    var no:int = Math.floor(Math.random()*20);
    var sp:* = getInstanceById("sp"+no) ;
     var animeList3:LoopList = new LoopList();
    animeList3.addCommand(new DoTweener(sp,{scaleX:1.5,scaleY:1.5,time:1,transition:"easeOutElastic"}));
    animeList3.addCommand(new DoTweener(sp,{scaleX:0.4,scaleY:0.4,time:1,transition:"easeOutElastic"}));  
   //コマンドリストの実行
    addCommand(animeList1, animeList2, animeList3);
  }
 }
}

logo_67ws_on.gif
大重美幸の
ActionScript 3.0初級講座

AS3初級講座(前半)
  • 2010年3月19日(金)
  • 2010年4月21日(水)
  • 2010年5月21日(金)
AS3初級講座(後半)
  • 2010年3月26日(金)
  • 2010年4月28日(水)
  • 2010年5月28日(金)

10000円キャッシュバック