やっと Progression3 をさわりはじめたメモ

サイトを作る機会がなかなかなかったんだけど、ついに Progression3 からデビュー。んで、土日でマスターする必要がw。とりあえず http://progression.jp/ しか読んでない状態なので、しばらく我流でいじってみる。そのメモなので読んでも分からないと思います。

とにかく見まくることになるASDocはこちら

何を今さらという話だけど、生成するシーンの数だけ SceneObject を拡張したシーンクラスや CastButton を拡張したボタンを用意する必要はなかった。例えば Progression サイトのクラスベースチュートリアルに従って作ったとすると IndexScene.as の中で for文使って、同じクラスの別インスタンスをぐぁーっと作れる。mySceneClass の第2引数 initObject にオブジェクト形式でデータ渡せるので、各シーンごとの個性付けはその値で設定してやればいいかと。

for (var i:int = 1; i <= sceneNum; i++ ) {
// day を作成する
var myScene:mySceneClass = new mySceneClass ("myScene"+i,{myNum:i+"だよ"});
myScene.name = "myScene"+i;//上の第1引数で命名してるので不要
addScene( myScene);
}

ちなみに mySceneClass のコンストラクタは

mySceneClass( name:String = null, initObject:Object = null )

もちろんボタンも同一クラスから量産できる。これは Index.as に書いたコード。

// ボタンを作成する
for (var i:int = 1; i <= 3; i++) {
var btn:MyBtn = new MyBtn({sId: "btn"+i});
btn.x = 400;
btn.y = 100 * i;
addChild( btn );
}

そのMyBtnクラスの中の initObject でパラメータ受け取れてるから、こちらもそれで個性つけてやればOK。

public function MyBtn( initObject:Object = null ) {
super( initObject );
// クリック時の移動先を設定する
sceneId = new SceneId( "/index/"+initObject.sId );
// 表示を作成する
var txt:TextField = new TextField();
txt.mouseEnabled = false;
txt.text = initObject.sId;
addChild( txt );
}

initObject 最高。
で、何よりも先に xml で生成個数を算出するなら、Index.as の冒頭で、こちらも Progression に組み込まれてる LoadURL クラスを使って

// LoadURL インスタンスを作成します。
var com:LoadURL = new LoadURL( new URLRequest( "data.xml" ) );
com.addEventListener("complete", loadCompleteListener);

と便利にxml読み込める。

要するに「最初にxml読み込んで、その要素の数だけ動的にシーンやボタンを作る」なんて超簡単にできるということ。Progressionイベントで壇上で喋ったくせに、今やっと小さく一歩を踏み出した。

でもあとは、シーントランジションの「出たか・来たか・通過されたか・その他もろもろ」の遷移分岐を設定すればサクッとサイトできる気がする。すごいコレ。

「お前まだそんなレベルやったんか!」と taka さんから怒られること必須。

