12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Unity】ノベルゲーム用アセットFungusをVRで使えるようにしてみた

Last updated at Posted at 2020-02-02

【Unity】ノベルゲーム用アセットFungusをVRで使えるようにしてみた

Unityでのノベルゲーム製作で有用なアセットに、Fungusがあります。
私はOculus用のVRゲームを開発しており、その中で会話ウィンドウを出したくなりました。
しかし残念ながら、FungusはVRに対応していません。そこで、FungusをVRでも使えるように機能追加してみました。

作ったもの

こんな感じで会話ウィンドウが出るようになります。

FungusVR2.gif

やらなければいけないこと

会話ウィンドウをVRで使えるようにします。具体的には以下の機能を実装します。

  • 会話ウィンドウを3D空間上に発生させる。
    • Fungusのデフォルトの会話ウィンドウは、画面前面に表示されるようになっています。
  • 発言者の口から会話ウィンドウを発生させ、プレイヤーの目の前に移動する。
    • 発言者から発せられたように見せることができます。
  • プレイヤーの頭の動きに追随させる。
    • 好みによりますが、空間上に会話ウィンドウが固定されているより見やすいと感じました。

環境

  • Unityのバージョン: 2019.2.18f1(64-bit)
  • Fungusのバージョン: v3.12.0
  • Oculus Integrationのバージョン: 12.0

作り方

以下の流れで作成します。

  • VR用の会話ウィンドウを作成する
  • VR用のSayコマンドを作成する
  • 会話を次の状態に進めるスクリプトを作成する
  • SayDialogの設定を行う

VR用の会話ウィンドウを作成する

VR用の会話ウィンドウ、SayDialogForVRコンポーネントを作成します。
SayDialogコンポーネントとの違いは、会話ウィンドウが3D空間上に配置されること、発言者の位置から会話ウィンドウが発生し、定位置に移動することです。

Assets/Fungus/Scripts/Components/SayDialogForVR.cs

using UnityEngine;
using System;

namespace Fungus
{
    public class SayDialogForVR : SayDialog
    {
        Vector3 prevPosition = Vector3.zero;
        Quaternion prevRotation = Quaternion.identity;
        Vector3 prevScale = Vector3.zero;
        Vector3 toScale;
        public float speed = 0.05f;
        public Vector3 localPosition = new Vector3(0.0f, -0.222f, 2.3732f);        
        public Transform mainCamera;
        
        Vector3 GetDialogPosition()
        {
            return mainCamera.position
                + mainCamera.right * localPosition.x
                + mainCamera.up * localPosition.y
                + mainCamera.forward * localPosition.z;                
        }

        Quaternion GetDialogRotation()
        {
            return Quaternion.LookRotation(GetDialogPosition() - mainCamera.position);
        }

        public void SetDialogTransform(Transform from)
        {
            prevPosition = from ? from.position: GetDialogPosition();
            prevRotation = GetDialogRotation();
            prevScale = from ? Vector3.zero : toScale;
        }

        protected override void Start()
        {
            toScale = transform.localScale;
            mainCamera = mainCamera ?? Camera.main.transform;
            base.Start();
        }

        protected override void UpdateAlpha()
        {
            transform.position = Vector3.Lerp(prevPosition, GetDialogPosition(), speed);
            transform.rotation = Quaternion.Lerp(prevRotation, GetDialogRotation(), speed);
            transform.localScale = Vector3.Lerp(prevScale, toScale, speed);
            prevPosition = transform.position;
            prevRotation = transform.rotation;
            prevScale = transform.localScale;
            base.UpdateAlpha();
        }
    }
}

VR用のSayコマンドを作成する

Fungusでは自作コマンドを作成できます。
VR用のSayコマンド、SayForVRコマンドを作成しましょう。先ほど作成した、SayDialogForVRを呼び出すコマンドです。

Assets/Fungus/Scripts/Commands/SayForVR.cs
using UnityEngine;

namespace Fungus
{
    [CommandInfo("Narrative", 
                 "SayForVR", 
                 "Writes text in a dialog box.")]
    [AddComponentMenu("")]
    public class SayForVR : Say
    {
        // 子オブジェクトから、指定のタグが付いたTransformを探す。
        protected Transform FindTagInChildren(Transform t, string tagName)
        {
            if (t == null)
            {
                return null;
            }

            if (t.tag == tagName)
            {
                return t;
            }

            foreach (Transform child in t)
            {
                Transform s = FindTagInChildren(child, tagName);

                if (s)
                {
                    return s;
                }
            }

            return null;
        }

        public Transform talker;

        public override void OnEnter()
        {
            Transform from = FindTagInChildren(talker, "Head") ?? talker;
            ((SayDialogForVR)setSayDialog).SetDialogTransform(from);
            base.OnEnter();
        }
    }
}

会話を次の状態に進めるスクリプトを作成する

会話を次に進めるコンポーネントは、SayDialogについているDialogInputコンポーネントなのですが、このコンポーネントは、UIへの入力検知にStandaloneInputModuleを使用していることを前提としています。
通常、Hierarcy上にCanvas等のUIオブジェクトを配置したとき、同時にEventSystemというGameObjectが配置されます。
このEventSystemにはStandAloneInputModuleというコンポーネントがくっついています。これによりUIオブジェクトがキー入力、マウス入力を検知できるようになるわけですが、Oculusでは、入力にOculus Touchコントローラを使うため、このStandAloneInputModuleOVRInputModuleに置き換えて使うはずです。
そこで、OVRInputModuleを使用するように書き換えた、DialogInputForVRを作成します。

