2011年1月27日木曜日

趣味のコードがかけない

いやー仕事忙しすぎです。趣味のこと何も出来ない。アニメーションライブラリもチュートリアル作成で止まってるし、Wonderflに投稿するネタを考える時間もない。知り合いからモノづくり頼まれてるけど、絶賛逃げ中。

2011年1月20日木曜日

Wonderflでログ出力

Flashの開発でよく使うtraceですが、Wonderflで開発すると、traceしても何も起きないのでデバッグが不便でした。
でもこの方法ならWonderflのコンソール上にtraceの内容を出力することができます。

Wonderfl.log - wonderfl build flash online


どうやって見つけたかというと、たまたま自信のソースをEmbedして中身を見たら、Wonderflというクラスが組み込まれているのがわかりました。

forked from: forked from: forked from: include - wonderfl build flash online


じゃあdescribeTypeしてみよう

logメソッド発見

じゃあ呼び出してみよう

なんか出た

trace関数を上書きしてやれ

おお便利じゃん

という流れ。標準で組み込まれるといいですね。

2011年1月16日日曜日

Flash用のPixelBenderはちょっと微妙

あくまで主観です。
  1. デバッグできない
  2. ツールキットがスペース含むディレクトリに保存できない
  3. 思ったほど速くない
  4. ShaderJobが再利用できない
  5. GPU使ってくれない
  6. FlashBuilder上で開発できない
ってところでしょうか。デバッグビルドとリリースビルドで速度差があるので、
基本的にGPUではなくFlashPlayerで計算しているのでしょうか。
次期FlashPlayerのmolehillだとGPU使えるようなので、GPGPUもできるようになるのでしょうか。
100万パーティクルが60fpsで動かせるのを心待ちにしています。

ちなみに速くないといっても、同じ処理を行うAS3のコードよりは速いです。自分の環境で2倍強くらい。
4つの値を1パーティクルとして、パーティクルを100万(Vectorで400万要素)用意して、PixelBenderに投げても、100msから150msくらいはかかってました。AS3なら200msから300msくらい。
でも、パーティクル情報を更新しつつ、ビットマップにも書きつつということを同時にやってくれないので、トータルするとそんなにコストは変わりません。

セキュリティーホール??

FlashのLoaderクラスは、crossdomain.xmlが設置されていなくても、クロスドメインで画像が読み取れます。
まぁ、これはいいでしょう。ただし、このLoaderをBitmapDataに転写したりはできません。
が、実は抜け穴があります。以下をご覧ください。

外部画像をビットマップ化する - wonderfl build flash online


操作できてますね。ここで思ったのが、「外のサイトのHTMLをLoaderInfoのbytesから取得できるのか」です。
Loaderとしては当然画像やSWFではないですからエラーになりますね。しかしbytesが取得出来れば、文字列化してHTMLを解析することができます。
そこで以下を見てください。

ストーキング - wonderfl build flash online


forked from: Twitterのストーキング - wonderfl build flash online


結論からいうと、bytesは0バイトですが、totalBytesは0ではありません。何バイト読めたかがわかります。
つまり、ログインしている場合としていない場合で、HTMLのサイズが大きく違うようなサイトの場合、
ユーザがログインしているかを判断することができてしまいます。

1つの広告で、Mixiを使っている人向けの広告と、使っていない人向けの広告を出し分けるみたいなことができてしまいます。
これってセキュリティホールですよね・・・??

2011年1月6日木曜日

MatrixのtransformPointの最適化

Twitterの方にもつぶやいたのですが、MatrixのtransformPointは、Matrixの各要素を元に直接求められることに気づいたのでメモ。
var m:Matrix = ...;
var x:Number = ...;
var y:Number = ...;
x = x * m.a + y * m.c + m.tx;
y = x * m.b + y * m.d + m.ty;
通常だと入力用にPointのインスタンスを作る必要がありますが、最適化としてPointを使い回すというやりかたもあります。でも戻り値が新しいPointクラスなので、使用メモリが増えるし、生成コストが馬鹿になりません。
この方法ならa/b/c/d/tx/tyを使い回せば、大量に座標変換しても高速に計算できます。

ベジェのパスでアニメーションできる機能を追加した

ベジェのパスでアニメーションできる機能を追加しました。距離の割合を元に座標を求めているので、きつい曲線などでも等速でアニメーションできます。
ライブラリの機能としては、曲線と直線の組み合わせの情報を元に、t(0.0から1.0)に対してセグメントの距離と全体の距離から、x/yを求めるようになっています。Matrixの適用も可能です。
そのパスライブラリを使ったサンプルをWonderflにアップしました。