追記メモ

  • addCommandの中のDoTweenerの末尾に「;」をタイプしてしまってパブリッシュエラーで悩むこと数分。
  • シーン遷移中は他のボタンもちゃんと無効になって押せないようになってる。こりゃ楽だわ~。
  • xml を読み込んでパースまでの一連のフローを addCommand しようかと思ったけど、Index.as の _onInit の中では addCommandは動かない?まぁ他の SceneObject の中では動いたから IndexScene 内でやるか。いつの間にか動いてた。ひょっとして、prog = new Progression( "index", stage, IndexScene ); より前にprogにアクセスしようとしてたかも。はずかし!なんにしても addCommand 便利。
  • xmlをロードして、それをどこからでもアクセスできるようにしたくて、Index.asのクラスメンバーにしてみたけど、別のシーン(クラス)からのアクセス方法が分からない。別アプローチとして、xmlをあるシーンに持たせようとIndexScene(idは"index")のクラスメンバーにしてみて、別のシーンからProgression.getProgressionById("index")でアクセスを試みるも、Progression.get...の時点でコード補完出ず。←削除されました。同様の機能を実現するには Progression インスタンスの getProgressionById() メソッド、または getProgressionById 関数をパッケージからインポートして使用してください。
    ということでprogression.getProgressionByIdならいけそうだったけどnullエラーでNG。明日考えよう。
  • Index以外の別シーンからのprogとかrootへのアクセスへの参照方法がよく分かってない。これも明日考える→シーンクラスのコンストラクタ内でのprogressionやprogやrootのアクセスは怒られるってことかな?_onInitとかの中なら問題なく使える。グラフィックのドローとかも_onInit内でやるといい感じ。ステージ幅はprogression.stage.stageWidthでとれる。
  • このへんにtakaさん直筆のサンプルがあるので、まずはここ読破すること。
  • Index.asの_onInitn内にaddCommandで「xmlロード→要素数調べて、そこで一気にfor文でシーンを全部生成しaddScene、ボタンも同様に作っておいてaddChildしておく」のが常套手段だろうか。addSceneだけ全部やっといて、あとは画面遷移に応じて各シーンのCastSpriteなりの表示要素をaddChild・removeChildするアプローチかな。→その手法であらかじめ全シーン・全ボタンをIndex.as上での生成に成功。
  • 2階層ムービーを作っているのだけど、1階層目の遷移で、その移動後に2階層目のシーン(写真コンテンツのサムネイル)を表示させておく方法がわからない。孫シーンに移動ではなく、子シーンにgotoしたときに孫シーンの表示オブジェクトを表示させたいだけというケース。例:ChildScene(extends SceneObject)の_onInit時にその配下シーンであるMagoScene(extends SceneObject)群の写真を表示させておく方法、および、根本的にChildScene内からMagoSceneシーンにアクセスする方法を調べる。→サムネイルはサムネイル(ボタン)であるのが普通だから、孫シーンを表示させておくのではなく、孫シーンの縮小版写真を内包するボタン(extends CastButton)をaddChildしておくのが常套手段かな。
  • サムネイルをクリックした時に「現状のステージを表示させつつ写真を拡大表示」的なlightbox的な使い方をする時は、写真拡大のシーンに移動させるわけだけど、「現状を残す」処理はそのシーンの LOAD イベントと UNLOAD イベントで実装すること。僕の場合、_onGotoでの処理を_onUnloadに移し替えたらそれっぽい実装になった。このへんがAppleStoreでむらけんさんが言ってたことだな。niumさんのこのエントリを参考に
  • onInitで表示させてるものがあるとして、ディープリンク機能でその子シーンに直接アクセスされた時に、onInitが動くのか?あるいはそれはonLoadに別途記述しておくべきなのかを調べること。サムネイル表示の有無を考慮すると、たぶん後者ではなかろうかと勝手に考えてる。→テストしてみた。onLoadとonInitに同じ処理を書いたらDoTweenとかが2連続で動く。onLoadだけに書いておいてもページ到着時にはそのonLoadがちゃんと動くので、onLoadだけに記述するだけで済む場合もあるっぽいな。今のとこの印象としては、パーマリンクのことを考慮すると「中継される可能性のあるシーンはonLoad と onUnload の使用がメイン」になるのかもしれない。
  • カタマリさん制作らしい「Girl's Trip」も Progression かもしれない。僕がいま作ろうとしているものにとても近い。しかもデザイン的にも同じ方向。クオリティはかなわないけど。。
  • 外部画像の読み込みはCastLoaderで。その使い方は Is It So Easy? 様のこのエントリからダウンロードできるサンプルプロジェクトが参考になります。
  • あー、サムネイルボタンを作る時は、シーンに直接CastButton置くんじゃなくって、CastSpriteを作ってその上にCastButtonをaddChildする方が遷移時のアニメーションができるからいいんだねぇ。とIs It So Easy?さまのサンプル見て思った。
  • サムネイルを配置しているシーンでサムネイルスプライトを管理しようとすると、各イベント内でコントロールするために配列で管理したりする必要があるけど、サムネイルスプライトをCastSpriteにしてその中で遷移表示の管理すればそんな配列管理しなくても済む。なるほど。→niumさんよりお言葉「全部のサムネイルボタンの group プロパティに同じ値を設定して、getInstancesByGroup() すれば、配列作らなくてもダイレクトアクセスできますー」とのことなので、シーン上での管理もフレームワーク的には余裕で想定内らしい。複数グループを正規表現で取捨選択する getInstancesByRegExp なんてのもあるらしいw。
  • Progressionは普通のaddChildはあんまり使わないみたい。これ使っちゃうと遷移時のイベントが動かないっぽい。その代わりとして
    // 表示コンテナとなる CastSprite インスタンスを作成します。
    var container:CastSprite = new CastSprite();
    // 表示コンテナに追加する CastSprite インスタンスを作成します。
    var child:CastSprite = new CastSprite();
    // AddChild コマンドを作成します。
    var com:AddChild = new AddChild( container, child );
    // AddChild コマンドを実行します。
    com.execute();
    を多用することになる。addCommandのなかならnew AddChild...でいく。
  • DoTweenerでTweenerのスペシャルプロパティを使う時はimport caurina.transitions.properties.ColorShortcuts;およびColorShortcuts.init();しておかないとエラーが出るので注意すること。
  • コンストラクタ内でaddCommandは動かないのかな。
  • ツリー構造のXMLをXMLUtil.xmlToObjectでObjectに変換して、孫ノードにアクセスってできるのかな。
  • どっからでもアクセスできるデータ(今回はXML)を保持する方法がまだわからない。何階層か下のシーンで必要なのは、その大元のXMLのツリー構造を下った部分だけよって時にどうするのが得策なのかな。→indexシーンで持ってる変数は配下シーンからコンストラクタ以外の場所で trace(IndexScene(root).hoge); で取得できた。なんかもっとスマートな方法ある気がするなぁ。。あと、Index.asで宣言してるプロパティにアクセスしたい。
  • IndexSceneにinitObjectを持たせる方法はコードヒントが表示されるとこまではいったのにうまくいかなかった。件のxmlの孫ノードの値を孫シーンに持たせる方法は、孫自身の initObject に必要なノード以下のxmlを突っ込んで対応した。→僕のケースではXML型じゃなく、XMLListにするとうまくいった。
  • 自分のシーンの子シーンを配列で取得できる trace(this.scenes) とかはコンストラクタで唱えても0、onInitの中で唱えればOK。まぁそりゃそうだ。
  • progression.container...のprogressionってどのタイミングで生成されるんだろう。Index.asから呼べない。
  • FlashでのonResize時の処理をどこでどう管理するのか考えねば。
  • 僕含め、たぶんクラスベース開発初心者の人はprogとprog.rootとprogressionの違いと、それにアクセスできるタイミングに戸惑う気がする。
  • IndexSceneの_onInit内からそのまま扉絵シーンを表示させようとしたけどprogression.goto(new SceneId("/index/tobiraScene"));が動かない。
  • 上の疑問点の対応策としてIndex.asでいきなりroot以外に遷移するようにprog.goto(new SceneId("/index/tobira"));とかしたんだけど、これだとブラウザ機能のパーマリンク1発目が機能しない?prog.goto( prog.firstSceneId );でIndexSceneにgotoしてる時はちゃんと機能してる。
  • IndexSceneに置いたムービークリップmyMcを、異なるシーンやボタンのイベントからコントロールする方法、キャスト必須:IndexScene(getSceneBySceneId(new SceneId("/index"))).myMc.x = 100;
  • CastButton継承クラス内でsceneIdを未設定にしておいて、そのインスタンスに対してCastButtonInstance.sceneId = null; で「クリックしてもシーン移動しない」ボタンになる。何をしたかというと「写真サムネイルをクリックしたら拡大シーンに移動。サムネイルドラッグのマウスアップ時は拡大シーンに移動しない」という条件分岐実装。
  • CastButtonクラスのonCastAddedがfinalなので、継承させたボタンクラス内でonCastAddedできない。そんなことをしようとしている僕の実装の仕方がおかしいんだろう。
  • addCommand内のfunction内でのthisは、「その関数自身」なので、function内でthis.name="hogeFund"とでも命名しておけば、エラー出力された時にどこでエラーが起きてるか分かりやすくなる。
  • 何気なくCastButtonのロールオーバー処理とかでDoTweenerを使ってマウスでチロチロやってると「 コマンドで CommandTimeOutError エラーが発生。」というエラーメッセージに出くわすことがある。とくにややこしいことはしていないんだけど。しかもエラーは出るけど一応動いてる。警告的意味?それとも対応すべきエラーなのか。→パラレルリストの中で複数個DoTweener設定して、個別に異なる時間設定して、チロチロやると出やすい。リストから外したらでなくなったのでとりあえあず済。

