メイン

2011年1月28日

都市伝説の検証!Flash CS5のswfはCS4よりファイル容量が大きい!?
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

flashエンジニアのnaoです。

ディスカバリーチャンネルの番組に「怪しい伝説」(Mythbusters)ってのがありますよね。そう、"ワイヤーが切れて自由落下するエレベーター、地面に衝突する寸前に中でジャンプしたら助かるんじゃね?"とか、あほう 希少な検証をやるあの番組です。
ちょっと考えればわかりそうな事を真面目に検証する彼らを生暖かく見守る私ですが、この度自分自身、都市伝説に遭遇いたしました。
その都市伝説というのが、標題の「Flash CS5のswfはCS4よりファイル容量が大きい!?」なワケであります。


私の所属するプロジェクトでは、前任のflashエンジニアがFlash Profesional CS4で開発を行い、私を含めた後任のチームはCS5で開発を行っております。
この時に、前任からの引き継ぎ注意事項として

『swfファイルはCS4で書きだしたほうが軽くなる』

という物があり、実際に前任から引き継いだflaファイルでswfを書きだしてみると、本当にCS5とCS4で容量が違ったのです。CS5で書きだしたswfは、3KBほど容量が重い。
なにこの怪奇現象こわい
flash liteを利用する製品では、3KBは命取りです。
臆病風に吹かれまくって天高く舞い上がった私は、CS4のライセンスを持っているチームメンバーに依頼して書き出しをしたのですが、上空の偏西風に流されてミッドウェイあたりまで来たところで、はたと気が付きました。

同じ処理をしている100KB程度のバイトコードで、3KBもサイズが違うのって、明らかにおかしくね?

100KBのうちの3KBといえば、全体の3%である。1989年に導入された当時の消費税率と同じである。当時の消費税の税収は、なんと6兆円!まぁ、金額は関係ないのですが。

flashのバイトコードは、1処理で2バイトとか3バイト程度。それに変数名が付いたって、ActionScriptの1行分の処理でもせいぜい数十バイトである。いったい、どこで容量が増えるのだろうか?

【実験1】
まず私が試したのは、diffコマンドによって差分を比較する実験。swfファイルの差分を取って比べてみたら、何かわかるのではなかろうか、と思ったのですが、この実験はあえなく失敗。考えてみたら、バイナリの状態のコードなんて人間が一覧して分かるわけがない。実験以前に問題だった。Mythbustersを哂えない。


【実験2】
次に行ったのは、swfmillをインストールしてswfファイルをテキスト化し、再びdiffを取る実験。こちらは成功。そらそうだ。
内部の構造を追っていくと、ActionScript部分は変化がない。しかし、途中で謎のベクタ描画のグラフィックスオブジェクトに遭遇する。CS4には存在しない。CS5で書きだしたswfにのみ存在するオブジェクトなのだ。犯人のしっぽをつかんだ


【実験3】
初心に返り、Flashで書き出す際にファイルサイズのレポートを出力してみる。グラフィックスオブジェクトが書き出されているなら、何か記述されているはずである。
しかし、全てのオブジェクト名をチェックしたが、見覚えのないオブジェクトは存在しない。


【考察】
・ActionScript部分の変化は観察されない
・内部に謎のグラフィックスオブジェクトが挿入されており、これがファイルサイズ増の原因らしい
・しかしサイズレポートには怪しいオブジェクトは記述されていない


さて、この謎のグラフィックスオブジェクトは何か。レポートには名前も出ない存在だけのデータである。が、よくよくサイズレポートを見ているうちに、重大なものに気がついた。

なんと

デバイスフォントが埋め込み処理されている


のである。


-再現イメージ-

_等幅 : 確たなデりなまタ認しくー

いやいやいやいや、ないわー。完全にFlash Profesional CS5のバグである。
原因がわかれば対処方法も簡単。埋めこまれてしまっているなら、再定義してやればいい。
今回は以下のような手順で修正を行いました。


・デバイスフォントを指定しているテキストボックスを選択
・別の日本語フォント(_明朝でも何でもいい)に変更する
・全部のデバイスフォントのテキストボックスで上記を行う
・ファイルを保存して閉じる
・ファイルをもう一度開く
・全部のデバイスフォントのテキストボックスを、もう一度、等幅などのデバイスフォントに戻す
・保存して閉じる

以上である。すみませんね、手動作業で。
回避方法はあるかもしれないけど知りません。だってもう、私の環境では関係ないから。

