🤪

[Unity × MediaPipe] 「変顔で操作する格ゲー」の開発記録

に公開

この度、「変顔で操作する格ゲー」というコンセプトの "Strange Fighter" というゲームを作成したので、その開発記録を記事にしました‼️

この作品は、「オモコロ杯 2025」に提出するために開発しました。(結果は、落選でした、、、😞)
https://omocoro.jp/cup-2025/

動作の様子

最終的に完成したゲームの動作の様子です。キャラクターに重ねて表示されている顔のオブジェクトの表情の動きに応じて、キャラクターがアクションを行っているのがわかるかと思います。😎

https://youtu.be/HU3weG2afzI?si=PTxYDoHu95-G6h23

どんなものを作るか

コンセプト:変顔で操作する格ゲー

"変顔×格ゲー" という本来交わることのない組み合わせに面白みを見出した。格ゲーでは、メリハリのある入力が必要とされ、特にコマンド操作においては定められた連続入力が要される。そこに変顔という要素を導入することによって、変顔から変顔への切り替えを素早くやっている様子がシュールで面白いのはないかと考えた。

使用技術

  • 開発環境:Unity
  • ゲームアセット:UFE2(格闘ゲーム用)
  • 表情検出:MediaPipe
  • 通信:PUN2(UFE2 内で使用)、WebRTC(表情の共有用)

開発

格ゲー部分:UFE2

基本的な格ゲー部分について、UFE2 というアセットを利用した。このアセットでは、基本的な格ゲーのシステムから、キャラクターの 3D モデル、オンライン対戦のコードまで用意されているため、このアセットを購入すれば、格ゲー部分についてはほぼ完成したものが手に入る。UFE2 には 6 つのプランがあるが、今回はオンライン対戦ができるように "Network Support" を含む "Standard" プランを購入した。


出典:Features and Prices | UFE

ゲーム制作に関してほとんど初心者であったため、最初は「格ゲー部分も1から作れるのではないか」と考えていたが、ネットコード(オンラインで複数プレイヤー間のゲーム状態を同期し、一貫性を保つための通信技術)の実装は、個人開発で対応できるレベルを超えており(参考:↓の記事)、もし有料のネットコードライブラリを導入するのであれば、ネットコードを含む格闘ゲーム用アセットを活用した方が合理的だという結論に至った。結果として、格ゲーアセットを使用した上でも開発に苦戦したため、良い選択であったであろう。

https://soysoftware.sakura.ne.jp/archives/2238

表情入力コントローラーの実装

表情点(ランドマーク)検出:MediaPipe

表情点検出ライブラリは多く存在するが、MediaPipe を利用した。決め手は、精度の高さとランドマークの数の多さである。(より多様な表情を認識できるため)

↓の画像を見ると、ランドマークの数の多さがわかるだろう。(全部で468個)一つ一つのランドマークに数字が割り振られており、それらを元に表情の検出を行う。


