LoginSignup
3
2

【Unity】MediaPipeのポーズトラッキングでゲームを作ろう

Posted at

はじめに

※この記事は、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をダウンロードしてきて解凍してください。

Releases/v0.12.0

サンプルの実行

解凍したMediaPipeUnityPlugin-allをUnityで開きます。試しにサンプルを実行してみましょう。
Assets/MediaPipeUnity/Samples/Scenes/Start Sceneを開いて実行します。

実行画面右上のメニューボタンをクリックするとトラッキングの種類を選択できます。
スクリーンショット 2023-11-23 170803.png

ハンドトラッキング
MediaPipeHandTrackingTest.gif

ポーズトラッキング
MediaPipePoseTrackingTest2.gif

ゲーム制作

サンプルの動作確認ができたところで、ゲーム制作に移っていきます。今回使うのはポーズトラッキングなのでAssets/MediaPipeUnity/Samples/Scenes/Pose Tracking/Pose Trackingを開きます。

ゲームシーンの用意

まずはヒエラルキーウィンドウでシーン名を選択してシーンを別名で保存しましょう。名前は何でもいいですが、この記事ではPose Tracking Gameとして保存することにします。以降はこのシーンを編集していきます。

MediaPipeQiita1.gif

トラッキングポジションの取得

トラッキングポジションを取得して追従するオブジェクトを作っていきます。
ランドマークモデルはPose landmark detection guideから確認できます。

PointListAnnotationオブジェクトについているPointListAnnotationスクリプトを編集しましょう。
ここでは左右の手の人差し指のトラッキングポジションを取得しています(ランドマークモデルの19,20番が対応)。

スクリーンショット 2023-12-13 211327.png

PointListAnnotation.cs
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オブジェクトを作成してください。

MediaPipeQiita2.gif

分かりやすいようにインスペクターウィンドウでサイズを10倍にしておきます。

スクリーンショット 2023-12-14 123859.png

作成したオブジェクトをPointListAnnotationの枠にドラック&ドロップして関連付けましょう。

MediaPipeQiita3-2.gif

これでオブジェクトが手の位置に追従するようになりました。オブジェクトを剣や盾に変えれば構えたり振ったりと、もう遊べますね。

スクリーンショット 2023-12-16 103341.png

グリーンバックの変更

今のままだと背景が緑一色なので変えたいと思います。
背景画像を用意して以下のGIFのようにインポートからの設定を行ってください。
MediaPipeQiita4.gif

MaskAnnotationオブジェクトを選択し、インスペクターウィンドウのマスクテクスチャ欄に先ほどの背景画像をプロジェクトウィンドウからドラック&ドロップして関連付けしてください。

スクリーンショット 2023-12-16 105330.png

スクリーンショット 2023-12-16 105344.png

これで背景が自分の用意したものに変わりました。

スクリーンショット 2023-12-16 105515.png

ボーン、サブスクリーンの非表示

身体の前に表示されているボーンを非表示にしましょう。

一度実行してみると分かりますが、ボーンはPointListAnnotationオブジェクトとConnectionListAnnotationオブジェクトの子として生成されています。

以下のGIFを参考にそれぞれのオブジェクトに設定されているAnnotationPrefabRendererを非アクティブにしましょう。

MediaPipeQiita5.gif

これでボーンが非表示になったはずです。加えて、画面右下に表示されているサブスクリーンも非表示にしておきましょう。
おそらくボーン確認用に用意されているのだと思いますが、今は必要ないので非表示にします。

SubScreenオブジェクトとRoomオブジェクトを、それぞれインスペクターウィンドウで名前の横のチェックマークを外して非アクティブにしましょう。

スクリーンショット 2023-12-16 115339.png

スクリーンショット 2023-12-16 115937.png

これでボーンとサブスクリーンが非表示になりました。

スクリーンショット 2023-12-16 120304.png

ハンドボタンの実装

せっかくトラッキングができるのに、ボタンを押すためにキーボードの前に戻るのは味気ないので、トラッキングを使って押せるボタンを作りましょう。

まず、MainCanvasの子としてImageオブジェクトを作り、名前をHandButtonとします。
さらにその子としてImageオブジェクトを作り、名前をFillImageとしましょう。FillImageには適当な画像を設定して画像タイプを塗りつぶしに変えます。

MediaPipeQiita7.gif

次にHandButtonスクリプトを作ります。
プロジェクトウィンドウでスクリプトを新規作成して名前をHandButtonとしてください。

MediaPipeQiita6.gif

HandButtonスクリプトを開いて内容を以下のように書き換えて下さい。

HandButton.cs
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スクリプトをアタッチしてRightIndexSphereFillImageを設定します。

OnCustomEvent()マークを押してイベントを追加することで通常のUnityボタンと同じように使えます。

MediaPipeQiita8.gif

これでハンドボタンができました。右手を1秒間かざすことで登録されたイベントを実行します。

MediaPipeQiita9.gif

制作時の注意点

あとはそれぞれにオリジナルのゲームを作っていけばOKなのですが、いくつか注意点があります。

ビルドの注意点

ビルド時にはStart Sceneを含むようにしてください。これが無いとゲームが正常に動作しません。

シーン遷移の設定などは必要なく、もとからあるStart SceneをBuildSettingsで一番上に置けばOKです。

スクリーンショット 2023-12-16 121048.png

エディタ上でシーン遷移するときの注意点

ゲームを制作する上でシーンを複数用意することがあると思いますが、エディタ上でSceneManager.LoadScene("SceneName");などを使ってシーン遷移をすると、Unityが落ちます

これはおそらくStart Sceneからゲームを始めていないためMediaPipeの設定がされていないのが原因だと思います。なので、シーン遷移のテストがしたいときはまずStart Sceneから始めてください。

シーン遷移を行うのにStart Sceneから始めていなくて保存していないデータが飛ぶ...ということが何度もありました。

(おまけ)トラッキング方式の変更

ゲームを作っていると、他のトラッキング方式で作っていればよかった...。と思うことがあります。

ここではトラッキング方式を変更する方法を解説していきます。

トラッキング方式の変更方法

例として、ポーズトラッキングからホリスティック(ポーズに加えてハンド、フェイストラッキングもできる)形式に変えていきます。

まずAssets/MediaPipeUnity/Samples/Scenes/Holistic/Holisticを開きます。
ここでAnnotatableScreenオブジェクトとSolutionオブジェクトをコピーしましょう。

スクリーンショット 2023-12-16 152954.png

Pose Tracking Gameシーンに戻ってきて元からあったオブジェクトと置き換えます(削除して、コピーしてきた方を代わりに置く)。

Solutionオブジェクトの[トラッキング名] Solutionスクリプトを画像のように設定します。
設定するものはAnnotatableScreenAnnotationAreaAnnotationLayerの三つです。

  • AnnotatableScreen:先ほどコピーしてきたAnnotatableScreen
  • AnnotationAreaRoomの子オブジェクト
  • AnnotationLayerAnnotatableScreenの子オブジェクト

スクリーンショット 2023-11-20 121057.png

これで変更完了です。

おわりに

最後まで読んでいただきありがとうございました!

MediaPipeUnityPluginを使えばWebカメラだけでトラッキングを使ったゲームを作れます。いろいろと応用が利きそうで面白いので、この記事を読んでいいなと思った方はぜひ作ってみてください!

ではまたどこかで('ω')ノ

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2