Help us understand the problem. What is going on with this article?

ant+ 心拍計を用いて、Unity上でVRMの表情やアニメーションを制御する。

これがやりたい

心拍.png

※追記※心拍ゾーンとは何ですか? | Garmin サポートセンター

誰しも心拍によって顔が赤くなったり元に戻ったりしたい筈。したいですね?
(心拍数センサーの入力によって、VRMモデルのブレンドシェイプの数値を変動させたい)

この記事は知識のない者が感覚でばーっとやって、びゃーとした感想を書いています。
分からないなりに頑張って書いたから誉めてくださいお願いします。間違ってたら指摘をお願い致します。


この記事を書こうと思ったきっかけ

私はサイクリストバーチャルyoutuber仁志乃ちゃんの監督です。

https://twitter.com/Nishino_cyclist
普段はかわいいカンガルー🦘の女の子にYoutubeで雑談させたり自転車させたりしています。
やっぱスポーツ頑張ってたら汗かいたり暑くて顔が赤くなったりするよね。
スポーツ自転車乗ってる人なら結構持ってるant+ の心拍計と連動して
VRMモデルの顔色を変えるみたいなことをやりたくなりました。


用意するもの


環境

Uniy 2018.4.10f1

※私の環境なので他の環境でも出来ると思います。

Univrm v0.53.0

今回はあくまでも心拍とVRMに関して書くのでアバターとして動かしたい場合は別途バーチャルモーションキャプチャーなどを使用してください。無料です!

AdvancedAnt+

有料アセットです。$60です!プログラミングわかんないのにこれ使ったら秒ですよ最高。
このアセットでant+のデータを取得できるようになります。今回心拍数だけですが、ケイデンス(自転車のクランクの回転数)やスピード、パワー(ワット数)なんかも取れます。

赤面表情差分があるVRMモデル

今回は冒頭画像のようにゾーン2~4では頬付近だけをだんだん赤らめ、4~5では顔のメッシュ全体を赤くしていきたいので、
半透明のテクスチャを乗せたメッシュを仁志乃ちゃんに追加しました。
image.png


AdvancedAnt+を使うには開発者登録が必要

AdvancedAnt+っていうUnityのアセットについて
AdvancedAnt+アセットの導入について書かれています。

サイトにて開発者登録が必要です(無料)

とありますので、登録します。登録時のことに関して補足すると、サイトにアクセスしたら、右上のregister now(赤丸)をクリックしてCREATE A USER ACCOUNTに行き、登録を行いましょう。
image.png

ACTIVATION IS NOT AUTOMATIC AND WILL TAKE UP TO 1 BUSINESS DAY, NOT INCLUDING STATUTORY HOLIDAYS

登録ページにこう書かれてある通り、自動返信で即返事が来るわけではないので注意。
私は土日を挟んで月曜には登録完了メールが届きました。
早速ログインして、
https://www.thisisant.com/developer/ant-plus/ant-plus-basics/network-keys
のページにアクセスし、下段のこの部分、赤丸のところのネットワークキーを控えます。
(後ほど使用します。)

image.png


別の手段で取得?

私はアセットを使いましたが、こちらの記事では御自身で心拍を取れるようにされているみたいです。
ANT+で脈拍数を取得してUnityで作ったVR空間に表示させる


機材

GARMIN(ガーミン) ハートレートセンサー HRM-Dual

私と仁志乃は胸に巻くタイプの心拍計を使用しています。
上記はant+とBluetooth両方使えるタイプですが、必ずしもこれでなくても大丈夫。
リストウォッチタイプの心拍転送モードでもできるはず
GARMINのvívosmartという腕時計タイプの活動量計でも検証しましたが、使えました。(心拍転送モードにしてね)

USB ANTスティック mini

パソコンにぶっさしてant+のデータを受け取る方。


UnityにAdvancedAnt+とUnivrmを入れる

二つともUnitypackageファイルをインポートするだけでOK


ネットワークキーを有効にする。

AntManager
Assets/AdvancedAnt/Plugins/Ant/AntManager.csを開き、

42行目に先ほど控えたネットワークキーをコピペします。

AntManager.cs
readonly byte[] NETWORK_KEY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };// COPY THE CORRECT NETWORK KEY HERE

このへんのところAssets/AdvancedAnt/PC_MAC_readme.txt にも書かれています。


demowithPrefabsシーンを試す

それではUSB ANTスティックをPCに刺して認識させてましょう。
そして心拍計を体に装着します。機材の準備が整ったら、Unityに戻って
Assets/AdvancedAnt/demowithPrefabs/demowithPrefabsを開きます
hierarchyのHeartRateDisplayをアクティブにしてからシーンを再生。
心拍の情報がUnity届いていることを確認しましょう。届いているなら再生前は
Enable the gameObjects you would like to use
と表示されている部分が変化し、心拍数(HR = XX)が表示されます。
HeartRateDisplay

うまくいったところで、このシーンを別名保存してVRMを読み込みましょう。


VRMのblendshape

UniVRMのブレンドシェイプの設定を見てください。
[オプション]表情を追加する という項目がありますのでちょっと読んでみてください。メッシュだけではなく
テクスチャを変化させられるのが分かります。
_colorのTarget Valueをピンクに変えましょう。これで心拍ゾーン4~5の、顔全体が真っ赤にする方法が何となくわかるかと思います。(汗は思い付きで入れましたがこれは普通のブレンドシェイプ)
image.png

心拍ゾーン3~4では頬のテクスチャをNEUTRAL表情の時は_colorのアルファ0にしておき、赤ら顔表情の時にアルファ100になるようにしています
//image.png
コメント 2020-03-16 214929.png


顔色を操作するスクリプトをVRMにアタッチする