MediaPipe のランドマーク(出典:Models | Face landmark detection guide

MediaPipe の使い方については、以下を参考にした。
https://qiita.com/river_9167/items/3af6eb57b69acbb543b9

上記記事で紹介されている、以下のプラグインを実装に使用した。(Unity で MediaPipe を使えるようにしてくれている)
https://github.com/homuler/MediaPipeUnityPlugin

表情判定→入力の送信

具体的な表情の判定の流れは、以下の通りである。3 の正規化は、カメラとの距離による誤差をなくすための処置である。(同じ表情であったとしてもランドマーク間の距離の値は、カメラに近づいた時に大きくなり、離れた時に小さくなってしまう。顔の大きさ(頭頂と顎先の距離)を元に正規化することで、どの距離でも同じ基準で判定できるようにしている。)

  1. MediaPipe で顔のランドマークを取得
  2. ランドマーク間の距離・角度を計算
  3. 顔のサイズに合わせて正規化
  4. 各特徴量と閾値を比較
  5. 入力イベントの送信

具体的な、検出する表情特徴と対応する入力は、以下の通りである。極力、直感的に操作ができるように工夫している。主に、顔の下半分で移動、上半分で攻撃を行う。なお、これらはコマンド操作にも対応しており、連続的に表情入力を行うことで、コマンド技を使うことができる。

表情特徴 検出方法の概要 入力として使う動作
口の位置(左右移動) 口の位置の左右のずれを検出し、口を寄せた方向に移動 左右移動
鼻と上唇の距離 鼻先と上唇の距離を計測し、距離が短いとジャンプ動作と判断 ジャンプ
下唇とあごの距離 下唇とあご先の距離を計測し、距離が短いとしゃがみ動作と判断 しゃがみ
目の開き具合 目の上下の開きを計測し、十分開いていると攻撃動作と判断 パンチ
眉間の寄せ具合 眉と鼻横の距離を計測し、距離が短いとしかめ面として攻撃動作と判断 キック

顔の個人差への対応

顔のランドマークの位置には個人差があり、それらを考慮しないと判定が難しくなる。例えば、目の開きが大きい人と小さい人で同じ閾値を使用してしまうと、目の開きが大きい人は通常時でも目が開いている判定になり常にパンチし続けてしまう。逆に、目の開きが小さい人は頑張って目を開いても入力判定にならずパンチできない、ということが考えられる。そこで、そのような顔の個人差に対応する必要がある。

今回は簡易的に、以下のような方法で個人差対応を行った。(完璧に個人差対応をするのは難しく、よりクオリティを高めるには更なる工夫が必要であろう。)

  1. あらかじめプレイヤーの顔の表情特徴量を計測する。
  2. 計測した値を元に、閾値を設定する。
  3. 設定された閾値を元に表情判定を行う。

1 の計測では瞬きなどを考慮して、数秒間計測した値の平均値を採用する。2 では、計測された通常時の値×固定倍率を閾値とする。(通常時の1.2倍目を開いていたらパンチ入力をする、みたいなイメージ)

変顔メッシュの作成と表示

MediaPipe で取得したランドマーク座標のデータをもとに、メッシュの描画を行う。メッシュの描画方法としては、以下のサイトを参考にした。三角形のメッシュの作り方を説明しているが、やることはその応用で、顔のメッシュを三角形を組み合わせて表示する。この際、どのようにメッシュを作成するかを決めるために頂点座標の結ぶ順番を表すデータが必要になるが、そのデータは MediaPipe に予め含まれいてるため、それを利用した。

https://shibuya24.info/entry/unity-mesh-dynamic-basis

(初めは、以下のサイトのように MediaPipe のプロジェクトに含まれている顔のオブジェクトを使って表示を行おうとしていたが、表示がうまくいかず断念、、、)

https://www.northdetail.co.jp/blog/1586/

変顔メッシュの表示位置は、キャラクターの頭部のオブジェクトのスクリーン座標で指定した。

通信部分(変顔の共有)

格ゲーのネットコードは、UFE2 で既に実装されているが、今回はそれに加えてプレイヤー同士の顔の動きを送受信する必要がある。その通信部分には WebRTC を用いる。

UFE2 ではオンライン対戦用のネットコードとして PUN2(Photon Unity Networking 2) を使用しており、接続やゲーム状態の同期を行っている。ここに WebRTC を組み込みたいので、P2P通信を可能にするために PUN2 に Signaling を導入した。

(↓ 用語説明)

  • WebRTC:端末同士で直接映像や音声を送受信できる P2P 通信技術
  • PUN2:UFE2 が利用しているネットワークアセット。接続とゲーム同期を提供。ここに Signaling 機能を統合することで、WebRTC を介した映像通信と同期が可能になる
  • Signaling:P2P 通信に必要な接続情報(IPアドレスやポート、通信条件など)を交換する仕組み

PUN2 のコードを修正する上では、以下を参考にした。
https://zenn.dev/o8que/books/bdcb9af27bdd7d

最後に

今回、「変顔で操作する格ゲー」というコンセプトでゲームを作成しましたが、感想としては、思っていたよりもめちゃくちゃ大変でした、、。😓

ゲーム開発自体ほぼ初心者であったということもあり、Unity の使い方を理解するところから始まり、UFE2 に関しては参考になる記事などもほとんどないため、コードを見ながら手探りでいろいろ試していくしかありませんでした。また、UFE2 に MediaPipe を導入するのもそれなりに苦戦しました。(プレイ開始とともにカメラを立ち上げて MediaPipe を起動する流れ、など)

期限も定められていたため正直消化不良な部分も多くありますが、とりあえずコンセプトの最低限の部分は実装し切れたので、よかったかなと思います。

(もしまた、この続きを実装することになったら、変顔がより強調されるような演出などが入れられたら面白いだろうなと思っています。🧐)

Discussion