はじめに
※この記事は、JOKEN Advent Calender 2023の参加記事です。
こんにちは! 株式会社トルクスでエンジニアをしているオガワです。
松江高専情報科学研究部のアドベントカレンダーですが、OB参加もOKとのことで参加させてもらいます。
今回は、とあるイベントの出展用にMediaPipeのUnityプラグインを使ってゲームを作ったので、そのプラグインをゲーム制作に使う方法を解説していきます。
MediaPipeとは
Googleが提供しているオープンソースのライブラリです。画像・動画から顔、手、姿勢などの識別を行えます。
作ったゲーム
環境
Windows 11 Home
Unity 2021.3.18f1
MediaPipeUnityPlugin v0.12.0
MediaPipeUnityPluginの導入
MediaPipeをゲームエンジンのUnityで使えるようにしてくれるプラグインがGitHubで公開されているので、ありがたく使わせてもらいます。
このプロジェクトをダウンロードしてそのまま使いたいところですが、Unityで開くとパッケージが足りていない旨のエラーが大量に出てきます。
どうやら必要なパッケージを自分で導入する必要があるみたいですが、リリースにてパッケージを導入済みのUnityプロジェクトが配布されているのでそちらを使います。
以下のリンクからMediaPipeUnityPlugin-all.zip
をダウンロードしてきて解凍してください。
サンプルの実行
解凍したMediaPipeUnityPlugin-all
をUnityで開きます。試しにサンプルを実行してみましょう。
Assets/MediaPipeUnity/Samples/Scenes/Start Scene
を開いて実行します。
実行画面右上のメニューボタンをクリックするとトラッキングの種類を選択できます。
ゲーム制作
サンプルの動作確認ができたところで、ゲーム制作に移っていきます。今回使うのはポーズトラッキングなのでAssets/MediaPipeUnity/Samples/Scenes/Pose Tracking/Pose Tracking
を開きます。
ゲームシーンの用意
まずはヒエラルキーウィンドウでシーン名を選択してシーンを別名で保存しましょう。名前は何でもいいですが、この記事ではPose Tracking Game
として保存することにします。以降はこのシーンを編集していきます。
トラッキングポジションの取得
トラッキングポジションを取得して追従するオブジェクトを作っていきます。
ランドマークモデルはPose landmark detection guideから確認できます。
PointListAnnotation
オブジェクトについているPointListAnnotation
スクリプトを編集しましょう。
ここでは左右の手の人差し指のトラッキングポジションを取得しています(ランドマークモデルの19,20番が対応)。
public class PointListAnnotation : ListAnnotation<PointAnnotation>
{
//追加
public GameObject RightIndex;
public GameObject LeftIndex;
//追加ここまで
[SerializeField] private Color _color = Color.green;
[SerializeField] private float _radius = 15.0f;
//...
public void Draw(IList<NormalizedLandmark> targets, bool visualizeZ = true)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, visualizeZ); }
});
}
//追加
if (RightIndex != null && LeftIndex != null)
{
RightIndex.transform.position = children[20].transform.position;
LeftIndex.transform.position = children[19].transform.position;
}
//追加ここまで
}
//...
}
スクリプトを編集できたらトラッキングポジションに追従するオブジェクトを用意します。以下のGIFのようにしてRightIndexSphere
オブジェクトとLeftIndexSphere
オブジェクトを作成してください。
分かりやすいようにインスペクターウィンドウでサイズを10倍にしておきます。
作成したオブジェクトをPointListAnnotation
の枠にドラック&ドロップして関連付けましょう。
これでオブジェクトが手の位置に追従するようになりました。オブジェクトを剣や盾に変えれば構えたり振ったりと、もう遊べますね。
グリーンバックの変更
今のままだと背景が緑一色なので変えたいと思います。
背景画像を用意して以下のGIFのようにインポートからの設定を行ってください。
MaskAnnotation
オブジェクトを選択し、インスペクターウィンドウのマスクテクスチャ
欄に先ほどの背景画像をプロジェクトウィンドウからドラック&ドロップして関連付けしてください。
これで背景が自分の用意したものに変わりました。
ボーン、サブスクリーンの非表示
身体の前に表示されているボーンを非表示にしましょう。
一度実行してみると分かりますが、ボーンはPointListAnnotation
オブジェクトとConnectionListAnnotation
オブジェクトの子として生成されています。
以下のGIFを参考にそれぞれのオブジェクトに設定されているAnnotationPrefab
のRenderer
を非アクティブにしましょう。
これでボーンが非表示になったはずです。加えて、画面右下に表示されているサブスクリーンも非表示にしておきましょう。
おそらくボーン確認用に用意されているのだと思いますが、今は必要ないので非表示にします。
SubScreen
オブジェクトとRoom
オブジェクトを、それぞれインスペクターウィンドウで名前の横のチェックマークを外して非アクティブにしましょう。
これでボーンとサブスクリーンが非表示になりました。
ハンドボタンの実装
せっかくトラッキングができるのに、ボタンを押すためにキーボードの前に戻るのは味気ないので、トラッキングを使って押せるボタンを作りましょう。
まず、MainCanvas
の子としてImage
オブジェクトを作り、名前をHandButton
とします。
さらにその子としてImage
オブジェクトを作り、名前をFillImage
としましょう。FillImage
には適当な画像を設定して画像タイプを塗りつぶしに変えます。
次にHandButton
スクリプトを作ります。
プロジェクトウィンドウでスクリプトを新規作成して名前をHandButton
としてください。
HandButton
スクリプトを開いて内容を以下のように書き換えて下さい。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class HandButton : MonoBehaviour
{
// カスタムイベントを定義
[System.Serializable]
public class CustomEvent : UnityEvent { }
// カスタムイベントを公開
public CustomEvent onCustomEvent;
public Transform handObject; // ハンドオブジェクトのTransformコンポーネントを設定
public GameObject FillImage;
private float timer = 0f;
public float MAX_timer = 1f;
// Update is called once per frame
void Update()
{
// ハンドオブジェクトが目標地点にあるかを確認
bool objectAtTarget = Vector3.Distance(handObject.position, transform.position) < 1f;
// ハンドオブジェクトが目標地点にあって、かつMAX_timer秒経過したらカスタムイベントを実行
if (objectAtTarget)
{
timer += Time.deltaTime;
if (timer >= MAX_timer)
{
timer = 0f;
// カスタムイベントを実行
if (onCustomEvent != null)
{
onCustomEvent.Invoke();
}
}
}
else
{
// オブジェクトが目標地点から移動した場合、タイマーをリセット
timer = 0f;
}
FillImage.GetComponent<Image>().fillAmount = timer / MAX_timer;
}
}
HandButton
オブジェクトにHandButton
スクリプトをアタッチしてRightIndexSphere
とFillImage
を設定します。
OnCustomEvent()
の+
マークを押してイベントを追加することで通常のUnityボタンと同じように使えます。
これでハンドボタンができました。右手を1秒間かざすことで登録されたイベントを実行します。
制作時の注意点
あとはそれぞれにオリジナルのゲームを作っていけばOKなのですが、いくつか注意点があります。
ビルドの注意点
ビルド時にはStart Scene
を含むようにしてください。これが無いとゲームが正常に動作しません。
シーン遷移の設定などは必要なく、もとからあるStart Scene
をBuildSettingsで一番上に置けばOKです。
エディタ上でシーン遷移するときの注意点
ゲームを制作する上でシーンを複数用意することがあると思いますが、エディタ上でSceneManager.LoadScene("SceneName");
などを使ってシーン遷移をすると、Unityが落ちます。
これはおそらくStart Scene
からゲームを始めていないためMediaPipeの設定がされていないのが原因だと思います。なので、シーン遷移のテストがしたいときはまずStart Scene
から始めてください。
シーン遷移を行うのにStart Scene
から始めていなくて保存していないデータが飛ぶ...ということが何度もありました。
(おまけ)トラッキング方式の変更
ゲームを作っていると、他のトラッキング方式で作っていればよかった...。と思うことがあります。
ここではトラッキング方式を変更する方法を解説していきます。
トラッキング方式の変更方法
例として、ポーズトラッキングからホリスティック(ポーズに加えてハンド、フェイストラッキングもできる)形式に変えていきます。
まずAssets/MediaPipeUnity/Samples/Scenes/Holistic/Holistic
を開きます。
ここでAnnotatableScreen
オブジェクトとSolution
オブジェクトをコピーしましょう。
Pose Tracking Game
シーンに戻ってきて元からあったオブジェクトと置き換えます(削除して、コピーしてきた方を代わりに置く)。
Solution
オブジェクトの[トラッキング名] Solution
スクリプトを画像のように設定します。
設定するものはAnnotatableScreen
、AnnotationArea
、AnnotationLayer
の三つです。
-
AnnotatableScreen
:先ほどコピーしてきたAnnotatableScreen
-
AnnotationArea
:Room
の子オブジェクト -
AnnotationLayer
:AnnotatableScreen
の子オブジェクト
これで変更完了です。
おわりに
最後まで読んでいただきありがとうございました!
MediaPipeUnityPluginを使えばWebカメラだけでトラッキングを使ったゲームを作れます。いろいろと応用が利きそうで面白いので、この記事を読んでいいなと思った方はぜひ作ってみてください!
ではまたどこかで('ω')ノ