以下のスクリプトをVRMにアタッチしてください。先程作ったオプションの表情のBlend Shape nameを直接指定してください。

kaoiro.cs
using UnityEngine;
using System.Collections;
using ANT_Managed_Library;
using VRM;

public class kaoiro: MonoBehaviour
{

    GameObject HeartRateDisplay; //GameobjerctとしてのFitnessEquipmentDisplayが入る変数
    HeartRateDisplay script; //FitnessEquipmentDisplayScriptが入る変数
    BlendShapePreset currentFace;

    private VRMBlendShapeProxy proxy;
    private CharacterController characterController;
    private Vector3 velocity;
    public float maxheart = 200f;//最大心拍数
    public float minheart = 60f;//安静時心拍数


    [SerializeField]
    void Start()
    {

        HeartRateDisplay = GameObject.Find("HeartRateDisplay"); //FitnessEquipmentDisplay をオブジェクトの名前から取得して変数に格納する
        script = HeartRateDisplay.GetComponent<HeartRateDisplay>(); //FitnessEquipmentDisplay の中にあるFitnessEquipmentDisplay を取得して変数に格納する
        var proxy = GetComponent<VRMBlendShapeProxy>();

        // Blend Shape Nameを指定して適用量をセット
        proxy.SetValue("赤ら顔", 0f);

    }

    void Update()
    {
        if (proxy == null)
        {
            proxy = GetComponent<VRMBlendShapeProxy>();
        }
        else
        {
            float faceCollor1 =  maxheart - minheart;//計算の順序わからなくて無理やり
            float faceCollor = 1f / faceCollor1;//やりたいのは平常時心拍と最大時心拍の間を0~1で変化させたかった。
            float heartRate = script.heartRate;//scriptで持ってきた心拍数を代入


            if (heartRate < maxheart * 0.6f)//心拍ゾーン1(~60%)
            {
                proxy.SetValue("赤ら顔", faceCollor *heartRate *0.5f);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate * 0f);

            }
            else if (heartRate < maxheart * 0.8f)//心拍ゾーン2~3(~80%)
            {
                proxy.SetValue("赤ら顔", faceCollor * heartRate);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate * 0.5f);
            }
            else if (heartRate < maxheart * 0.9f)//心拍ゾーン4~5(90%~)
            {
                 proxy.SetValue("赤ら顔", faceCollor * heartRate);
                proxy.SetValue("顔真っ赤", faceCollor * heartRate *1.1f);
            }

        }
    }
}

そしたらもう表情変わりますよ!!ぴゅんですよ!!やったね
(画像でセンサーが取得出来てないのはわざとです。即座に自分の心拍を上昇できなかったのでHeartRateDisplayを直接数字いじりました。)
image.png


ハートの鼓動アニメーションを連動させたい

書き忘れました。
冒頭のツイッターの動画右上に鼓動するハートのアニメがあります。これは取得している心拍とシンクロしています。
image.png

これを実装しましょう。
[Unity] Canvasに Image 画像を配置しScriptで変更
【Unity】uGUIでスプライトアニメーションするには
こちらの記事がとても参考になりました。

1秒間に1回ドクンとするAnimationClipを作成し、ドキドキさせたいGUIのimageにアタッチします。
image.png

次に、下のスクリプトをアタッチします。

kokoro_Move_speed.cs
using UnityEngine;
using System.Collections;
using ANT_Managed_Library;
using UnityEngine.UI;

public class kokoro_Move_speed : MonoBehaviour
{
    GameObject HeartRateDisplay; //GameobjerctとしてのFitnessEquipmentDisplayが入る変数
    HeartRateDisplay script; //FitnessEquipmentDisplayScriptが入る変数

    private CharacterController characterController;
    private Vector3 velocity;
    [SerializeField]
    private float speed;
    private Animator animator;



    void Start()
    {


        HeartRateDisplay = GameObject.Find("HeartRateDisplay"); //FitnessEquipmentDisplay をオブジェクトの名前から取得して変数に格納する
            script = HeartRateDisplay.GetComponent<HeartRateDisplay>(); //FitnessEquipmentDisplay の中にあるFitnessEquipmentDisplay を取得して変数に格納する


        animator = GetComponent<Animator>();
        animator.speed = 1.0f; //1秒に1回鼓動するアニメーションの場合

        //heartRate数値に合わせて段々とimageの色を赤く変更する準備
        //GetComponent<Image>().color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
    }

    void Update()
    {
        float heartRate = script.heartRate;//scriptで持ってきた心拍数を代入
                                           //Debug.Log("心拍数は" + heartRate);

        characterController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();
        animator.speed = 1.0f / 60.0f * heartRate;
        //image色変更 
        //GetComponent<Image>().color = new Color(1.0f , 1-(0.0025f * heartRate), 1 - (0.0025f * heartRate), 1.0f);



    }
}

シーン再生するともう完成です。


最後に

だれか既にやってるはずと思って探したらあんまり見当たらなかったのでメモです。
リングフィットアドベンチャーやビートセイバーを顔真っ赤にしながらやっていただければ嬉しいです。
もっとこうしたらいいよとかあったら是非指摘たりしてください。使わせてください。

以上です。あ、チャンネル登録してくれると喜んじゃいます
仁志乃チャンネル


Nishino_cyclist
サイクリストVtuber仁志乃ちゃんの監督。 普段は全然違う仕事してます。リモートワーク中 ノンアルビールとセール品のお刺身で優勝したりしています。 仁志乃ちゃんには内緒だよ。 ちょっとおっちょこちょいな女の子で 17歳です(おいおい) 技術的なことを忘れて調べ直す…を何度もやらかしてるので、 同じことで悩んでる人いたら共有したいです。チャンネル登録よろしくです
https://www.youtube.com/channel/UCnxwGgXbrAyjEogAjay-bog
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした