はじめに
このシリーズでは iOS/Android ゲームアプリの描画手法として、ベジェ曲線の可能性を模索します。
第一回目は、ベジェ実行環境の準備と、点の情報であるアンカーポイントを利用した形状の変化について探っていきます。
サンプルアプリのプロジェクトは GitHub にあります。
https://github.com/hakumai-iida/BezierSample
次回:【その②:ストローク(線)による表現の付け足し】
ことの発端
スマホを新しくした際、お気に入りのアプリのキャラクタが、ボヤけて表示されて残念な気分になったことはないでしょうか?
これは、アプリに保持される画像データの解像度が実行環境に対して低いせいで、拡大表示されてしまっているからです。
このボヤけを回避するには、アプリ側で大きな解像度の画像を用意する必要があるのですが、これはいちいち手間ですし容量も増えてしまいます。
それに、端末の解像度の向上とイタチごっこになるため根本解決とはいえません。
では、ゲームアプリの画像表示を解像度に依存しないベジェ曲線で行ったらどうでしょうか?
端末の解像度と同様、スマホの性能もどんどん向上しています。
最近の高性能端末だったら、ベジェ曲線の描画を余裕でこなしてくれるのではないでしょうか?
ものは試しです。
iOS/Android 上でベジェ描画によるゲームが制作ができそうか否か、色々と検証してみましょう。
ベジェ曲線のおさらい
さて、検証を始める前に、ベジェ曲線に関して簡単におさらいしましょう。
ベジェ曲線とは「座標+補助情報」で曲線を表現する手法で、ドロー形式と呼ばれるデータ形式です。
ドロー形式のデータは実行時に線を計算して描画するため、解像度に依存しないのが強みです。
が、形状が複雑だったり塗りの面積が広くなるほど、実行時の処理が重くなるのが弱みです。
一方で、png や jpg といったデータ形式は、ペイント形式と呼ばれます。
全ての画素をデータとして保持するため、計算不要でどのような見た目の画像も表現できるのが強みです。
が、画素の情報は固定的なので、実行環境の解像度が大きすぎると表示がボヤけてしまうのが弱みです。
アンカーポイントとパス
ベジェ曲線では座標のことを「アンカーポイント」と呼びます。
このアンカーポイントを複数置き、順番につなげることで「パス(線)」を表現します。
↓ 図:アンカーポイントを4つ置いてパスを構成
上記画像において、黒い四角がアンカーポイントとなります。
このアンカーポイント間【0→1】、【1→2】、【2→3】、【3→0】にパスを引くことで、形状を表現します。
方向線
アンカーポイントだけだとまっすぐなパスしか表現できないので、パスを曲げるための補助的な情報が必要です。
パスを曲げるためにはアンカーポイントに「方向線」と呼ばれる2種類のベクトルを付与します。
*誘導ベクトル*
1つ目の方向線は、「アンカーポイントから出発するパスを誘導する」効果を持つベクトルです。
この効果はパスの引き始めほど強く影響し、ここでは「誘導ベクトル」と呼ぶことにします。
↓ 図:アンカーポイントに誘導ベクトルを追加(4本の青い矢印)
それぞれのアンカーポイントから伸びる誘導ベクトル(青い線)に沿って、パスの描き始めが曲げられました。
*誘引ベクトル*
2つ目の方向線は、「アンカーポイントに到着するパスを誘引する」効果を持つベクトルです。
パスの終わり際ほど強く影響し、ここでは「誘引ベクトル」と呼ぶことにします。
↓ 図:アンカーポイントに誘引ベクトルを追加(4本の赤い矢印)
誘導ベクトルとは逆に、アンカーポイントの誘引ベクトル(赤い矢印)に引っ張られて、パスの終わり際が曲げられました。
誘導ベクトルと誘引ベクトルは、1つのアンカーポイントに両方指定することもできます。
↓ 図:誘導ベクトルと誘引ベクトルで円の描画
アンカーポイントと2つの方向線を駆使することで自由自在にパスを描く。
これがベジェ曲線の基本であり最大の強みです。
アプリの想定とサンプルデータ作成
今回は線の描画をベジェ曲線で行う以外、いたって普通の2Dゲームを想定します。
また、素材の表示データ(絵の関連性やアニメを指定するもの)も、一般的な2D表現形式を想定します。
具体的には、Live2D っぽく、パーツ別に素材を作成して組み合わせることで、1枚の絵としてのバリエーションを出せるようなデータ構造を目指します。
では実際にデータを作成してみましょう。
下絵の作成
パーツ分けした下絵を作成してエディタに取り込みます。
(※Live2D のサンプルを真似してミクさんを作成してみます)
↓ 図:パスを作成する際の下絵(パーツ単位で素材を作るのでバラバラ)
パーツ素材の作成
エディタ上で下絵をトレースしていきます。
パーツ素材の組み合わせ
バラバラに作成したパーツを組み合わせることで、絵を構築していきます。
アンカーポイント(点)による形状の変化
今回作成したサンプルデータでは、アンカーポイント(点)に色々な補正値を設定しています。
この補正値によりアンカーポイントの座標が調整され、描画される線が変化する仕組みとなります。
サンプルアプリで確認できる内容は下記となります。
移動補正値
アンカーポイントは、移動(風やキャラクタの動き)に対する補正値を保持します。
髪や布などのアンカーポイントにこの値を高めに適用することで、キャラクタの動き(移動)に沿った変化をさせることが可能です。
(※サンプルプアプリの「SWY」と「MOV」ボタンの機能です)
↓ 図:移動値による髪などのなびき(移動方向を目で追います)
体型補正値
アンカーポイントは、体型パラメータに対する補正値を保持します。
この値により、キャラクタの体を細くしたり、背をのばしたりすることが可能です。
(※サンプルプアプリの「H」と「V」ボタンの機能です)
サイズ補正値
パーツは、サイズパラメータに対する補正値を保持します。
この値により、パーツ単位にサイズの変更が可能です。
(※サンプルプアプリの「S」ボタンの機能です)
↓ 図:サイズパラメータによる変化(ツインテールが大きめに補正されています)
回転補正値
パーツは、回転パラメータに対する補正値を保持します。
この値は、腕や肘などの稼働領域の影響されやすさとなり、絵によって受け入れられる角度を制限します。
極端な角度を指定しても、関節があらぬ方向へ曲がらないように調整することが可能です。
(※サンプルプアプリの「T」ボタンの機能)
ベジェ曲線ならではの表現
アンカーポイントの補正値とは別に、実行時に画像を生成することによる表現手法は下記となります。
擬似的な傾け
絵全体を擬似的に傾けることで、画像に左右/上下に傾いた印象を与えることが可能です。
傾け基準点との相対位置に応じて、アンカーポイントが補正されます。
(※サンプルプアプリの「LR」と「UD」ボタンの機能)
ストロークの切り替え
パスの描画ブラシ(ストロークの種別)を変えることで、ストロークの差し替えが可能です。
これぞベジェ曲線といえる機能です。
(※サンプルプアプリの「STROKE」メニュー項目の機能)
パレットの切り替え
処理の単純化とメモリ容量を抑えるために画像をパレット形式で管理しているので、パレット差し替えが可能です。
(※サンプルアプリの「COLOR」メニュー項目の機能)
素材の差し替え
一般的な2D素材作成でよくある、パーツ差し替えが可能です。
サンプルデータでは、基本素材とは別に「ネギの着ぐるみ」を作成してパーツ差し替えに利用しています。
(※サンプルアプリの「COSTUME」メニュー項目の機能)
(※パーツ差し替えについては【その③(完結):パーツによる絵のバリエーション】でさらに詳しく深掘りします)
課題
今回作成したベジェ曲線環境ですが、アプリ制作へ導入するには大きな課題が2つあります。
描画速度
残念ながらというか、やはりというか、とにかく画像の生成が遅いです。
(※端末の性能と解像度のバランスにもよりますが、1回の画像生成に数十ミリ秒かかっています)
特に、大きなサイズでリアルタイムにアニメさせようとすると、処理落ちがひどいことになります。
一方で、1枚絵ジェネレータとして、アプリ起動時にキーフレーム別に画像を生成しておき、
その後、静的に参照するような使い方であれば問題なく導入できそうです。
データ作成用のエディタ
今回、サンプルデータの作成はデバッグ画面レベルの簡易エディタで行いました。
が、現段階では、とても人様に提供できるレベルではありません(使いづらい&動作が安定しない等)。
ゲーム制作においては、大量のデータをいかに効率よく作成できるかが重要です。
エディタの良し悪しで工数は大きく変わってくるので、使いやすいエディタの準備が必要となります。
最後に
今回、ベジェ曲線によるゲームアプリの可能性について試してみましたが、アンカーポイントを動的に補正することで、体型の表現や、ちょっとしたアニメが実現できました。
また、ストローク描画の際、1画素単位でプログラム管理できるため、そこで色々な表現を加えることも可能そうです。
例えば、細密画のように主線に沿ってタッチをつけたり、テクスチャブラシのような表現が考えられます。
さらに、異なる素材に対して線と色味を統一できるので、素材の組み合わせの自由度が高そうです。
例えば、まったく違う印象のパーツ同士を組み合わせても、線と塗りの統一感でゴリ押しできそうな印象です。
結論として、ゲームアプリにおけるベジェ曲線描画は、十分に採用の余地がありそうです。
「少ない素材と手間」で表現のバリエーションがだせるよう、今後も色々と模索していこうと思います。