書きだしたswfファイルは、みごとCS4で書きだしたswfと完全に一致しました。
現在では無事、CS5で開発を行っております。
なお、この不具合はCS4で編集したファイルをCS5で読み込む場合に発生します。
一度対応するか、初めから CS5で製作されたファイルでは発生しません。

こうして、Flash CS5のswfはCS4よりファイル容量が大きい!?疑惑は嘘っぱち(まぁバグっぽい物ではあったのですが)都市伝説と証明され、事なきを得ました。
何事も検証してみることが大事だよね☆

都市伝説の例
あやしい!と思った人は検証してみればいいと思うよ。

Mythbusters: Collection 1 [DVD] [Import]
Discovery Channel (2007-05-22)
売り上げランキング: 79419

2011年1月 7日

flashlite1.1で文字圧縮してみた
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

明けましておめでとうございます&はじめまして。12月に入社した加藤です。

FlashLite 2.0をやるつもりで入ってみたらバリバリ1.1だったので、出す機会を逸していたネタをここで出したいと思います。

FlashLite 1.1の制限にはいろいろありますが、開発する上で一番困るのは100KB制限です。
100KBと言うと、このページの右カラムに並んでいる著者一覧の写真が一つだいたい8KBくらいですので、だいたい12枚分になります。その中にグラフィックとスクリプト両方をつめ込まねばなりません。
普通のFLASHならパブリッシュ設定の「ムービーの圧縮」にチェックを入れれば圧縮が有効になるのですが、残念ながらFlashLite 1.1ではその機能はグレーアウトされて使えません。
そういうわけでFlashLite 1.1のエンジニアは日々シェイプの最適化から変数名の文字数まで、地道な作業に血道をあげています。

しかし、生成エンジンで変数差し込みをする場合、いくらswfを最適化したところで「100KB-swfのファイルサイズ」分のデータしか入れられません。Twitterクライアントを作っていたとき、これだと50件表示までしか出来なくて何とかして100件表示にしたいと思い辿り着いたのが文字圧縮です。

続きを読む "flashlite1.1で文字圧縮してみた" »

2010年11月12日

自力でUNIX TIMEを計算してみよう
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

 Flashエンジニアのくせに今日もFlashの話題を振らないnaoです。こんにちは。
 クビにならないよう努力はしています。

 今回のお題。何の役に立つのかと問われれば、漢らしく「何の役にもたたん」と答えましょう。しかし、ひょっとしたらいつか、使う日も来るかもしれない。こうして諸葛孔明の様に何事にも用意周到に備える私の部屋はモノで溢れかえっております。みろ、部屋がゴミのようだ。だってさー、PowerBook Duo(270cカラー液晶だぜ)とか、そのうちプレミアつきそうじゃん?いつか本当に必要な時が来たら、その時には声高らかに朗々と「こんな事もあろうかと」と宣言をするのである。

 というわけで今回は、まるで昔使った思い出の品のように、近年すっかり過去の遺物へと変貌し始めているUNIX TIMEです。UNIX TIMEといえば昔Perlのcgiで掲示板スクリプトを作るときなんかにはよく使われましたが、最近ではそこらの無料レンタルサービスでもDBが普通に使えるので、きょう日常用するのはtimestamp型ですよね。人間にも分かりやすいし、なにより遠い未来でも利用できる。それに対してUNIX TIMEは、2038年(2038年問題でググろう)までしか使えません。
 ではなぜ今更UNIX TIMEなのか。それはやっぱり、いつか使うかもしれないじゃん?という個人的なこだわりです。まぁ、使おうと思えばいくらでも使う場面はありますけどね。あえて使う理由も無いんですが。

 UNIX TIMEとは、1970年1月1日0時0分0秒(これをUNIX EPOCHという)より始まるタイムスタンプで、1秒経過すると値が1増えるという非常に扱いやすい性質を持ちます。そのため、例えば1970年1月1日0時1分0秒なら、UNIX TIMEは60というとうに、非常にシンプルです。この性質を利用すれば、例えば現在の時刻が判明すれば、UNIX EPOCHからの秒数を計算して、現在のUNIX TIMEを得ることが出来るのである。今回は、それを計算してみようというわけです。
 勘の良い人はそろそろ気がついたかもしれませんが、実は今回のラボブログ、目的はUNIX TIMEを計算することですが、本質的にはグレゴリオ暦を計算する計算式の話になります。暦が計算できて、UNIX EPOCHからの日数の差分が得られたら、あとはそれを秒数に変換すれば、UNIX TIMEになるのです。

