ぐっちーの駄弁り部屋

個人的に制作しているものの進捗や日常について不定期投稿

Unityで作成したアプリでVRMファイルをランタイムに読み込む方法

皆さんこんにちはグッチーです。
今回はVRSNSで最近良く使われるようになってきたVRMファイルをWindowsのファイルダイアログから動的にロードする方法についてまとめていきたいと思います。
なんか面白いゲームを作りたいと考えていたときに「自分の持ってるVRMファイルで遊べるゲームとか面白そうじゃないか」と思ったのがことの発端で記事を探していた結果組み合わせてうまくいったので記事した感じです。

環境

今回実現した環境は以下の通りになります。

  • Windows10
  • Unity2019.3.13f1

Windows Formsを使用しているのでMacでは動作しないかと思いますのでご容赦ください。

手順

実装の手順としては大きく分けて2つのパートがあり

  1. ファイルダイアログを開いて使用したいファイルのパスを入手
  2. そのパスをもとにUniVRMを用いてVRMファイルを読み込み

となっています。記述するコード自体はそこまで難解なものではありませんでしたがDLLのコピーなど少し手間がかかる工程もあるのでそのあたりも細かく説明していきたいと思います。

その1:ファイルダイアログの表示

ファイルダイアログとはUnityやWebサービスなどを使っているときに「ファイルを開く」のような項目を選択すると表示されるウインドウのことです。UnityEditor内で動作させてファイルを検索したりするのであればファイルダイアログを表示させる必要はないのですがビルドしてexeファイルとしてから使用したいのであれば必要になります。
今回はビルドしたゲームからVRMファイルを読み込みたいと思っているのでこれを表示させる必要があります。
Windows向けに作られたアプリケーションにはOpenFileDialogを使ってこれを実現することができます。

OpenFileDialogを使ったコード

ではダイアログを開くためのコードを書いていきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Windows.Forms;

public class OpenFile : MonoBehaviour
{
    public string ItemLoad() {
        OpenFileDialog ofd = new OpenFileDialog();
        //ダイアログが開いたときに開くディレクトリのパス
        ofd.InitialDirectory = "E:/vroid/MyAvater";
        //選択できるファイル形式のフィルタ
        ofd.Filter = "VRM files(*.vrm)|*.vrm|All files(*.*)|*.*";
        //ダイアログ上部に表示するタイトル
        ofd.Title = "VRMファイルを選択してね";
        //ダイアログの表示
        ofd.ShowDialog();
        //選択したファイルのパス
        string filePath = ofd.FileName;
        return filePath;
    }
}

OpenFileDialogのインスタンスを作成して各種項目を設定してからダイアログを表示させます。OpenFIleDialogの設定項目などについてはMS社のリファレンスを参照してください

docs.microsoft.com

選択したファイルの情報はインスタンスが持ってくれているので参照します。
この関数をファイルの読み込みをしたいタイミングで呼び出してあげればいい状態です。(この実装ではこの関数でファイル自体を読み込んでいるわけではないです。)

DLLのコピー

上に書いたようなコードをそのまま使おうとすると「System.Windows.Formなんて見つからないが?」とUnityさんに怒られてしまいます。これを解消するためにDLLを用意します。
使用するDLLはUnityをインストールしたディレクトリの中にあります。詳しいパスとしてはE:\Unity_Editor\2019.3.13f1\Editor\Data\MonoBleedingEdge\lib\mono\unityjitにあるSystem.Windows.Form.dllをAsset\Pluginにコピーしてください。
11/29 追記
私の環境ではE:\Unity_Editorというディレクトリ以下にUnityhubからUnityをインストールしているので上のようなパスになっています。それぞれ自分の環境でUnityをインストールしたディレクトリ以下を探してDLLをこぴーするようにしてください
mono以下の他のディレクトリに同じ名前のファイルが存在していますがAPI Compatibility Levelを.NET 4.xとしている場合はこれを使えば良さそうです。

その2:VRMの読み込み

次にVRMファイルの読み込みを行います。VRMの読み込みにはUniVRMを使用しますのでダウンロードをしておいてください。

github.com

ダウンロードしたUniVRMをUnityへインポートしたらVRM読み込みのためのコードを書きます。

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using VRM;

public class ImportVRMFile : MonoBehaviour {
    public GameObject player;
    
    public async Task Import(string filePath) {
        //ファイルダイアログから取得したパスを使ってVRMファイルをByte配列に読み込み
        var bytes = File.ReadAllBytes(filePath);
        //VRMImporterContextがVRMとしての読み込み関連を行ってくれます
        var context = new VRMImporterContext();
        //GLB形式でJsonの読み込みとParse
        context.ParseGlb(bytes);
        //metaデータを取得(引数のtrueはサムネの読み込み用です)
        var meta = context.ReadMeta(true);
        
        //非同期で読み込み
        await context.LoadAsyncTask();
        //読み込み完了するとcontext.Rootに読み込んだVRMモデルのGameObjectが入っているので取得
        var root = context.Root;
        
        player = GameObject.FindWithTag("Player");
        //適当にプレイヤーオブジェクト以下に配置
        root.transform.SetParent(player.transform, false);
        //メッシュの描画
        context.ShowMeshes();
    }
}

それぞれコードの説明はコメントのとおりです。最後のShowMeshes()が実行されるまではメッシュが描画されないので描画する前に適当な場所に配置するなどして上げると良さそうです。

おまけ:デモ用のUI周りのコード

このあと載せるデモ動画で使っているUI周りのコードです。ボタンを押したらロードされるだけの機能です。

using UnityEngine;
using UnityEngine.UI;
using System;

public class UIManager : MonoBehaviour {
        
    public void OnLoadButton() {
        OpenFile of = new OpenFile();
        string filename = of.ItemLoad();
        ImportVRMFile importVrmFile = new ImportVRMFile();
        importVrmFile.Import(filename);
    }        
}

この記事書いてて「なんで俺はImportVRMFile側からOpenFileを呼んでないんだろう」と不思議で仕方がありませんでした(前日寝る前に思いついてテスト用に作ったコードを載せているのでご容赦いただきたい…)
実際にゲームに組み込むときはしっかりと呼び出しの関係を整理していきたいと思っています。

できたもの

結果的にできたものがこちらになります。

録画の都合上フルスクリーンでUnityEditor上で動かしているように見えなくもないですがビルドしたものを実行しています。

最後に

こんな感じでランタイムにVRMファイルを取得して来ることができました。あとはこれを組み込んだゲームを作って遊んだユーザーが使いたいVRMファイルで遊べるようにしてい着たいと思います。
もちろんファイルの読み込みは別のファイルでも可能ですしOpenFileDialog単体での読み込みもできるみたいですので興味のある方はやってみてください。
それでは今日はこのへんで( ー`дー´)キリッ

参考記事

qiita.com

qiita.com