「Back」「Next」ボタンを使って、ある子シーン内でシーン遷移しつつ、その子シーンが終わったら、隣の親階層の1番目の子シーンに移動するという「Progressionらしさ」を実感できるコード。最初と最後にストッパーかけとくこともできる。これ動いた時感動したw

if(next){
photoNextButton.sceneId = next.sceneId;
}else {
if(parent.next){
photoNextButton.sceneId = parent.next.scenes[0].sceneId;
}else {
var com:RemoveChild = new RemoveChild(progression.container, photoNextButton);
com.execute();
}
};
if(previous){
photoBackButton.sceneId = previous.sceneId;
}else {
if(parent.previous.name!="tobira"){//各写真サムネイルシーンの前に"tobira"という扉シーンがある場合
photoBackButton.sceneId = parent.previous.scenes[parent.previous.scenes.length-1].sceneId;
}else {
var comBack:RemoveChild = new RemoveChild(progression.container, photoBackButton);
comBack.execute();
}
}

「子シーンから自分のシーンに上がってきたときのみ○○しない」という分岐が分かんない。ascend,decendは通過される時に使うイベントだし。直前のシーンが自分の子シーンかをチェックするコードを書けるかどうか。

自分のシーンに移動する前のシーン名が /index/tobira だった時に hoge 出力するって条件文は次のように書く。

