見出し画像

[Unity]会話アセットDialogueSystemをカスタムする方法

ゲーム開発進捗は、ラグドールのつかみ投げを実装する予定でしたが、その下準備として、各種パラメータの変動と制御が必要な頃合いなので、データ登録や買い物やクエストに対応できる会話システムのアセット・DialogueSystemを試しています。
https://assetstore.unity.com/packages/tools/ai/dialogue-system-for-unity-11672

公式マニュアル(英語)
http://www.pixelcrushers.com/dialogue_system/manual2x/html/

画像7

DialogueSystemは豊富なUIデザインサンプルを持ち、それ単体で3Dアドベンチャーゲームが作れる一通りのものを備えていますが、すでに自作のデータベースとUIとキャラコンがある場合は…
…というか、デザイナーだったりモデラーだったりの見た目を自分色に染めないと気がすまないって人は、会話システムの必要な部分だけ都合良く使いたいという話になります。

DialogueManagerのセットアップ

DialogueSystemインポートののち、DialogueSystemのコアであるDialogueManagerプレハブをシーンに入れます。

画像1


画像2


DialogueManagerをインスペクタに表示して、まず自分のデータベースファイルを作ります。Initial DatabaseのCreateボタンを押します。
中身作成はひとまず後回しです。

画像3


ウィザードは各種設定が一度にできますが、ひとまず何もしなくても良さげです。文字送りスピードぐらいは変えてもいいかも。

UIのカスタムをするには

最初につまずくであろうことは「会話UIはどこにあるの?」です。

画像14


DialogueManagerの本体、DialogueSystemControllerコンポーネントの下にInstantiatePrefabsコンポーネントがあり、ここで会話UIのテンプレートとなるプレハブが選べます。が。
これは実行時にUIを生成する機能なので、エディタ上にUIを作るわけではありません
InstantiatePrefabコンポーネントは、Canvasになにもない場合にテンプレートUIを生成する補助機能で、Canvas下に手動でUIをセットしておくと、そっちが優先されます。
すでに手動でUIがセットしてあるなら、InstantiatePrefabコンポーネントは不要なので削除もしくは非アクティブにしてOKです。その下のInputDeviceManagerも同様です。

画像4

UIを自分色に染めるには、Prefabフォルダの中からたたき台にふさわしいデザインのUIプレハブを選んで、それをDialogueUIの場所=Canvas下に入れて、プレハブ解除して改造します。

画像5

その後、DialogueUIの欄にカスタムしたUIを設定すれば、自作UIでDialogueSystemを使うことができます。

画像8


検証したところ、テキスト送りボタンや顔アイコンなどは設定しなくても動きます。使わないUIは非表示にしとけばいいでしょう。
実際のゲームでは会話UI以外と複数のカンバスとUIを併用するので、会話UI表示時に他のUIとぶつからないような、かつ複数の画面サイズに対応できるような、うまいレイアウトを考える必要があります。

画像6

データベースの仕様

データベースのエディターウィンドウでは、変数、アクター、クエスト、アイテムなどの変数の項目はありますが、基本ブールと整数と文字列しか使えません。

画像9

肝心の会話は、Conversationsタブから新規に会話ツリーを作れますが、
会話ツリーの名前の設定がエディターウィンドウからはできず、インスペクターからでないとできません。ここも結構なつまづきポイントです
エディターウィンドウ内ののなにもないところをクリックするとインスペクターに会話ツリーの概要が出て編集できます。

DialogueSystem固有の変数を制御する

一番のつまづきポイントですが、
データベース自体は、実行中は直接制御できません。
データベースは初期設定であり、
実行時にインスペクタでDialogueManagerやデータベースの数値をいじっても、ゲームでの動きには反映されません。
実行時にDialogueManagerの設定とデータベースの中身はLuaという独自スクリプトに渡され、Luaの中の変数のみ変更できる…という仕様になってます。実行を終了するとLuaの中身はリセットされます。