コード

以下のコードでは、Oculus Touchコントローラの、ABXYのいずれかのボタンを押したときに、会話ウィンドウが次に進むように設定しています。デバッグ用に、キーボードのEnter、Space、Zも有効化しています。

Assets/Fungus/Scripts/Components/DialogInputForVR.cs
using UnityEngine;
using UnityEngine.EventSystems;

namespace Fungus
{
    public class DialogInputForVR : DialogInput
    {
        protected override void Update()
        {
            if (EventSystem.current == null)
            {
                return;
            }

            if (writer != null && writer.IsWriting)
            {
                // ここを好きなキーに設定する
                if (OVRInput.GetDown(OVRInput.Button.One)
                || OVRInput.GetDown(OVRInput.Button.Two)
                || OVRInput.GetDown(OVRInput.Button.Three)
                || OVRInput.GetDown(OVRInput.Button.Four)
                || Input.GetKeyDown(KeyCode.Return)
                || Input.GetKeyDown(KeyCode.Z)
                || Input.GetKeyDown(KeyCode.Space))
                {
                    SetNextLineFlag();
                }
            }

            if (nextLineInputFlag)
            {
                var inputListeners = gameObject.GetComponentsInChildren<Fungus.IDialogInputListener>();
                for (int i = 0; i < inputListeners.Length; i++)
                {
                    var inputListener = inputListeners[i];
                    inputListener.OnNextLineEvent();
                }
                nextLineInputFlag = false;
            }
        }
    }
}

参照の追加

上記のスクリプトはAssets/Fungus/Script/Componentsに作りましたが、その場合、Fungus.csprojにOculus.VRのアセンブリ参照を追加する必要があります。そうしないとOVRInputがないといわれてコンパイルエラーになります。

  • Unity Editor上でAssets/Fungus/Fungus.asmdefを選択してください。
  • InspectorでAssembly Definition ReferencesにOculus.VRを追加してください。

assembly.png

Fungus.csprojではなく、Oculus.VRが参照されている別のプロジェクト(Assembly-CSharp.csproj)でスクリプトを作成してもよいです。

SayDialogの設定を行う

Funugsでの会話ウィンドウであるSayDialogを、VRで使えるように設定変更します。

  • SayDialogを配置します。

    • Tools(画面上部のメニューバー) -> Fungus -> Create -> SayDialog で、SayDialogが配置されます。
    • Hirarchy上の場所はどこでもよいです。
    • Rect Transformは、WidthとHeight、Scaleが重要です。ほかはどうでもいいです。
    • 私はこんな感じに設定しました。
    • image.png
  • SayDialogの設定を変更します。

    • InspectorからCanvasコンポーネントを探します。
    • Render ModeをWorld Spaceに変更します。
    • canvas.png
  • Say Dialogコンポーネントを外します。

  • Say Dialog For VRコンポーネントをつけます。

    • Main Cameraに、CenterEyeAnchorをセットします。
    • Speedはとりあえずデフォルトでいいと思います。
      • 発言者の位置から目的の位置まで移動する際の速度です。大きいほど早く移動します。
    • Local Positionもデフォルトでいいと思います。
      • Main Cameraから見た相対位置です。位置が良くない場合は、いい感じの位置になるようにこの位置を調整してください。
    • その他、Continue Button、Dialog Canvas等の設定項目は、もともとSay Dialogコンポーネントに設定されていたものを設定してください。
    • SayDialogForVR.png
  • Dialog Inputコンポーネントを外します。

  • Dialog Input For VRコンポーネントをつけます。

    • 設定変更は必要ありません。
    • DialogInputForVR.png

使い方

SayForVRコマンド

Flowchart上で、SayForVRコマンドを作成します。冒頭のgifだとこんな感じです。

FungusInspector.png

通常のSayコマンドと同じように使えますが、Talkerというパラメータが一つ増えているはずです。
ここに、発言者のTransformを入れましょう。
すると、発言者の口(頭?)から会話ウィンドウが出てきたように見えるはずです。

発言者が自分以外の場合の例です。
image.png

発言者が自分の場合の例です。
image.png

Talkerを設定しなかった場合は、会話ウィンドウがその場に発生しますので、地の分の時に使えます。
image.png

3Dモデルの頭の位置のTransformを探し出すのがめんどくさい

3Dモデルの頭の位置は、モデルによってはHierarchyの階層の深いところにあると思います。例えば今使用している3Dモデルはこんな深いところに頭のTransformがあります。
image.png
毎回これを探し出して、SayForVRコマンドで指定してやらないといけないのはめんどくさいです。そんな時は、頭のTransformを持つGameObjectに、Headタグをつけましょう。
image.png
SayForVRコマンドの中で、デフォルトでHeadというタグのついたTransformを、子階層のTransformを探してくる仕様になっています。
これにより、3Dモデルの一番親階層のTransformをTalkerに指定してやればよくなるので、あまりめんどくさくないですね。

まとめ

Fungusの主要な機能である、SayDialogをVRでも使用できるようにしました。
Menuも需要があれば対応してみます。

12
12
1

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
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?