対応バージョン:Unity 6.3 LTS(6000.3系)/WebGL出力 この記事はシリーズ全11回の第9回です。第8回ではオープニング会話と話題カードのフラグ連動を実装しました。今回はUIのクリック検知とScriptableObjectの扱い方に焦点を当てて、キャラクター選択の仕組みを掘り下げます。 【PR】本記事にはアフィリエイトリンクを含みます。
目次
- はじめに
- 1. UIオブジェクトのクリック検知
- 2. 話題の解放管理——ScriptableObjectの落とし穴
- 3. 話題の状態に応じたセリフ出し分け
- 4. 会話終了後の遷移設計
- 5. 動作確認チェックリスト
- まとめ
- あわせて読みたいおすすめ書籍【PR】
- この記事を作成するにあたって参考にしたサイト
はじめに
前回はオープニング会話と話題カードのフラグ連動を実装しました。
今回は「キャラクターをクリックして話題カードを開く」仕組みを作ります。一見シンプルに見えますが、UnityのUIクリック検知には注意点があり、話題の解放管理にも設計上の落とし穴があります。
この記事で解説するのは以下の3点です。
- なぜ
OnMouseDownではなくUIクリックには専用の仕組みが必要なのか - 話題の解放状態をScriptableObjectで管理すると何が起きるか
- 話題の状態(あり・尽き・未解放)に応じてセリフを出し分ける方法
1. UIオブジェクトのクリック検知
OnMouseDownが使えない理由
立ち絵はCanvasの子オブジェクト(UI Image)として配置されています。WorldSpaceのオブジェクトに使うOnMouseDown()はUI要素では動作しません。
UnityのUIには別のクリック検知の仕組みがあります。それがIPointerClickHandlerインターフェースです。
IPointerClickHandlerとは
IPointerClickHandlerを実装したコンポーネントをUI要素にアタッチすると、そのUI要素がクリックされたときにOnPointerClick()が自動的に呼ばれます。
仕組みはシンプルです。
プレイヤーがクリック
↓
UnityのEventSystemがUI要素のRaycastを検知
↓
IPointerClickHandlerを持つコンポーネントのOnPointerClick()を呼ぶ
ただしRaycast TargetがONになっていないとクリックが届きません。立ち絵のImageコンポーネントでRaycast TargetがONになっているか必ず確認してください。

CharacterClickHandler.csの作成
Assets/Scripts/UI/フォルダにCharacterClickHandler.csを新規作成します。
using UnityEngine;
using UnityEngine.EventSystems;
public class CharacterClickHandler : MonoBehaviour, IPointerClickHandler
{
public CharacterData characterData;
public void OnPointerClick(PointerEventData eventData)
{
if (characterData == null) return;
if (DialogueManager.Instance.IsDialogueRunning()) return;
TopicCardManager.Instance.OpenTopicPanel(characterData);
}
}
会話中はクリックを無効にするため、IsDialogueRunning()を使っています。このメソッドはAssets/Scripts/Dialogue/DialogueManager.csに以下を追加して実装します。
public bool IsDialogueRunning()
{
return isPlaying;
}
Unityでの設定
各キャラクターオブジェクト(CharacterTanaka・CharacterSaya・CharacterIsobe)に対して:
Add Component→ CharacterClickHandlerを追加characterDataに対応するCharacterDataをアサイン

2. 話題の解放管理——ScriptableObjectの落とし穴
何が起きるか
前回の実装では、フラグが立ったときにTopicData.isUnlocked = trueと書き換えていました。
これはエディタ上でPlayモードを終了しても変更が残るという問題を引き起こします。ScriptableObjectはアセットファイルそのものなので、Playモード中の変更がアセットに保存されてしまうのです。
Playするたびに話題のチェックが増えていき、毎回手動でリセットしなければならなくなります。
解決策:FlagManagerで管理する
ScriptableObjectを書き換えるのをやめ、FlagManagerに「解放済みTopicIDのセット」を持たせます。
話題の解放状態
Before:TopicData.isUnlocked(アセットに保存される)
After :FlagManager.unlockedTopics(実行中のみ保持)
Assets/Scripts/System/FlagManager.cs内の話題解放処理をこう変えます。
private HashSet<string> unlockedTopics = new HashSet<string>();
public bool IsTopicUnlocked(string topicID, string unlockFlagID)
{
if (string.IsNullOrEmpty(unlockFlagID)) return true; // 最初から解放
return unlockedTopics.Contains(topicID);
}
void UnlockTopicsByFlag(string flagID)
{
TopicData[] allTopics = Resources.LoadAll<TopicData>("Topics");
foreach (TopicData topic in allTopics)
{
if (topic.unlockFlagID == flagID)
unlockedTopics.Add(topic.topicID);
}
}
Assets/Scripts/Dialogue/TopicData.csからisUnlockedフィールドも削除します。Playを止めてもTopicDataアセットは一切変化しなくなります。
3. 話題の状態に応じたセリフ出し分け
キャラクターをクリックしたとき、3つの状態が考えられます。
| 状態 | 挙動 |
|---|---|
| 話せる話題がある | TopicPanelを開く |
| 全話題を聞き終えた | 「もう話すことはない」と表示 |
| まだ解放されていない | 「あの人とは、まだ話せない」と表示 |
「もう話すことはない」「まだ話せない」は通常の会話パネルを使ってモノローグとして表示します。これにより専用UIを作らずに済みます。