if (String(progression.departedSceneId) == "/index/tobira") {
trace("hoge");
}

addCommandを使えばシーケンシャルで実行できるので「画像ロード→サイズ取得して反映→その他の処理etc..」と連続的に行えるのだけれど、画像ロードで失敗した時の処理コードをどこにどう書けばいいのか、そうい生々しい実装部分をすっかり忘れていた。

addCommand内でのエラー処理をniumさんが、外部xmlのエラー処理をapeirophobiaさんがエントリされています。ところが僕はよく見直してみると、CastButton継承クラスの中にロード定義し、実行タイミングは別クラスから呼び出してたりという、変なことをやっていたので、無理やり独自に作った。画像パスの場所に画像がなかった場合に別のデフォルト画像を読み込むという「超僕用スクリプト」は以下:

/*ImageのLoad*/
public function loadImage():void {
//var _ImageUrlRequest = new URLRequest(_ImageUrl + "?noCache=" + String(new Date().getTime()));// ("photos/thumbnail/" + _ImageUrl);
var _ImageUrlRequest = new URLRequest(_ImageUrl);//タイムスタンプなし
_ImageLoader.addEventListener(CastEvent.CAST_LOAD_COMPLETE, ImageLoadComp);
_ImageLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
_ImageLoader.load(_ImageUrlRequest);
}

private function ioErrorListener(e:IOErrorEvent):void {
_ImageLoader.load(new URLRequest("default.jpg"));
_ImageLoader.addEventListener(CastEvent.CAST_LOAD_COMPLETE, ImageLoadComp);
}

IndexScene以外のSceneObjectではコンストラクタ内でroot内のメンバー変数を取得できない。自分自身のシーンがprogression.rootに追加されてからなら取得できるので、以下のようにする。

