Dialogue System for Unity を使ってスクリプトベースのノベル風会話シーンを作る

こんにちは。IAFです。

今回は、

Unity用アセット「Dialogue System for Unity」を使ってスクリプトベースのノベル風会話シーン

の作成について忘備録代わりに作成します。

 

この記事ではサンプルの組み合わせで以下のような感じを作ります。

 

このような方におすすめ。

・Dialogue System for Unityを会話画面に使用したい

・URP(Universal Renderer Pipeline)対応の会話画面表示ソフトを探している

・会話文章はテキストで作り、Dialogue System for Unityで読み込んで使用したい。文章は後で修正することを前提にしたい。

・c#スクリプトで会話遷移を作り、表示などはアセットを使用したい

・ランダムに遷移する会話等、ちょっと変わった会話システムを作りたい(今回の調査目的)

 

Dialogue System for Unityについて

アセットの一般的な説明

会話関係が2D3D問わずいろんなパターンで作れるアセットです。

色々できる代わりに学習コストそれなり。また日本語情報が少ないです。

Unity Asset Store 公式

Get the Dialogue System for Unity package from Pixel Crusher…

もうすぐ開催のSpring Saleで安く買えるみたいですので、もし興味あれば検討してみてはいかがでしょうか。

 

宴3との比較

一度前のゲーム製作で使用しての感想は以下です。この前には宴3を使用していたので、それと比較してみます。なお、他アセット(NaniNovel等)は使用していないのでわかりません。

—メリット—

①フォーラムの情報は非常に豊富で、検索能力と英文読解ができれば大体解決できる

②URPに対応している(URP用の他アセットを併用できる。パーティクルとか)

③(使い方が分かれば)いろいろなシーケンスが用意されていて拡張性は高い

④複数シーンを使用する開発において、デバック用に各シーンに配置することができる(もちろん正常動作時には1つしか動作しない状態にする必要がある)

⑤スクリプトから会話シーンの制御が可能

—デメリット—

⑥宴3のようにシーンに配置してすぐ使えるお手軽さは無い

⑦ドキュメントが基本英語。なんとなくの概要を把握するのに労力が必要。

⑧組み込みのノードベースエディタで文章を作ることになり、比較的長めの会話シーンは作りにくい。特に選択肢分岐が作りにくい

⑨ノベルゲームでよく見る「選択肢表示」は標準で用意されていない

 

個人的には②、④、⑤のメリットがデメリットを上回ったので宴3よりこちらを使用することにしました。宴は日本語だしお手軽でよかったのですが。

ちなみに私は英語が得意な方ではないですが、作者の方がきれいな英語を使っているそうで、マニュアル関係は結構翻訳ソフトが活用できます。

環境構築

Dialogue System for Unityのインポート

いつも通りアセットのインポート。

(Unity Editor->Window->Package Manager->Dialogue System for Unity)

ここはいつも通りでOKです。

 

Visual Novel Frameworkのインポート

会話シーンの表示をノベル風にしたいので、拡張パックを使います。

公式ページDialogue system ExtrasからUnityPackageをダウンロード。

ページを下方にスクロールしていくと

DOWNLOAD “VISUALNOVELFRAMEWORK_2_0_9A.UNITYPACKAGE”

というボタンがあるのでそこからダウンロードします。

完了後、

Asset->Import Package->CustomPackageよりダウンロードしたファイルを選択してインポート。

 

スクリプト動作用サンプルのインポート

以前同じようなことを考えていた方がいたようで(探すのに手間取りましたが…)、

公式の作者様がサンプルを用意してくださってます。

公式Forumの質疑「EXECUTING FROM SCRIPT」

ページ中央ぐらいから、作者様製サンプルをダウンロード。

↓直リンク

http://www.pixelcrushers.com/dialogue_system/download/fileshare/ScriptExample_2018-07-16.unitypackage

これもImport -> Custom Packagesよりインポート。

 

サンプルシーンの編集

サンプルシーンは

Assets\Dialogue System Examples\Script Example

に展開される。Script Example(シーンファイル)を開く。

 

ノベル用UIを表示するため、UI表示のPrefabをインスタンス化する。

Assets\Dialogue System Extras\Visual Novel Framework\Prefab

Prefab名:Dialogue Manager VN

 

