ふと思いついたので、 タッチ入力の軌跡から図形をつくり、 それを操作するようなインターフェースを実装してみました。
つくりかたは大体思い描いていたのですぐにできるかと思いきや、
細かいところで何度も躓き、結局妥協しつつも完成までに三日・・・
ユーザ操作と物理演算という面倒なもの二つを扱うにはもう少し慣れが必要ですね。
動画
なにはともあれ、完成版をみないと認識を共有できないと思うので動画です。
こんな感じのものをつくります。
よく見ると何故か正方形が円のような挙動をしていますが、 これは急遽球のモデルを立法体のモデルに置き換えたためです。 詳細は昨日の記事を参照してください。
目指すところ
仕様や要求というにはあまりに大雑把ですがとりあえず目指すところは以下の通り。
- タッチ(マウス)入力の軌跡からなんかそれっぽい形状の図形をつくる
- つくった図形の中心を軸にして回転させる
- つくった図形とその他のオブジェクトの衝突判定ができる
アバウトすぎてなんだかよくわからないかもしれませんが、 実装部分を説明しながら順次補完していきます。
実装
実装は以下の手順で行いました。
- 位置情報の蓄積
- 点の選択
- 点の重み付け
- 線の膨張(多角形化)
- 曲線化
- 曲線上の点の選択
- メッシュ化
- 中心の計算
位置情報の蓄積
まず最初にすべきことは入力された位置情報の蓄積です。 指が離された時点で生成するため、指の触れた位置から離れた位置までの軌跡を記録する必要があります。
あまり深く考えずに毎フレームの位置をリストに突っ込むこともできますが、 いつまでも指が離されなかった場合にメモリを大量に消費することになります。 そこで現実的には環状のリストなどにして最新のN個の入力を保持することになります。
有限個の入力しか保持できないので、 ここで無駄な入力を省くことによるスペースの節約を考えます。 今回の実装では指の軌跡のみを対象としているので指が一定距離移動した場合にのみ記録することにしました。 しかし、この実装により細かい指の動きが検出できなくなっていることに注意する必要があります。
点の選択
これ以降の処理はある程度重たい処理であり、 点の数が多いとその分遅くなってしまうため早めに不要な点を間引いてしまいます。 また、点の数が多いと指のブレまで反映されてしまうためそれを防ぐ効果もあります。
今回の実装では大雑把に点間の距離で間引きました。
点の重み付け
軌跡を表現するそれぞれの点に対してどの程度太くすべきかを決定していきます。 この重み付けによって概形が決まります。
今回の実装では筆で描いたような形にしたかったため、 始点と終点を細く、角度が急な部分を太くするように重み付けをしました。
線の膨張(多角形化)
ここまでで重み付けされた点列が出来上がっているはずなので、 それぞれの点を重みに従って法線方向に押し出します。 正の方向と負の方向の両方へと押し出して、始点同士と終点同士を結ぶと多角形ができます。
折れ線の法線方向をどう定義するかは自由ですが、 今回は角を二等分する方向を使いました。
ここでできた多角形は衝突判定のためにPolygonCollider2Dに与えて使います。
曲線化
ゲームのデザインによっては多角形をそのまま描画するのもアリですが、 今回はまるっこい形にしたかったのでこの多角形を曲線化します。 曲線化にはCatmull-Rom曲線を使いました。
曲線上の点の選択
曲線は関数の形で表現されているため、 メッシュの頂点とするために適当にサンプリングして座標情報に落とします。 これにより曲線は再度多角形になりますが、前のものよりも滑らかになります。
メッシュ化
滑らかな多角形ができたので、これを三角形に分割することで描画可能にします。
多角形の三角形分割アルゴリズムはいろいろありますが、 どれもゲーム内で使えるように実装するには手間がかかり過ぎるので 今回は自前の簡易アルゴリズムを用いました。
中心の計算
ここまで出来たらあとは中心さえ計算してしまえばそれを軸にした回転もできます。 メッシュの頂点座標(衝突判定用の多角形の頂点でもいいかもしれません)を利用して中心を求めます。
多角形の中心をどこにするかは悩みどころですが、 やはり最も直観的なのは重心だと思います。 実は重心というのもいくつかあるらしいのですが、今回は幾何的重心を用いました。
完成!!・・・と思いきや
これでメッシュもできたし、中心軸もコライダもできた、 よって描画も回転も衝突判定もできる、と思っていたら意外なところで躓きました。
回転の制御
実装前は漠然と指の動きに沿って回転させればいいや、と考えていたのですが、 いざMoveRotationで実装してみるとなんともうまくいきませんでした。 理由は以下の通り。
- 図形同士が刺さりやすい
- 近い方から回転してしまう
- 回転速度が変化しない
かといってAddTorqueで実装しようにも指の動きにうまく追従しません。
一応理想的な動きとしてはこんな感じ。
- 指の動きに追従する(何かに引っかからない限り、指の位置に図形がある)
- 図形同士が交差しない
- 常に(何かに引っかかった場合にも)中心を回る指の方向に回転する
- 勢いをつけて指を離したときに回転速度を維持する
いろいろ組み合わせてそれっぽい動きを表現するしかないのかなぁ・・・
現状は指の位置と回転目標位置が近い場合にだけMoveRotation、
そうでないときにはAddTorqueを利用しています。
また回転が速すぎると物理演算時のパワーが強すぎるという問題もあって結構難しいです (質量とか調整すればなんとかなるだろうか?)。
おわりに
なんとなくゲームっぽいものをつくってみたところなかなか面白かったので、 このインターフェースを利用して何かつくってみたいところですが、 かなり時間をかけて調整しないと操作感が悪すぎてクソゲー化しそうな感じがします。 深く考えるのはまとまった時間がとれた時にしよう。