グレゴリオ暦とは
グレゴリオ暦(グレゴリオれき)とは、1582年にローマ教皇グレゴリオ13世がユリウス暦を改良して制定した暦である。現行の太陽暦として世界各国で用いられている。単に新暦(英語:New Style、略称:N.S.、NS)と呼ばれる場合もある。現在使われている西暦はグレゴリオ暦である。
以上、wikipedia グレゴリオ暦より引用。

早い話が、今現在使われている暦の事である。500年近く前に作られたアルゴリズムなんですって。すごいよね。
それでは、さっそくUNIX TIMEの基準となる紀元元年1月1日からUNIX EPOCHまでの経過日数の計算をしてゆきましょう。

閏年の判定の仕方
閏年の判定は、条件式で書くとこんな感じになります。

year % 4 == 0 && year % 100 != 0 || year % 400 == 0

1)西暦を4で割って余りが0(4の倍数)の年は閏年
2)だけど100で割って余りが0(100の倍数)の年は除外して
3)なおかつ400で割った場合に余りが0(400の倍数)の年はやっぱ例外的に閏年

ということですね。

 これだけ理解しておけば、何かしらのプログラミング言語であれば、forとか構造体を使って暦の紀元元年1月1日からの経過日数を計算できますが、そこは公衆の面前に晒されるラボブログの記事。もう一歩突っ込んで、公式を使ってイッパツで計算しましょう。まぁ、誰にも見せないコードの中身だったら、面倒だから何も考えずforとかで回しちゃいますけどね。

グレゴリオ暦換算紀元0年1月1日からの経過日数の数え方
ここに「Fairfieldの公式(フェァフィールドのこうしき)」という非常に素晴らしい公式があります。こんなに素晴らしいのに、何故かマイナー。google先生にお伺いしても、あんまり情報がありませんが、今回はコレを使います。

Fairfieldの公式による経過日数の算出
365*(y-1)+[y/4]-[y/100]+[y/400]+31+28+1+[306*(m+1)/10]-122+(d-1)

さぁ、ややこしいモノが出てまいりました。まずは、順を追って見ていきましょう。

365*(y-1)


まずここは簡単。計算したい日の前年までの年数*1年の日数ですね。

+[y/4]-[y/100]+[y/400]

次にこちら。ここは、閏年の処理ですね。[ ]の記号はガウス記号ってヤツで、int()と同じ小数点以下切り捨てですですが、これは高校とか大学で習うのかどうか知らないので、一応説明しておきました。自分中卒なので。 ここまでで、計算したい日の前年までの日数が出ます。

+31+28+1+[306*(m+1)/10]-122+(d-1)

で、問題はこちら。ここが公式の決定的な肝であり、難関でもあります。 ざっくりと説明すると、この公式では1年間を3月〜14月として計算することで、2月の最終日を1年間の最後の日として扱うことが出来ます。 まず「+31+28+1」この部分が1月と2月(公式中の扱いとしては前年の13月と14月)の日数。 次に「[306*(m+1)/10]」ここが、3月〜12月(公式中では1月〜10月)の日数。 最後に「-122+(d-1)」で、13月と14月の補正と、計算したい日の当月の日数になります。

ざっくりとした説明になりましたが、こんなものは数字大好きな人が理解していればいいことなので、私のような凡人は、さらにこの式を発展させて、以下のような式を使います。

365 * y + [y/4] - [y/100] + [y/400] + [306 * (m+1) / 10] + d - 428

「428」って何よ、とか言わないでください。元の式を計算すると、まぁなんとなくこんな感じになります。13月と14月の分と、1年の始まりが3月からになるようにしていた分を全部予め計算しておくと出る数字です。
数学が大の苦手の私的には、こっちがありがたい。

それでは、早速基準となるUNIX EPOCHである1970年1月1日0時0分0秒までの経過日数を計算しましょう。

365 * 1970 + [1970/4)]- [1970/100] + [1970/400] + [306 * (1 + 1) / 10] - 428 + 1 = 719161
 これで、UNIX EPOCHは、グレゴリオ暦換算紀元元年1月1日から719161日経過している、という事がわかりました。  次は、この719161を起点として、そこから何日経過しているかを計算します。
_EPOC_TIME = 719161;
// Fairfieldの公式で算出した719161日からの経過秒数の算出
diff = ((365 * year + int(year / 4) - int(year / 100) + int(year / 400) + int(306 * (month + 1) / 10) - 428 + day) -  _EPOC_TIME) * 24 *60 * 60;

