5
3

More than 3 years have passed since last update.

NitendoSwich Joycon + FinalIKで簡単にVTuberを作ってみる

Posted at

本記事はVTuber Tech #1 Advent Calendar 2019の23日目の記事です。
Vtuberハッカソンでやったことを書こうと思っていたのですが、プレミアム大会がまだ終わってないので、別のことを書きます。

準備

準備するもの

  • Unity
  • UniVRM
  • FinalIK
  • JoyconLib
  • 任意のVRMファイル
  • NintendoSwitchのJoy-con

Unity

Qiitaを見るような人に説明は不要
今回使ったバージョンは2019.2.9f1

UniVRM

VRMをUnityで動かすためのあれこれ

FinalIK

Humanoidをいいかんじに制御するAsset

JoyconLib

UnityでJoyCon使うためのやつ

任意のVRMファイル

VroidHubでかわいいやつを見つけたのでそれを使います
守りたいこの笑顔

使ったモデル

必要なものをダウンロードする

Unityのインストールは飛ばします

UniVRM

ReleaseページからUnityPackageをダウンロードします。

FinalIK

FinalIK
UnityAssetStoreにあります。有料なので買ってください。

JoyconLib

1.JoycobLibのリポジトリに行きます
2.「Clone or download」ボタンを押して「Download ZIP」を選択します
3.ダウンロードした「JoyconLib-master.zip」を展開しておきます
4.必要な別のファイルを持ってくるためここに行きます
5.「Clone or download」ボタンを押して「Download ZIP」を選択します
6.ダウンロードした「Unity-Wiimote-master.zip」を展開しておきます

使うAsset、オブジェクトを配置する

VRM,UniVRM

1.UniVRMのUnityPackageをUnityEditor上に放り込みます。
2.VRMファイルをUnityのプロジェクトに追加すると、勝手にPrefabファイルが生成されます。

FinalIK

FinalIKをAssetStoreからImportしておきます

JoyconLib

「JoyconLib-master/Assets」フォルダ内の「JoyconLib_scripts」フォルダをUnityプロジェクトに追加します
「Unity-Wiimote-master/Assets/Wiimote/Plugins/win64フォルダ内の「hidapi.dll」を Unity プロジェクトに追加します

JoyCon

JoyconをBluetoothでパソコンにつないでおきます

FinalIKで頭を動かす

1.VRMモデルのPrefabをSceneに追加します。
2.Create→3DObject→CubeでCubeをSceneに追加します
3.頭の位置にCubeを持っていきます
Cube
4.モデルのInspectorからAddComponent→VRIKをアタッチ
5.さっき作ったCubeをVRIKのSolver→Spine→HeadTargetに投げ入れます
VRIK
6.CubeのMeshRendererのチェックを外しておきます
7.実行して、Scene ViewでCubeの角度を変えてみると頭がそれに追従して動きます!
首が動くよ

JoyconRを使って頭を制御する

1.Create→CreateEmptyでJoycon制御用のObjectを作ります。
2.JoyConLib_scriptsの中のJoyConManagerをアタッチします。
3.AddComponent→NewScriptから新しいScript「JoyConController」を作ります。

JoyConController.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class JoyConController : MonoBehaviour
{
    [SerializeField]
    private GameObject targetObject;

    private Joycon m_joyconR;

    void Start()
    {
        SetControllers();
    }

    void Update()
    {
        if (m_joyconR == null) return;

        rightProcess();
    }

    private void rightProcess()
    {
        Vector3 gyro = m_joyconR.GetGyro();
        Vector3 angle = targetObject.transform.rotation.eulerAngles + new Vector3(-gyro.y, gyro.z, -gyro.x) * 0.5f;
        targetObject.transform.rotation = Quaternion.Euler(angle);
    }

    private void SetControllers()
    {
        List<Joycon> joycons = JoyconManager.Instance.j;
        if (joycons == null || joycons.Count <= 0) return;
        m_joyconR = joycons.Find(c => !c.isLeft);
    }
}

JoyConControllerのTargetObjectにさっき作ったCubeを入れます。