PixelCrushers.DialogueSystem.DialogueLua.SetVariable(string,object);
で、DialogueSystemの外からC#でデータベースの変数を書き換えられます。

画像11

            //0-1のシリアル値で計算してる時刻DayTimeを、大雑把な時間帯int aに変換する
            //a=0 朝 a=1 昼 a=2 夕方 a=3 夜
            int a = 3;
           if ((DayTime >= 0.25f) && (DayTime < 0.35f)) { a = 0; }
           if ((DayTime >= 0.35f) && (DayTime < 0.45f)) { a = 1; }
           if ((DayTime >= 0.45f) && (DayTime < 0.70f)) { a = 2; }

           PixelCrushers.DialogueSystem.DialogueLua.SetVariable("DayTime",a);

上の例は、DialogueSystemデータベースにDayTimeという変数を作り、そこにゲーム内時間を0朝1昼2夕3晩という整数の値に変換して送ってます。
これで時間が変わるとセリフが変わる演出ができます。
取得する場合はGetVariableです。

これで、自前のゲームマネージャーデータベースとDialogueSystemデータベースの中身を同期させることができます。
スクリプトで制御する詳しい説明は、こちらに載ってます。
http://www.pixelcrushers.com/dialogue_system/manual2x/html/logic_and_lua.html#howToUseLuaInScripts


外部から会話を制御する

DialogueSystemはトリガーコライダーとRigidBodyとeventsystemと、用意された数個のコンポーネントで、TPS操作で照準を合わせたキャラを判別してキーを押したら固有の会話ができる仕様が実現できますが、
操作系はすでに作ってある、キャラ名は表示はしない、余計なことせずキャラに近づいたら話すボタンが出てそれ押したらしゃべれるようにしたい(そして会話中はUIを消したい)、という場合、DialogueSystemの各種コンポーネントは使わず、
DialogueManagerを直接制御して会話をスタートさせられます。

 string a="会話ツリーの名前";
_DialogueMamager.StartConversation(a);

_DialogueMamagerはシーン上に配置したDialogueMamagerプレハブの核をなすDialogueSystemControllerを割り当てた変数です。String aには目当ての会話ツリーの名前(文字列)を入れます。これを呼び出して実行すると会話が始まります。

手順は、
1.しゃべる対象=NPCや張り紙などのゲームオブジェクトに、動作させたい会話ツリー名の文字列を何かしらの方法で持たせて
2.プレイヤーは1.に近よってトリガーやレイキャストやGetComponentの力で1.が有する文字列を読み取って保持
3.同時に会話UIを出現させる
4.UIボタンが押されたらボタンのイベントからプレイヤーに上記のメソッドで_DialogueMamager.StartConversation(2.で取得した文字列);を実行させる
とやるといいでしょう。

5.会話中はUIを消して操作不能にして会話終了後に復帰、
も必要ですがそれは後述します。

会話ツリーでの変数を制御するには、Luaの文法で記述します。

データベース上で制御する場合は、会話ツリー内のノードを選んでインスペクタから、
会話中の演出ならSequenceの欄に記入
変数フラグの判定ならconditionsの欄に記入
変数フラグの書き換えならScriptの欄に記入

画像10

基本はVariable["変数名"]演算子、という文法です。
例:不等号の判定
Variable["enemiesKilled"] < 5
Variable["enemiesKilled"] >= 5
CurrentQuestState("Enemy Attack") == "active"
Variable["password"] ~= "dominate"
例:値の代入
SetQuestState("Get the Launch Codes", "success")
Variable["Gold"] = Variable["Gold"] + 50
とc#と似た記述法になってます。複数の処理を同時に行う場合は末尾に;を書きます。

文法が間違ってたらシンタックスエラーの警告が出ますが、変数名が間違ってたり演算子が=と==間違えたとかだと、何も出ない場合があります。

Luaコードと変数名の入力には、VisualStudioとか当然使えないので手打ちになります。複数の文字列をコピペできるソフトなどを導入してると楽になるでしょう。(私はClibor使ってます)

