2010-07-11

Flashでグニグニ曲がるUIを作る方法

前にtwitterアイコンやpixivの画像をプヨプヨすることのできるpuyopixというコンテンツを作りました。

Puyopix -プヨプヨにするよ-

このページの右上にあるブログパーツもこれです。
解説をやると言っておいて、ずっと書いていなかったので書きます。
あんまりコードだらけにしても面白くないし、方法の概念的なものを図を交えながら説明していきます。
画像をプヨプヨする方法の概要と、それをUIに応用する方法です。

プヨプヨの実装

骨組みを作る

格子状バネという、わりと普通の実装をしています。
格子状に並んだ各点をばねのように接続します。
バネはお互いの点の距離が一定になるように、2つの点に逆方向の力をかけます。
フックの法則というのがあって、「F = -kx」とかいう式もありますが、プログラムとしての感覚は「本来あるべき距離の方向へ、ズレた分の○%だけ加速度をつける」って感じになります。
あんまり強すぎると、バネがぐわんぐわん動いて止まらなくなります。


距離だけの維持だと、斜め方向にペシャっと潰れてしまうので、バネには角度方向の力も持たせます。
各点に角度を持たせ、隣の点の位置との位置のズレを修正する方向に動こうとします。
接続された点を元に戻そうとすると同時に、点自体も逆側に回転するようにするのがポイントです。


これを16×16個生成し、骨組みにします。

画像を貼り付ける

読み込んだ画像の背景を切り抜き、外形を取ります。それを先ほどの骨組みと同じ大きさの正方形に分割し、有効な部分と無効な部分を判定します。


その後、テクスチャを貼り付けます。
貼り付けるのにはdrawTrianglesというものを使います。
ポリゴンのテクスチャと一緒で、三角形に分割した範囲に画像を貼り付けることができます。


グラフィックの背景は単純に1色で判断しています。また、既に透明箇所がある場合は背景抜きをしません。
見ていただければわかりますが、実際には正確な外形判定ではなく、バネの格子サイズまでの判定になっています
これでも不自然に見えないために

という工夫をしてあります。

問題点

とにかく処理が重くなりやすいことと、このままでは、ある程度の「硬さ」が維持できないということが問題になります。
処理の重さはArrayじゃなくてVector使うだの、オブジェクトではなく生のデータを展開して扱うだの、AS3の知識としての色々な方法があるのですが、専門的になるので置いておきます。
あまり僕の得意分野でもないし、もっと凄い人がいるので調べてみてください。(wonderflとかにいっぱいいます)
硬さが維持できないという問題について詳しく解説します。
今回、1秒間に20フレームの速度で作っていますが、この場合大抵ぐだっとした、ゼリーというより生卵態度の柔らかさになってしまいます
バネ定数を上げて、バネをどんどん強くしていっても、一定以上の弾力は得られず、むしろ不安定になり、ブルブル震えたり、バラバラになってしまい上手く行きません。
これは、どれほどバネが強くても、プログラムの処理が1フレームに1回であるため、2個先の場所へ力が伝わるのに次のフレームまでかかってしまうからです。
力が端まで伝わるまで15フレームかかっていたら、どうしても柔らかい表現になってしまうのです。

これを解消するための、高度な物理アルゴリズムは存在しているようですが、僕自身あまりアルゴリズムとか得意ではないので、もっと原始的な方法で解決を図っています。

1・力の伝達速度をスキップさせる

単純に、2個先までバネを繋いでしまえば伝達速度は2倍になります。
というわけで、puyopixは1つの点に対して、隣への4つだけでなく、隣の隣とも繋がる4つのバネをあわせて、8つのバネが接続したモデルになっています。

2・フレームでの処理を増やす

これも本当に単純な方法です。
ただし、フレームの速度を上げる、のではないことに注意してください。
画面表示するのは重い処理ですし、1秒に20回程度で十分です。
描画の処理と、計算の処理を分離して、計算のほうだけ1フレームに3回連続で処理させています。(当然、力を3分の1にしています)
単純なだけに調節が容易で、cpuと硬さの兼ね合いをはかるのにかなり重宝しました。
上の処理と合わせて、単純計算で6倍の伝達速度を実現できたことになります。
これだけでかなりゼリーっぽくすることができました。

持ち上げインターフェース

マウスへで持ち上げるために、点に透明な円形Spriteを配置し、それがマウスに反応するように実装しました。
円の半径はかなり大きくしておくのがポイント。
摘むポイントがズレることよりも、絵の上なのにドラッグできなかった場合のほうがストレスを感じるので。
円はある程度隣と重なり合うように、隙間がなくなるよう調節してあります。

UI機能の追加

作っている最中に、UIまでプヨプヨにしたら面白くね?と思いつきました。
本当はボタンも全部プヨプヨにしたかったのですが、画面内に複数プヨプヨ動くものがあると処理が重くなるので、そこは断念し、タイトル・ロード表示・テキスト入力・エラー画面の4つをプヨプヨにしてみました。
一番問題だったのは、テキスト入力の部分で、当然ながら通常のUIの挙動を損なってはいけません。
マウスドラッグによる選択とか、右クリックの挙動なんかが、できないもしくは使いにくくなってしまっては本末転倒です
試行錯誤した結果、このように上手く出来上がりました。

曲がっても、ひっくり返しても、正確にマウスドラッグや右クリックメニューに反応してくれます。
Flashの常識として、本来テキストフィールドは回転や拡大縮小のみで、このように曲げることはできないわけです。
種明かしをするとこうなっています。

テキストフィールドが2個あります。
手前の半透明のテキストフィールドが「本来のテキストフィールド」で、後ろ側のグニャグニャしているのが「見せかけのテキスト」です。
つまり、手前にある「本来のテキストフィールド」(本番では透明にしてある)がマウスや文字入力を受け取り、処理をして、後ろの「見せかけのテキスト」はその表示を反映している、というわけです。
そして、「見せかけのテキスト」は、マウスが最も近い点から本来のマウス位置を逆算し、それと合うように「本来のテキストフィールド」の位置を調節しています。
よって、マウスカーソルのある1点においては、「見せかけのテキスト」と「本来のテキストフィールド」は、同じ場所で重なっています。
これなら元のテキストフィールドの機能を損なうことはありません。
この自由形をUIにする方法は別のUIでも応用がきき、DotWarの3D入力欄や3Dボタンインターフェースでも使われています。
問題点は日本語入力をしようとすると変なことになってしまうということです。
変換待ちの文字列は表示に反映することができず、変な位置(というか本来の位置)に浮いて表示されてしまいます。
まあでも、puyopixもDotWarも日本語を入力する機会は無いので、この点は大丈夫でした。

  • コメント
  • 1
  • トラックバック
  • 0

コメント

  1. mkan より:

    こんにちは
    はじめまして
    このFlashおもしろいですね。
    なかなかFlashを触るきかいが少ないのですがこれを期にまたべんきょうしてみようかな

コメントする

ブログトップへ