頭にJoyconRをどうにか固定して、動かしてみましょう

JoyconLを使ってズレを補正する

Joyconのgyroで角度をとると、ずれてくるので、手動でまっすぐにできるようにしておきます

JoyConController.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class JoyConController : MonoBehaviour
{
    [SerializeField]
    private GameObject targetObject;

    //staticとして定義しておく
    static Quaternion ZeroQuaternion = Quaternion.Euler(Vector3.zero);

    private Joycon m_joyconL;
    private Joycon m_joyconR;

    private bool isLerpControl = false;
    private float lerpTime;
    private Quaternion beforeQuaternion;


    void Start()
    {
        SetControllers();
    }

    void Update()
    {
        if (m_joyconL == null || m_joyconR == null) return;

        //JoyconLの処理は常に動かしておく
        leftProcess();

        //Lerpコントロール中はJoyconRでの制御はしない
        if (isLerpControl)
        {
            lerpProcess();
        }
        else
        {
            rightProcess();
        }
    }

    private void lerpProcess()
    {
        lerpTime += Time.deltaTime * 2.0f;

        if(lerpTime >= 1.0f)
        {
            lerpTime = 1.0f;
            isLerpControl = false;
        }

        targetObject.transform.rotation = Quaternion.Lerp(beforeQuaternion, ZeroQuaternion, lerpTime);
    }

    private void leftProcess()
    {
        if (m_joyconL.GetButtonDown(Joycon.Button.SHOULDER_1)) //L1が押されたとき
        {
            beforeQuaternion = targetObject.transform.rotation;
            isLerpControl = true;
            lerpTime = 0.0f;
        }
    }

    private void rightProcess()
    {
        //省略
    }

    private void SetControllers()
    {
        List<Joycon> joycons = JoyconManager.Instance.j;
        if (joycons == null || joycons.Count <= 0) return;
        m_joyconL = joycons.Find(c => c.isLeft);
        m_joyconR = joycons.Find(c => !c.isLeft);
    }
}

完成

完成したものがこちら

最終的に完成したスクリプト

JoyConController.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class JoyConController : MonoBehaviour
{
    [SerializeField]
    private GameObject targetObject;

    static Quaternion ZeroQuaternion = Quaternion.Euler(Vector3.zero);

    private Joycon m_joyconL;
    private Joycon m_joyconR;

    private bool isLerpControl = false;
    private float lerpTime;
    private Quaternion beforeQuaternion;


    void Start()
    {
        SetControllers();
    }

    void Update()
    {
        if (m_joyconL == null || m_joyconR == null) return;

        leftProcess();

        if (isLerpControl)
        {
            lerpProcess();
        }
        else
        {
            rightProcess();
        }
    }

    private void lerpProcess()
    {
        lerpTime += Time.deltaTime * 2.0f;

        if(lerpTime >= 1.0f)
        {
            lerpTime = 1.0f;
            isLerpControl = false;
        }

        targetObject.transform.rotation = Quaternion.Lerp(beforeQuaternion, ZeroQuaternion, lerpTime);
    }

    private void leftProcess()
    {
        if (m_joyconL.GetButtonDown(Joycon.Button.SHOULDER_1))
        {
            beforeQuaternion = targetObject.transform.rotation;
            isLerpControl = true;
            lerpTime = 0.0f;
        }
    }

    private void rightProcess()
    {
        Vector3 gyro = m_joyconR.GetGyro();

        Vector3 angle = targetObject.transform.rotation.eulerAngles + new Vector3(-gyro.y, gyro.z, -gyro.x) * 0.5f;

        targetObject.transform.rotation = Quaternion.Euler(angle);
    }

    private void SetControllers()
    {
        List<Joycon> joycons = JoyconManager.Instance.j;
        if (joycons == null || joycons.Count <= 0) return;
        m_joyconL = joycons.Find(c => c.isLeft);
        m_joyconR = joycons.Find(c => !c.isLeft);
    }
}

おわりに

適当に作った割にはいいものができた。嬉しい。

5
3
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
5
3