CharacterDataに専用フィールドを追加する
Assets/Scripts/Dialogue/CharacterData.csに2つのフィールドを追加します。
public DialogueData noMoreTopicsDialogue; // 話題が尽きた時
public DialogueData notYetAvailableDialogue; // まだ話せない時

DialogueDataを作成する
以下のCSVをインポートして、2種類のDialogueDataを作成します。
NoMoreTopics.csv
sceneTitle,setFlagOnComplete,speakerName,dialogueText,isMonologue
NoMoreTopics,,凛,(もう話すことはない。),true
NotYetAvailable.csv
sceneTitle,setFlagOnComplete,speakerName,dialogueText,isMonologue
NotYetAvailable,,凛,(あの人とは、まだ話せない。),true
これを全キャラのCharacterDataにアサインします。1つ作れば全キャラで使い回せます。
クリック時の分岐フロー
クリック時の処理の流れは以下の通りです。
キャラクタークリック
↓
話題の状態を判定
↓
HasTopics → TopicPanelを開く
NoMoreTopics → 「もう話すことはない」を再生 → 選択画面に戻る
NotYetAvailable → 「まだ話せない」を再生 → 選択画面に戻る
4. 会話終了後の遷移設計
会話が1つ終わったとき、次にどの画面を出すかの設計も重要です。
基本の流れ
会話終了
↓
そのキャラにまだ話題がある → TopicPanelを再表示
そのキャラの話題が尽きた → キャラクター選択画面に戻る
Assets/Scripts/UI/TopicCardManager.csのRefreshTopicButtons()に「表示できる話題がゼロなら選択画面に戻る」処理を追加します。
EndDialogue()のコールバック設計に注意
EndDialogue()内でフラグを立てると、そのフラグに反応して別の会話がすぐに始まることがあります(Chapter4への自動遷移など)。このときonEndedCallbackが上書きされてしまう問題が起きます。
対策はシンプルです。コールバックをローカル変数に退避してnullにしてから、フラグを立てるという順番にします。
コールバックをローカル変数に退避
↓
onEndedCallback = null(上書き対策)
↓
フラグを立てる(この中で新しい会話が始まっても影響なし)
↓
退避したコールバックを呼ぶ
5. 動作確認チェックリスト
- [ ] キャラクターをクリックするとTopicPanelが開く
- [ ] 会話中にキャラクターをクリックしても反応しない
- [ ] 話し終わった話題は話題カードから消える
- [ ] 全話題を聞き終えたキャラをクリックすると「もう話すことはない」と表示される
- [ ] まだ解放されていないキャラをクリックすると「まだ話せない」と表示される
- [ ] Playを止めてもTopicDataのチェックが残らない(ScriptableObject汚染なし)
まとめ
今回のポイントは2つです。
UIクリックにはIPointerClickHandlerを使う。 Canvas上のUI要素はOnMouseDownでは反応しません。EventSystemを通じてクリックを検知する仕組みを理解しておくと、今後のUI実装でも役立ちます。
ScriptableObjectを実行時に書き換えない。 状態管理はMonoBehaviourのフィールドやHashSetで行い、アセットは設定データとして読み取り専用で使うのが基本です。
次回は、キャラクター選択画面の実装と、Chapter3・4へのフラグ連鎖を解説します。
あわせて読みたいおすすめ書籍【PR】
今回実装したUIクリック検知・ScriptableObjectの扱い方についてさらに学びたい方には、以下の書籍もおすすめです。本セクションにはアフィリエイトリンクを含みます。
- 『Unityの教科書 Unity 6完全対応版』(北村愛実 著/SBクリエイティブ) ── EventSystemやUIコンポーネントの基礎を体系的に学べるUnity 6対応の定番入門書
- 『楽しく学ぶ Unity「2Dゲーム」作りのきほん』(森巧尚 著/マイナビ出版) ── UIのクリック判定やキャラクター操作の基礎を実例を交えて解説
- 『作って学ぶゲームプログラミング Unity まるっと入門 Unity6完全対応』(トライタム 著/吉谷幹人 監修) ── ScriptableObjectとMonoBehaviourの使い分けなど、今回の設計に直結する内容を含む
- 『ゲームシナリオの書き方 第2版』(佐々木智広 著/SBクリエイティブ) ── 話題が尽きたとき・まだ話せないときのセリフ設計をシナリオ側から学べる一冊
- 『作って学べる Unity本格入門 Unity6対応版』(賀好昭仁 著/技術評論社) ── 今回のシリーズを読み終えてさらにステップアップしたい人向けの中級者向け書籍
※このセクションのリンクはアフィリエイトリンクであり、商品が購入された場合、サイト運営者に紹介料が支払われることがあります【PR】。
この記事を作成するにあたって参考にしたサイト
- Unity – Scripting API: IPointerClickHandler(Unity公式ドキュメント)
- Unity – Manual: ScriptableObject(Unity公式ドキュメント)
- 前回の実践記事「【ADV編⑧】オープニング会話からゲームを始める——GameStarterと話題カードの連動」(本サイト内)


コメント