public function SomeSceneObject( name:String = null, initObject:Object = null ){
:
this.addEventListener(SceneEvent.SCENE_ADDED_TO_ROOT, addedListener);
}
private function addedListener(e:SceneEvent):void {
trace(IndexScene(root).xml.)
}

Index.asの中でaddCommandがうまく動かないという報告をapeirophobiaさまのprogressionエントリで拝見し、その記事周辺のコメント欄でもみなさんのアプローチが書かれているのだけど、僕はIndex.asの_onInit内に

protected override function _onInit():void {
prog.root.onLoad = function():void {
this.addCommand(
new LoadURL( new URLRequest( "hoge.xml" )),
function():void{なんかいろいろxmlからシーン生成とか}
);
};
prog.goto( prog.firstSceneId );
};

がうまく動いているっぽい。このソースの元ネタどこだったかな?でもこれxmlのロード待ちすらできていないような気がしてきた。なぜだかいつも動いているで見過ごしていたのだけど。

シーン移動ボタンを連打されるとおかしくなることがあるので、nagggさんのエントリを参考に「シーン移動処理中はボタンを効かなくする」実装をする時は:

progression.addEventListener(ProcessEvent.PROCESS_START, function() { _nextButton.mouseEnabled = false; } );
progression.addEventListener(ProcessEvent.PROCESS_COMPLETE, function() { _nextButton.mouseEnabled = true; } );

ProcessEvent を使ってボタンの機能を一時的にオフにしてやる。僕はIndexScene の onLoad のとこに書いた。「progression.addEventListener..」でいいですよね。動いてるし。


久々に Verbose.enabled = true; して出力チェックしてみると、・・・コマンドを中断。・・・と出まくる。でもFlashはちゃんと動いているっぽいのでよしとする。


IndexSceneで生成してnew AddChildAt(progression.container, hogeMc, 1000); しておいて別シーン内で別の CastSpriteインスタンスをnew AddChild(progression.container, fooMc); とするも hogeMc が上に配置されていない。new AddChildAt(progression.container, fooMc, 10); とするも fooMc 自体が消えてしまうという謎。でも別のProgression習作ではきちんと配置できてたりするのでうーん。

Progression に Google Analytics for Flash を埋め込む方法

現状うまくできない。まず Index.as にトラッキング用のオブジェクトを作る。実際にアクセスカウントは他のボタン系のクラスで実行するので、外部クラスから見えるように Getter も用意しておく。

(中略)
import com.google.analytics.AnalyticsTracker;
import com.google.analytics.GATracker;
public class Index extends CastDocument {
private var _tracker:AnalyticsTracker;
public function Index() {
//GoogleAnalytics
_tracker = new GATracker(this, "UA-xxxxxx-y", "AS3", true);
}
(中略)
public function get tracker():com.google.analytics.AnalyticsTracker { return _tracker; }
(中略)

これで初期化はされてる。GATracker の第4引数の「デバッグモード」を true にしているので、解析用の検証パネルが通常なら画面の最前面に覆いかぶさるはずが、Progression のステージ管理の方が上に重なるようで、Analytics パネルが最背面になってしまう。でもまぁ、これは本番時にはどうせ見えないパネルなので、とりあえずは Progression で AddChild しているオブジェクトをステージ外に押しやることで、Analytics パネルは見えるようにできる。きちんと _tracker は初期化され、計測できる状態になっているので、まぁここまではOK。

問題は外部クラスから、この tracker にアクセスし、架空のページカウントをインクリメント(加算)する trackPageview メソッドを呼び出す時、キャストしないと Index.as 内で getter しているオブジェクトにはアクセスできないから:

Index(root).tracker.trackPageview("/flashPage/clicked");

としてパブリッシュする。上のアクションが起きる箇所(例えばボタンクリック)を呼び出すと、一応 Flash としては動作してはいるのだけど、Analytics はできていない。出力パネルを見ると:

TypeError: Error #1034: 強制型変換に失敗しました。flash.display::Stage@33d90f99 を myproject.Index に変換できません。

とある。かといって Index() とキャストをしないでパブリッシュすると、root.tracker なんて null だよ!と怒られる。

Progression における Flash でいうところのフツーの "root" にオブジェクトを保持したい&外部クラスからアクセスしたい時はどこに持たせたらいいのだろう。

追記:Analytics 関係なく、どうもこの root が僕は分かってないや。Index 以外のクラスから

trace(root); //出力:[object Stage]
trace(Index(root)); //出力:TypeError: Error #1034: 強制型変換に失敗しました。flash.display::Stage@455bf99 を myproject.Index に変換できません。

この時点でひっかかってる。でも tracker にアクセスするには下のようにキャストする方法しか思いつかないからどうしようかなぁ。

参考:Progression のルートシーンは、Progression インスタンスを作成すると root プロパティとして自動的に作成されます。←ってことは Flash のドキュメントクラスにアクセスするには別プロパティがあるんかな?

trace(progression.root == root); //出力:true

だった。やはり盛大に勘違いしていた。ってことは IndexScene(progression.root) と IndexScene(root) は全く同じものになるけど、どっちの記述方がアクセスが速いとかあるんかな?GoogleAnalyticsForFlashは再度見直す必要あり。急場しのぎでこっちの旧アプローチで解決しちゃった。


“やっと Progression3 をさわりはじめたメモ” への13件の返信