会話から外部を制御する

会話中はUIを消して操作不能にする、会話によってデータベースの外の変数を制御する、ゲームオブジェクトを生成するなど外部に干渉させたい場合は、方法はいくつかあります。
まず会話ノードのシーケンスSequenceで、カメラや音声の制御ができます。しかしシーケンスは変数の制御まではできません。
Luaにc#のメソッドを紐つけて実行させることもできるようですが、
一番妥当なのは、シーンイベントです。
会話ツリーのノードの下の方にEventsの項目があり、SceneEventを追加できます。※Scene-Indipendent Eventの方ではありません。

画像15


これで会話ツリーのはじめと終わりのノードで、UI表示を切り替えできます。
しかし、変数やオブジェクトの制御はともかく、UI非表示は全ての会話ツリーに仕込むのは冗長なので、別の方法にします。
会話中のフラグを取得する方法が適切ですが、DialogueSystemController内には isConversationActiveというフラグがあります。もしくは、実行中は会話UIが非表示から表示に切り替わってるはずなので、それらを参照することで、会話中限定の一括処理ができます。

言語切り替えを実装する

ローカライズする方法は、
https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
の言語コード表記と合わせるといいらしいです。※これに準拠せず独自表記でも使えそうではあるけど
英語 en
日本語 ja
これをデータベースのTemplates-Dialogue Entriesに追加します。

画像12

そしてMenu-Apply Template to Assetを押すと、データベースの会話ツリーのノードに多言語の項目が追加されます。

画像13

※キャラ名やアイテム名やクエスト名の翻訳は話が長くなるので割愛
これで、インスペクタからDialogueManagerのLanguageの項目で設定すれば、その言語に切り替わる…はずですが、なぜか空欄とenしか認識しませんでした。jaと入れても実行したらenに書き換えられる。実行中に書き換えてもだめ。
c#で書き換えるなら普通にいけるのですが。

 public void SendToDialogueDatabase()
 {
     if (Language == 0) { _DialogueManager.SetLanguage("ja"); }
     if (Language == 1) { _DialogueManager.SetLanguage("en"); }
 }

言語は後から追加すると色々厄介のもとになるので、開発初期のうちに英語と日本語の二言語くらい作っておくのがよさそうです。

他の機能

多分ゲーム終了すると変数・フラグがリセットされるので、データベースのセーブ機能も実装する必要があります。
他にもバーク=吹き出しUIの使用、会話から別の会話に移行、会話中の演出、など状況に応じてやりたいこともいろいろあるでしょうが、それは別の機会に解説します。もしくは自力で調べましょう。

いろいろ欲張るには、公式の用意したスクリプトを元に必要最低限の機能だけ取り入れたコードを自作することになるでしょう。汎用性を高めるとそれだけ使いづらくもなるので、公式がお出しできるものには限界があります。


おまけ。
私見ですが、プログラマーがインディーゲーム作るのとデザイナー・モデラーがインディーゲーム作るのでは、条件が大きく違います。
自分が持ってないスキルがないと作れないアセットはUnityアセットストアで入手して補えるという建前ですが、
プログラマーならモデルや画像のアセットが一通りあればゲームが作れます。
しかしデザイナー・モデラーはいくらプログラムのアセットをいくら入手しようが、それらをつなぎ合わせる手段を知り得ないから、ゲームを作ることはできません。
前者はインディーゲームを作れて一山当てる可能性が最低でも1%ありますが、後者は0です。大きな隔たりがあります。
少なくとも私はそれで何年も苦い思いをしてきました。
この記事は、2Dデザインと3Dモデリングができる、なんならUIもサウンドもできるけど、プログラムだけは脳が受け付けなかった自分が、それでもゲームが作りたい一心で数年かけて必死でC#を体に覚えさせた苦労をもとに書いてます。
デザイナー・モデラーだけどゲームを作りたい、でもプログラムのアセットがうまく使えない、という人が助かる記事がかけたら幸いです。



この記事が気に入ったらサポートをしてみませんか?