せっかくなのでFlash Lite 1.1のActionScriptで書いてみました。
これに、時刻を足してあげれば、UNIX TIMEの完成です。

_EPOC_TIME = 719161;
UNIX_TIME = ((365 * year + int(year / 4) - int(year / 100) + int(year / 400) + int(306 * (month + 1) / 10) - 428 + day) -  _EPOC_TIME) * 86400 + (hour * 3600) + (minute * 60) + second;

ほら1行でできた。エンジニアっぽいっ!

 今回はグレゴリオ暦の計算を用いてUNIX TIMEを導き出したわけですが、これを応用すれば、いろいろな事が計算できます。例えば、グレゴリオ暦換算の紀元元年1月1日は月曜日なので、Fairfieldの公式による経過日数を7で割った余りで曜日が判定できます。余り0なら日曜日。6なら土曜日。ちなみに、この割り算の"余り"を求める事をモジュロ演算と言います。演算子の"mod"や"%"のヤツ。サマーウォーズで見たよね?こいつのおかげで、ケンジは得意げになりナツキ先輩はメロメロなわけである。みんな覚えておこう。いつか「こんな事もあろうかと」という日が来るかもしれない。

 余談ですが、宇宙戦艦ヤマトの工場長兼技師長である真田志郎氏といえば「こんな事もあろうかと」で有名ですが、wikipediaによると劇中にそんなセリフは無いそうです。

えー、がっかりー。

2010年7月26日

オブジェクトをマウス操作で移動させる
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは、はじめまして!
goです。

6月からActionScriptのプログラマとして、ウノウに参画することになりました。
エンジニアとして経験がまだまだ浅いので、リファレンスを片手に日々奮闘しております。

Flashを扱うエンジニアとして、ここではインタラクションに関わる小ネタをActionScriptで披露していこうかと考えています。

ということで、今回はマウスを使ったオブジェクトの移動のお話です。

以下の方針に従って、作業を進めていきます。

1、画面全体でMOUSE_DOWNとMOUSE_MOVEのイベント監視
2、MOUSE_DOWNのイベントが発生したときに、マウスの座標、移動対象のオブジェクトの座標を取得します。
3、MOUSE_MOVE時に、移動前のマウス位置と、移動後のマウス位置の差(つまり移動量)を取得します。
4、移動前の円の座標に移動量を加算して、移動後の円の位置を取得します。

以下、ソースコードと実行結果です。

package {
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	
	public class ObjectMove extends Sprite {
		
		/*円を描画されるオブジェクト*/
		private var childSprite:Sprite;
		
		/*マウスカーソルの位置*/
		private var thisX:Number, thisY:Number;
		
		/*円オブジェクトの位置*/
		private var circleX:Number, circleY:Number;
		
		public function ObjectMove() {
			/*円を描画するchildSprite(Sprite)を表示リストに追加する*/
			childSprite = new Sprite();
			childSprite.x = 60;
			childSprite.y = 60;
			addChild(childSprite);
			
			/*childSpriteに円を描画する*/
			childSprite.graphics.beginFill(0x000000);
			childSprite.graphics.drawCircle(0, 0, 40);
			childSprite.graphics.endFill();
			
			/*マウスダウン時のイベントハンドラーを設定*/
			addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			/*マウスムーブ時のイベントハンドラーを設定*/
			addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		}
		
		/*マウスダウン時にマウスの位置と、円の位置を取得する。*/
		private function mouseDownHandler(event:MouseEvent):void{
			thisX = this.mouseX;
			thisY = this.mouseY;
			circleX = childSprite.x;
			circleY = childSprite.y;
		}
		
		/*マウスが動いた時に、円を移動させる。*/
		private function mouseMoveHandler(event:MouseEvent):void{
			if(!event.buttonDown) return;
			childSprite.x = this.mouseX - thisX + circleX;
			childSprite.y = this.mouseY - thisY + circleY;
		}
	}
}

MOUSE_MOVEイベント発生時にMOUSE_DOWNイベントが発生していない場合は、値を返すのがポイントです。
こうすることで、マウスカーソルが円の内側にある場合に限り円を移動することが出来ます。
簡単ですね!

次回は、もっといろんなモーションを加えていきたいと思います。
ではでは。


About ActionScript

ブログ「ウノウラボ by Zynga Japan」のカテゴリ「ActionScript」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

次のカテゴリはandroidです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Zynga Japan