インスタンス化したDialogue Manager VNのDatabaseを、サンプルで使用されているDatabaseに変える。

Assets\Dialogue System Examples\Script Example内のScript Example DatabaseをDialogue Manager VNのインスペクターのDatabase欄にドラック&ドロップ。

 

最後に、もともとSceneにあった「Dialogue Manager」インスタンスを無効化(Inspector上でチェックを外す)

 

プレイモードで確認できます。

 

以上、お疲れさまでした。

 

補足 サンプルシーンのソースコード

ForumからUnityPackageのダウンロードしないと見れない仕様なので、

これまた忘備録かねてソースコードを転記しておきます。

ファイル名:ScriptConversation.cs

using UnityEngine;
using PixelCrushers.DialogueSystem;

public class ScriptConversation : MonoBehaviour
{

    // Notes:
    // Settings changed on Dialogue Manager prefab:
    // - Subtitle Settings > Show PC Subtitles During Line ticked.
    // - Input Settings > Always Force Response Menu unticked.

    private void Start()
    {
        // Create database:
        var database = ScriptableObject.CreateInstance();

        // Create a template, which provides helper methods for creating database content:
        var template = Template.FromDefault();

        // Our actors defined in dialogue database:
        int aldoID = 1;
        int richardID = 2;
        int lucyID = 3;

        // Create a conversation: [ID 0=START] --> [ID 1=Hello]
        int conversationID = 1;
        var conversationTitle = "My Conversation";
        var conversation = template.CreateConversation(conversationID, conversationTitle);
        conversation.ActorID = aldoID;
        conversation.ConversantID = richardID;
        database.conversations.Add(conversation);

        // START node: (Every conversation starts with a START node with ID 0)
        int entryID = 0;
        var startNode = template.CreateDialogueEntry(entryID++, conversation.id, "START");
        startNode.ActorID = aldoID;
        startNode.ConversantID = richardID;
        startNode.Sequence = "None()"; // START node usually shouldn't play a sequence.
        conversation.dialogueEntries.Add(startNode);

        // Node 1:
        var node = template.CreateDialogueEntry(entryID++, conversation.id, string.Empty);
        node.ActorID = richardID; // Richard speaks this line.
        node.ConversantID = aldoID;
        node.DialogueText = "And you will spend a month in here, because of your new school. Is that so Aldo?";
        node.Sequence = "MoveTo(Aldo_Start, Aldo); MoveTo(Richard_Start, Richard); MoveTo(Lucy_Start, Lucy); {{default}}"; // {{default}} uses Dialogue Manager's Default Sequence.
        conversation.dialogueEntries.Add(node);
        // Link from START:
        var link = new Link(conversation.id, startNode.id, conversation.id, node.id);
        startNode.outgoingLinks.Add(link);

        // Node 2:
        var previousNode = node;
        node = template.CreateDialogueEntry(entryID++, conversation.id, string.Empty);
        node.ActorID = aldoID; // Aldo speaks this line.
        node.ConversantID = richardID;
        node.DialogueText = "Yes Mr. Rivera";
        node.Sequence = "Camera(Camera_A_Pos,,1); {{default}}"; // Move camera to Camera_A_Pos over 1 second.
        conversation.dialogueEntries.Add(node);
        // Link from previous node:
        link = new Link(conversation.id, previousNode.id, conversation.id, node.id);
        previousNode.outgoingLinks.Add(link);

        // Node 2:
        previousNode = node;
        node = template.CreateDialogueEntry(entryID++, conversation.id, string.Empty);
        node.ActorID = lucyID; // Lucy speaks this line.
        node.ConversantID = richardID;
        node.DialogueText = "Good, it has quite while since we got any visitors isn't it Richard";
        node.Sequence = "SendMessage(InitScene,,Script Conversation)@{{end}}";
        conversation.dialogueEntries.Add(node);
        // Link from previous node:
        link = new Link(conversation.id, previousNode.id, conversation.id, node.id);
        previousNode.outgoingLinks.Add(link);
        
        // Add it to the runtime environment and play it:
        DialogueManager.AddDatabase(database);
        DialogueManager.StartConversation(conversationTitle);
    }

    public void InitScene()
    {
        Debug.Log("InitScene() was called.");
    }

}