ベジェと直線のパス上を等速でアニメーション - wonderfl build flash online


参考元はこちらのサイトです。実際のところ数学は全く苦手なので、何やってるかわかりません。移植して最適化しただけです。
目標としてはSVGの命令を網羅したいところなのですが、積分が全くダメなので円弧の楕円積分などで躓くでしょう。。。

2011年1月1日土曜日

アニメーションライブラリの進捗具合

今開発中のアニメーションライブラリの進捗具合についてです。

開発も進んで、相当速くなってきたのと、メモリの使用効率もそこそこによくなってきました。コア機能については、そこそこに充実してきましたので、一旦開発を止めて、Tweener互換ライブラリに着手しようと思っています。Tweener互換機能が実装できたらテストを行って、version1.0として初リリースを行おうと思います。

各Tweenライブラリの開発元で用意しているベンチで比べても、同等もしくは高速という感じになってきたので、パフォーマンスは魅力的な部分だと思いますが、いまどき速いくらいじゃ魅力もないので、魅力的な機能を入れていかないと使ってもらえないだろうなと思っています。

まず1つ目がTweener互換機能。TweenerのSWCと入れ替えるだけで、今までのコードを修正することなく、アニメーションのパフォーマンスが上がるというもの。特に、大量のアニメーションを並列で動かすときに、差が出ると思います。逆に1つのオブジェクトを動かすくらいなら大差はないと思います。

ただし、完全互換は難しいと思っています。そもそもベースが違うし。TweenerクラスとSpecialPropertyとAuxFunctionsクラスは残すことになると思いますが、それ以外は残さないと思います。もし、拡張ライブラリ等でPropertyInfoObjとかアタッチするようなことがあれば、動かないでしょう。TweenerクラスとSpecialPropertyクラスだけ使っていれば、基本的に問題ないようにしようと思っています。

あと、もう一つ特徴的な機能としてFlex対応というところ。これもまだ未着手ですが、コアの部分では対応済みです。

Flexの場合、通常は24fpsで動かすことになりますが、アニメーションで24fpsは正直スムーズな動きとは言えません。ぬるぬる感を出そうとすると60fpsくらいが丁度いいです。そこで、通常時は24fps、アニメーション時はもっと高いフレームレートを出すために、通常のタイマーからFlex向けのタイマーへの差し替え機能を提供しています。

通常時はDisplayObjectクラスのENTER_FRAMEによるタイマー処理を行っていますが、TimerクラスのTIMERイベントによるタイマー処理を行うように変更できます。このタイマーは標準で10ms単位でイベントを呼び出すようにしており、またTimerEventのupdateAfterEventの呼び出しを行いますので、24fpsでも24fps以上のフレームレートで再描画が行われます。

Tweenの高速化についてまとめ。その4

年も変わったんで、文体変えてみようかな。ちょっと固い感じがする。

と、さておき、Tweenの高速化についてまとめてみましたが、徐々に実際のコードをベースに、細かいところについて書いていこうと思います

まずは、以下のようなリンクリストのノードがあったとします。
public class Node{

    public var next:Node;

    public var val:uint;

    public function Node(val:uint, next:Node){
        this.val = val;
        this.next = next;
    }

    public function any():void{
        trace(this.val);
    }
}
では、これを処理するコードはというと、
//リンクリストを処理する関数の中と仮定
//_headはリンクリストの先頭のprivate var の値

//whileの場合
var node:Node = _head;
while(node !== null){
    node.any();
    node = node.next;
}

//forの場合
for(var node:Node = _head; node !== null; node = node.next){
    node.any();
}
という感じになると思います。もしも、「nodeが仕様上1つ以上ではあるが、実態としては1つのケースが多い」と仮定した場合、for/whileをすっ飛ばすことによって最適化することができます。
//リンクリストを処理する関数の中と仮定
//_headはリンクリストの先頭のprivate var の値

//whileの場合
if(_head.next === null){
    _head.any();
}else{
    var node:Node = _head;
    while(node !== null){
        node.any();
        node = node.next;
    }
}

//forの場合
if(_head.next === null){
    _head.any();
}else{
    for(var node:Node = _head; node !== null; node = node.next){
        node.any();
    }
}
できれば、anyという関数も、呼び出し元の所にインライン展開してしまったほうが、もっと高速に動きます。業務ロジックを1箇所に固めたほうが当然メンテナンス性も可読性も良く、品質も管理しやすいですが、冗長でも関数呼び出しを減らすことによって高速に動作します。関数呼び出しのオーバーヘッドは馬鹿になりません。