バーチャル3Dクリエイター神部まゆみです(*^_^*)
この記事はUnityで会話システムを作ってみたメモです。
とりあえずキーボード操作向けでやってみるけど、インプットのところを変えればゲームパッドでも行けると思います。後でやるかも。
動作確認した最新の環境はUnity6000.0.32です。
やりたいこと:徘徊する村人に近付いたら話すボタンを表示し、会話できるシステムを実装する
前回Navmeshで村人を徘徊させるところまで作ったので、今度は会話システムを実装してみたい。
どんな感じになるか考えてみる
まぁChatGPTに聞けば教えてくれると思うけど、会話システムと言っても色々あるからな…。
どんなのが良いかイメージを伝えた方が具体的な回答が返ってきて実装しやすいかもしれない。
イメージとしては、
- 村人に会話用のコライダーを追加、コライダーに触れたら話すボタンを表示する
- 話すボタンを押したらゲームは一時停止、半透明の会話ウィンドウと顔グラフィックを表示してテキストを表示
- ボタンを押してテキストを進め、最後まで行ったら会話終了してゲーム画面に戻る
こんな感じかな?
恐らくテキストやグラフィック用のUIを追加しておいて、話すボタンを押したら表示していく感じかな。
以前ギャルゲーっぽいやつを作る試みで似たようなことをやった記憶があるけど、テキストを単に表示するだけなら簡単だった。
でも一文字ずつ表示したい場合はスクリプトが必要になるかな?
ChatGPT氏に聞いてみる
上記を踏まえてChatGPT氏に聞いてみます。




unityでTPSゲームの製作をしています。
会話システムを作りたいのですが、どのように実装すればいいでしょうか?


会話システムの実装手順として、以下のような流れで進めると良いでしょう。
村人に範囲の広いコライダーを設定
BoxCollider
などを村人のGameObject
に追加し、isTrigger
をtrue
に設定する。OnTriggerEnter
/OnTriggerExit
を使って、プレイヤーが範囲内にいるか判定する。- 範囲に入ったら「話す(T)」のUIを表示し、範囲から出たら非表示にする。
コライダーに触れたら話すボタンを表示するスクリプト NPCInteraction.cs
using UnityEngine;
using UnityEngine.UI;
public class NPCInteraction : MonoBehaviour
{
public GameObject talkUI; // 「話す(T)」のUIオブジェクト
private DialogueManager dialogueManager;
private bool canTalk = false; // 会話可能状態のフラグ
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player")) // プレイヤーか確認
{
talkUI.SetActive(true);
canTalk = true;
}
}
void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player"))
{
talkUI.SetActive(false);
canTalk = false;
}
}
void Update()
{
if (canTalk && Input.GetKeyDown(KeyCode.T))
{
FindObjectOfType<DialogueManager>().StartDialogue();
}
}
}
2. 会話モードの管理
DialogueManager
クラスを作り、会話ウィンドウの表示、テキストの進行、会話終了の管理をする。Time.timeScale = 0
を使ってゲームを一時停止し、会話終了時にTime.timeScale = 1
に戻す。
ゲームを一時停止して会話ウィンドウやテキストを表示するスクリプト DialogueManager.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class DialogueManager : MonoBehaviour
{
public GameObject dialogueCanvas; // 会話用キャンバス
public Text villagerDialogueText; // 村人のセリフ用テキストUI
public Text playerDialogueText; // プレイヤーのセリフ用テキストUI
private Queue<string> sentences; // セリフをキューで管理
private bool isTalking = false; // 会話中かどうかを管理
private bool isVillagerSpeaking = true; // 交互に話すためのフラグ
void Start()
{
sentences = new Queue<string>();
}
public void StartDialogue()
{
if (isTalking) return;
isTalking = true;
Time.timeScale = 0; // ゲームを一時停止
// 会話用キャンバスを表示
dialogueCanvas.SetActive(true);
sentences.Clear();
sentences.Enqueue("こんにちは!村へようこそ!"); // 村人
sentences.Enqueue("よろしくお願いします!"); // プレイヤー
sentences.Enqueue("この村にはたくさんの人がいますよ。"); // 村人
sentences.Enqueue("楽しみです!"); // プレイヤー
DisplayNextSentence();
}
public void DisplayNextSentence()
{
if (sentences.Count == 0)
{
EndDialogue();
return;
}
// 交互にセリフを表示(村人とプレイヤー)
if (isVillagerSpeaking)
{
villagerDialogueText.gameObject.SetActive(true); // 村人のテキストUIを表示
playerDialogueText.gameObject.SetActive(false); // プレイヤーのテキストUIを非表示
villagerDialogueText.text = sentences.Dequeue(); // 村人のセリフをセット
}
else
{
villagerDialogueText.gameObject.SetActive(false); // 村人のテキストUIを非表示
playerDialogueText.gameObject.SetActive(true); // プレイヤーのテキストUIを表示
playerDialogueText.text = sentences.Dequeue(); // プレイヤーのセリフをセット
}
isVillagerSpeaking = !isVillagerSpeaking;
}
public void EndDialogue()
{
// 会話終了後にキャンバスを非表示にする
dialogueCanvas.SetActive(false);
Time.timeScale = 1; // ゲーム再開
isTalking = false;
}
void Update()
{
if (isTalking && Input.GetKeyDown(KeyCode.Return)) // Enterキーで次の文章へ
{
DisplayNextSentence();
}
}
}
3. UIの作成
- 「話す(T)」のUI
Text
オブジェクトで作成し、talkUI.SetActive(false);
で非表示にしておく。
- 会話ウィンドウ
Canvas
内にPanel
を作成し、半透明にする。Text
オブジェクトを配置してセリフを表示。Image
を使ってプレイヤーと村人の顔を表示。
4. 拡張要素 ※後でやる
- 会話データをScriptableObjectで管理
→ 会話ごとにスクリプトを書かずに済み、管理が楽になる。 - 選択肢の追加
→Button
を配置し、選んだ内容によって分岐するシステムを作る。 - 音声の再生
→AudioSource
を使ってセリフごとに音声をつける。
まとめ
NPCInteraction
でプレイヤーの接触を検知し、「話す(T)」を表示。DialogueManager
で会話の流れを管理。- UIを作成し、プレイヤーとNPCの会話を再現。
これで基本的な会話システムが実装できます!
そんなに難しくなさそう?
ScriptableObjectとやらを使えば、テキストを別の場所に格納しておいて読み出しやすくできるのかな?
まぁ長くなりそうなのでとりあえず今回はそこまでやらずに実装してみます。
実際に実装してみる
UIの読み出しさえちゃんとできれば動くと思うので、まぁやってみます。
村人にコライダーを追加、触れたら話すボタンを表示する
まずコライダーを追加して半径をちょっと大きめにし、IsTriggerにチェックを入れます。
話すコマンドのUIを作成する
これは村人の頭上あたりに表示したいかな?ChatGPT氏に追加で聞いたらUIのレンダーモードをWorld Spaceにするとできるっぽいです。
キャンバスとテキストを追加して村人のRoot直下に配置、位置をXYZ0にして微調整します。
この時点で一応表示されてるっぽい。あとはサイズと位置を調整すればいいかw
調整後のコンポーネント
ちょっと大きいけどこんなものかな。