  1. ちょうど僕も今日『クラス制作ガイド』読んでいたところだったので、こちらの記事と併せて勉強させて頂きます!

  2. 「お前まだそんなレベルやったんか!」

    うそです、ごめんなさい!ごめんなさい!

  3. >niumさま
    こちらこそ、ごめんなさい!ごめんなさい!

    今他の関連記事とか見てるんですけど、上でやろうとしてることもaddSceneFromXMLとかが既に用意されてるとかどんだけ~!たいがいの人がやろうとすることはすでに先回りサポートされてるw。すごい!
    やっぱり作る目標決めて触るのが一番ですねぇ。完全に出遅れていますが僕もProgressionワールドの末席に参加させていただきますー。

  4. addSceneFromXML() は、こんな感じでいけますよ!

    addCommand(
    new LoadURL( new URLRequest( “data.xml” ) ),
    function():void {
    addSceneFromXML( new XML( this.latestData ) );
    trace( toXMLString() );
    }
    );

  5. ご丁寧にありがとうございますー。
    今ASDOC見ていて「あ~日本語ドキュメントっていいなぁ~。」とtakaさんの功績を味わっているところです。
    3都も遠征お疲れ様です。リアルで伝道師ですねー。

  6. おー奇遇。僕も今から自サイトでProgressionデビューしようと思とったところなんで助かります、ウマウマ>tera,taka

  7. 使ってみようと思い続けてずっといじれてないのですが、
    こういうメモがあるといざ使い始めたときにものすごく助かりそうです。
    この調子でメモり続けてもらえれば僕も使える気になってそうです 笑

  8. 試行錯誤の跡を開示していただけるのは、とても参考になります。ありがとうございます。

  9. 皆様
    わざわざコメントいただきありがとうございます。
    でも本当に個人的メモなので、たぶんあんまり参考にできないと思いますw。
    あとで記事っぽくまとめられたらよいのですが、はたして自分自身のメモを後日解読できるかどうか。。

  10. > 「子シーンから自分のシーンに上がってきたときのみ○○しない」という分岐が分かんない。ascend,decendは通過される時に使うイベントだし。直前のシーンが自分の子シーンかをチェックするコードを書けるかどうか。
    progression.departedSceneId がその移動シーケンスの出発地点のシーン識別子なので、出発地点が自分の子シーンかどうかを調べればいけると思います。

    !sceneId.equals( progression.departedSceneId ) && sceneId.contains( progression.departedSceneId );

    こんな感じですね。

コメントは受け付けていません。