ActionScript1.1コンテンツのCreateJS化作業メモ



ただのメモ書き
MarkDownに慣れてないので、読みにくいのは勘弁して下さい。

# 前提として
作業開始時点で、CreateJSもActionScriptもFlashも詳しくありませんでした…orz

# 作業を始める前に
予備知識としてまずはこれを読んどけ的なやつ

* [Flash Professional Toolkit for CreateJSのファーストステップガイド](http://www.adobe.com/jp/devnet/createjs/articles/getting-started.html)
* [Flash／CreateJSコンテンツを切り替え表示する方法](http://www.adobe.com/jp/devnet/createjs/articles/flash_createjs_01.html)
* [ActionScriptとCreateJSでコードを共有するためのTIPS](http://www.adobe.com/jp/devnet/createjs/articles/flash_createjs_02.html)


# 作業概要
* スクリプトの変換
Flash Lite1.1 → AS3 → JavaScript ( CreateJS )
※FlashはAS1.1のままでいいなら、AS3への変換は不要

* 必要に応じてシーン・タイムラインを調整
これがかなり大変

* JavaScriptで吐き出されるようにスクリプトをコメントアウト
**/* js ... */**
ASと併記すれば、FlashでもCreateJSでも動くようになるYO！

# 主な作業ポイント
## スクリプト変換
外部ASは無視されるので、
外部ASの内容は、インポートしている箇所に貼り付けなりすること！
## 対象のスクリプトの this / exportRoot の扱いに注意
具体的には以下の違いに気をつける

* メインのタイムライン
* ムービークリップのタイムライン

メインタイムラインでは this も exportRoot も同じタイムラインを指すが、
ムービークリップ内では **this = ムービークリップ内のタイムライン**、 **exportRoot = メインタイムライン** を指すので、
今、**どこのタイムラインを編集しているのか？**を意識しておかないと、
後で意図しない結果に…(；´Д｀)

## 必要なライブラリやムービークリップには名前を定義しておく！

* 名前定義していないムービークリップ
* 名前が重複しているライブラリ（※）

上記に該当するものは、toolkit for CreateJS で吐き出しす際に、
適当に名前が割り当てられるので、
後で制御したり置換したりするなら使いやすい名前を定義すること
※ライブラリの重複命名に関しては、
　**フォルダでグルーピングしているから大丈夫！
　…なんて考えは通用しない！
　容赦なく自動的に連番が振られた状態で出力される！！**

## tellTarget
this の向き先を、一時的に指定のムービークリップにする関数

```js
// tellTarget("mc_up_down"){ gotoAndStop("up"); } ->
this.mc_up_down.gotoAndStop("up"); 
```

## call
指定のラベルを実行する関数
→なんちゃって関数呼び出し…といった感じ
特定ムービークリップのラベルをcallしている場合は、そのムービークリップに関数を追加すればいい
…が、真っ当な方法では追加できないので、
* 関数を作っちゃうとかいった感じの工夫
* [callをエミュレートする](http://chitose-web.blogspot.jp/2012/08/toolkit-for-createjs.html)
…個人的にはこっちのほうが好み。

```js
// call関数シミュレータを追加
// AS : call("label");call(10);
// JS : exportRoot.call(this,"label"); exportRoot.call(this,10);
exportRoot.call = function(tar,obj){
	if(typeof(obj) == "string"){
		obj=tar.timeline._labels[obj];
	}
	tar["frame_"+obj]();
};
```

## add
**文字列結合(+)演算子に置換**
ただし、*数字文字列を結合している*可能性があるので、
**結合結果を数字として扱いたい時は、parseIntをしておくこと！**
…関数のオーバーヘッドよりも、想定外のバグを防ぐほうが大事！！

## int()
少数以下を切り捨てる用途が多いので、下記で対応する
* **Math.floor()**
真っ当に置き換えるならこれ
* **parseInt(), +value**
数字文字列をキャストしているだけならこれ
* **Number()**
空のときは 0 を期待しているならこれ

## substring
文字列抽出で使用しているので下記のように対応する
* **hoge.substr に置換して、開始インデックス値を-1**
開始インデックスを-1する理由は、**ActionScriptの文字インデックスは「1始まり」**なので、そのまま使うとJavascriptで切り出した時に1文字ずれてしまうため

この１始まりは他にも
* フレーム番号

とかも該当するので、変換作業時は意識しておくこと

## length
文字数を数えるときに使ってることが多いので、
**hoge.length に置換する**
ただし、数値の桁数を取得する時に使っている場合は、
**hoge.toString().length**でキャストしてあげること！

## trace
ログ出力みたいなものなので、下記で対応
* alert()
* console.log()

## _xscale, _yscale
インスタンスのスケール値指定
CreateJSはAS3に近いAPIを持っているので、AS3と同様になるように変更すればOK
ただし、**AS3から scaleX, scaleY に変更され、設定値もパーセンテージから実数になっている**ので、プロパティ名と同時に値も変更すること！
他にも[ここ](http://digitechlog.com/2009/10/18/flash-as2-move-to-as3-movieclip-property.html)にあるようなプロパティは変更必須

* _x, _y -> x, y
* _xscale, _yscale -> scaleX, scaleY
* _height, _width -> height, width
* _rotation -> rotation
* _visible -> visible
* _alpha -> alpha

## グローバル変数
exportRoot のプロパティにするのが一番楽かと思う。
exportRoot を _root にする方法でもあり。
(_root ならASでも同じ意味になるので変換しやすいかも…？)

## ボタンに割り当てられているスクリプト
そのままだと、toolkit for createJS の対象外なので、

* ボタン処理が必要フレームに別途キーフレームとスクリプトを追加
* イベントリスナでどうにかする

あたりで対応する。

## テキスト埋め込み
変数の値を宜しくムービーに反映してくれるやつだけど…
**toolkit for CreateJSでは当然非対応！**
なので、ボタンのときと同様に代入処理を作っておく

```js
hogeText = "hoge";
// message というTextオブジェクトのtextプロパティに代入してあげる
this.message.text = hogeText;
```

Textオブジェクトのtextプロパティを描画するので、
これに代入してあげればOK

# CreateJS（というよりJavascript）用にスクリプトを手直しする
## ライブラリ名の変更
同名ライブラリはCreateJSに変換した際に適当に連番されちゃうので、できるだけ名前を見ただけで使用用途がわかるようにしておくこと
とくに、**サーバやクライアントで変換するものは必須！**

## 擬似配列の排除
擬似配列とは…*Flash Lite 1.1の制約で配列が使用できないけど配列構造が必要なので、無理矢理配列っぽく実装したもの*とする
概ね、配列っぽく実装するために、**evalを駆使**していることが多い
eval は高コストなので、擬似配列は配列に実装しなおしたほうがいい…はず。

## できるだけオブジェクト（≒MovieClip）数を減らす
メモリ使用量は少ないほうがいい
ただし、下手に削るとハマりどころに記載した問題が発生するので、演出内容に影響しない範囲で削ること

# 主なハマりどころ
## Flashでは未使用なライブラリもオブジェクト定義されてしまう
toolkit for CreateJS で出力すると、**未使用ライブラリも律儀に出力**してしまうので、いらないものはFlash上で削除しておく
**「未使用なので影響しないはず」という考えは通用しない！**

## ボタンシンボルの扱いについて
CreateJSでは扱えないので、toolkit for CreateJSでButtonHelperを持ったMovieClipに変換されるので、最初からMovieClipにするのもアリ

ただし、透明なボタンを配置したいならボタンのままにして、
別途イベントリスナーでハンドリングしたほうが、
自前でButtonHelperを実装するより楽。

## 透明なオブジェクトについて
**存在はするが押せない**ので、ボタンの場合は以下のいずれかで対応しないといけないので要注意
* ボタンのままパブリッシュして別途イベントハンドラでハンドリング
* アルファ値を１にする
この場合、１％暗くなるのは已む無しと割り切る必要がある
* パブリッシュ後に、JS上でButtonHelperを用いて透明ボタンを作る

## CreateJSライブラリバージョン
toolkit for CreateJS で出力したままだと、
CreateJSのバージョンが0.6と古いく、
バージョンアップで解消する問題もあるので、
特に問題がなければ、バージョンアップしたほうがいい。

## CreateJS内のクリックイベントについて
詳しくは、[EaselJSのMouseEventを理解しよう](http://kudox.jp/java-script/createjs-easeljs-mouseevent)を参考すること

簡単に言うと、
**ActionScript と CreateJS(Javascript)では微妙に違う**ので、
同じ感覚でイベント処理を実装するとtouchstartで拾えなくて慌てるため注意すること！

## ムービークリップの初期化について
CreateJSではタイムライン上のフレームで出現したタイミングでオブジェクトが初期化されるので、メインタイムラインから呼び出すときは、オブジェクトが存在しており、初期化済みかを意識しておかないと悲しい目にあう

例：
* メインタイムラインからgotoAndStop(1)を実行しても再生されない
→実際は再生直後に初期化されてフレーム０で止まってた…
* ムービークリップ内のテキストを操作しようと思ったらundefinedで怒られる
→オブジェクト定義はあるけど初期化されてないので、テキストオブジェクトが存在してなかった

初期化の都合で、
今まで存在してなかったフレームで存在させるようにする場合は、
当該キーフレームをコピった後に、
見えたら困るオブジェクトのアルファ値を0にすればOK

ただし、以下のケースは要注意
* 事前に初期化済みでも、
利用後のタイムライン上で一旦削除
→削除したタイムラインの前に gotoAndPlay 等で再利用
…なんてときは、初期化処理が走るので要注意！
* 最初に初期化をしたいときは、アルファ値０では実行されないので、
ステージ外に配置するなりの工夫が必要

## クラシックトゥイーンを含むレイヤー
CreateJSの制限で１レイヤーにシンボルインスタンスはひとつしか配置できないので、
１レイヤーに複数の異なるインスタンスが配置されている場合は、
**インスタンスごとにレイヤーを分割すること**

なお、**配置したまま toolkit for CreateJS でコンバートすると、レイヤーごと削除されるよ！**

## ムービークリップの基準点とアニメーションの基準点
これがズレていると思わぬところでハマるので、**基準点は合わせておくこと！**

具体的には、
スクリプトで変形させる際に、
ムービークリップの基準点を基準に変形すると思っていたら、
アニメーションの基準点を基準に変形してしまった…
といった事態に遭遇します！！

※Flasherに聞いたら基準点を合わせるのは常識だそうですが、念のためです。

基準点の変更方法は、以下のとおり
1. シンボルを選択
1. Qを押下して自由変形ツールを呼び出す
1. 中心点を移動（※）
※左上にする場合は、ダブルクリックでOK

## 見えない（alpha=0）ムービークリップについて
見えるようになるまでタイムラインが動かない…

ざっとコードを追った感じでは、

* Container.isVisble() が false だと、
* Container.draw() で処理がスキップされてしまい、
* child.draw(ctx) が実行されないので、
* MovieClip.draw() も実行されず、
* MovieClip.updateTimeline() が実行されないので、
* タイムラインが動かない＆描画されない

…となるようです。

この場合、オプションでどうにかはできないので、以下のようにして回避
* alpha を0より大きくする
* 見せたくない要素を画面外に配置

## 同一レイヤーで異なるフレームごとに異なるクラッシクテキストがある場合
toolkit for CreateJS で出力すると、
各テキストのインスタンス名の別名で設定を無視して、
最初のテキストインスタンス名で連番出力される

addTween の際に文字列を突っ込んでるのでtoolkit のバグっぽく見えるけど…
これを回避するには、

* テキストインスタンス毎にレイヤ分割

toolkit を直すより現実的で簡単
…ただし、かなり手間がかかるのが欠点。

## 同一レイヤーで異なるフレーム毎に文言の違うクラッシクテキストがある場合
toolkit for CreateJS で出力した際に、
addTween の各フレームごとに文言が初期値で渡されてしまい、
置換がうまくいかなくなる。

置換する予定があるなら、**各フレームの文言を全て統一する**。
置換する予定がないならそのままでOK

## 子階層のムービークリップの特定ラベルを何度も再生させる場合
初期化を挟まない場合は、ラベル上のスクリプトは一度しか実行されない！

コードを追ってないので詳細は不明ですが、
一度実行したものをキャッシュしてしまい、
同じ条件で呼び出すとキャッシュしたものを実行されてしまいます。

なので、**テキストの置換等を再生先で行っている場合は要注意！**

## PreloadJS の速度改善
下記を追加
```js
loader.setMaxConnections(6);
```

ちなみに、デフォルトは**1**なので、並列読み込みせず…

## FPSの設定
詳細は、[CreateJS（EaselJS）でタイマを管理するTickerクラスの使い方](http://qiita.com/sangotaro/items/e4fc581a6819a867d0e0)を参照

基本はFlashから出力した時のデフォルト値

機種ごとに合わせて変動させるのが理想だが、
toolkit for CreateJS で出力したデフォルト値より早くすると、
早送りっぽくなるのでやめるのが吉。

低スペック端末向けに以下を設定しておくと安心できる
```js
// requestAnimationFrameに従うように変更
createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
```

計測用に以下の処理を仕込んでおくと若干幸せになれる。
```js
fpsLabel = new createjs.Text("-- fps", "bold 18px Arial", "#FFF");
stage.addChild(fpsLabel);
fpsLabel.x = 10;
fpsLabel.y = 20;
fpsLabel.text = Math.round(createjs.Ticker.getMeasuredFPS()) + " fps";	 // <- この処理は stage.update() 時に実行されるように仕込む
```

## 読み込み後のシステムアイドルが長い
巨大な音声ファイルを扱っているなら真っ先にそれを疑うべし！
仕様上必須ならともかく要らないならFlash上でライブラリごと削除すること！
どうしても必要なら…代替手段に置き換えるべし
* ストリーミング再生とか
* クライアントのネイティブ機能を呼び出して使うとか

## 端末の画面サイズに併せて描画サイズをリサイズしたい
stage.scaleX, stage.scaleY と canvas サイズの合わせ技
scale は元々の縦横サイズと document.body.で
…canvas サイズだけじゃ期待通りにならない

## どうしてもパフォーマンスが出ないなら
下記を参考にして、WebGLを用いるように修正することを検討する

* [flash →　createjs →　SpriteSheetBuilder でレスポンシブcanvasアニメーション](https://gist.github.com/ushiromuki/11143217)
* [Flashから書き出したアニメーションを実機上でスプライト化](http://jsdo.it/umeyoshioka/nfPG)

ただし、
CreateJSでWebGL描画刷る場合は、
スプライトアニメーションとして描画することになるので、
Flashアニメーションをスプライトアニメーションに変換する必要あり。

プロジェクト要件によっては、茨の道になることを覚悟しておくこと

# Tips的なもの
## タップハンドラ
Flashでは、フレーム別にbuttonシンボルに直接処理を割り当てていたが、これがCreateJSではできないのでどうするか…？

すぐに思いつく方法は以下のとおり
* 異なる処理分だけbuttonシンボルを作って、ボタンごとに処理を割り当てる
この場合、実装は簡単だが、オブジェクトが増えてメモリを食ってしまうことが問題になるかも…
* 状態遷移を管理して、ボタン動作は状態毎に切り分ける
goToStop()等で状態を更新しそこねないようにする必要があり、
そこが一番大変だったりする
* ボタンを押された時のフレーム数を使って切り分ける
難しいことは考えなくていいのがメリット
ただし、条件で使うフレーム値はラベルを用いて取得するようにしておかないと、
修正が入った時に大変フレームずれが発生する
実装例は以下のとおり

```js
// all_button handler
exportRoot.all_button.addEventListener("click", function(){
	
	var nowFrame = exportRoot.timeline._prevPosition;
	
	// CreateJS のフレームは 0始まり！
	if (exportRoot.timeline._labels['button_start'] == nowFrame) {
		// frame 3
		exportRoot.call(exportRoot.controller, 'startMovie');
	} else if (exportRoot.timeline._labels['button_next_start'] <= nowFrame && exportRoot.timeline._labels['button_next_end'] >= nowFrame) {
		// frame 55-62
		exportRoot.call(exportRoot.controller, 'nextCard');
	}
});
```

## 外部ファイルを関数っぽく使っている場合
この場合は、以下の様な感じで対応
* 外部ファイルの中身をコピペしまくる
呼ぶ回数が少ないなら、この方法がオススメ
* 実装時の思想を汲んで、exportRoot 等に関数を追加しちゃう
呼ぶ回数が多いなら、こっちのほうが楽
ただし、関数の実装については注意すること
特に、呼び出す箇所で this の指しているものが異なる点は要注意！
例：メインタイムラインとムービークリップ内のタイムライン

# PHPで動的出力するための作業ポイント
## Flashから出力されるjsは触らない
必要な処理（パラメータ設定や画像パス置換）は全てhtmlをいじるようにする
本当はhtmlも分離してhtmlの上書きも防ぎたいが、
**htmlはJavascriptほど変更しない**ので、
そこまで頑張らなくても大丈夫だったりする

## リソース配列をPHPで加工しやすいように置換する
例：```s/\t*\{src:"([^"]+)", id:"([^"]+)"\},?\n/"\2" => "\1",\n/```

## パラメータ配列をPHPで加工しやすいように置換する
例：```s/this.([^ =]+) *?= *?([^;]+);/"\1" => \2,/```