↓コンポーネントはこんな感じ。




↓はText(Legacy)でやった。フォントサイズが小さすぎると文字がボケてしまったので、大きめにしてキャンバスのスケールサイズで調整しました。


コライダーに触れたら話すボタンを表示するスクリプトを追加する
ボタンというかテキストを表示するだけだけども、まぁそこはボタンっぽいデザインに後でするってことで(^_^;)
上記のスクリプトNPCInteraction.csとDialogueManager.csをコピペして追加します。
とりあえずNPCInteraction.csだけ追加してみて動作確認してみます。
今回はDialogueManager.csは使わないけど、NPCInteraction.csの中にDialogueManager.csへの参照が書かれているので、両方コピペして作っておかないとエラーになるから注意。
これで動いた!
キャンバスのUIは非表示にしておく
これやらないと動作が逆になってしまった。
後でスクリプトでやるようにしておくかも。


テキストが常にプレイヤーを向くようにする
横から見ると見えなくなってしまうので、常にプレイヤーの方を向くようにします。
テキストが常にプレイヤーの方を向くスクリプト Billboarding.cs
// Y軸のみ回転させる
transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y + 180, 0);
の部分は、テキストが逆向きになっていたから追加しました。
テキストの向きによって変わるかも?その場合は180の値を調整すれば良い感じになるかもしれない。
using UnityEngine;
public class Billboard : MonoBehaviour
{
public Transform target; // プレイヤーのTransform
void Update()
{
if (target != null)
{
// プレイヤーの方向を向く
transform.LookAt(target);
// Y軸のみ回転させる
transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y + 180, 0);
}
}
}
これをキャンバスかテキストにアタッチして、プレイヤーのtransformを指定してやればOK。


これでいい感じになった!
もしくは3Dオブジェクトアイコンにして表示する手もあるかな。
まぁとりあえず今回はこれでいきます。
つづく
長くなったから続く。
最初は難しそうと思ったけど案外普通にできそうだな…。
ChatGPT様様ですねw
追記:続きはこちら↓