ふりかえり
前回は「レアモンスター」の追加を行いました!
倒すと確定でダイヤや大量の経験値がもらえ、演出も豪華でテンションが上がる仕様です。プレイヤーの強化に直結する、ちょっとしたご褒美のような存在になりました。
ストーリーを実装!
今回は、ついにストーリー機能の実装に着手しました!
これまでのメニューには、
- ステータス
- お知らせ
- 設定
- クレジット
…といった項目がありましたが、ここに「ストーリー」という新しいボタンを追加しました。
ストーリーで没入感アップ
ゲームの世界観にプレイヤーを引き込むには、物語性の強化がとても効果的です。
そこで、ステージの進行に応じてストーリーが段階的に解放されていく仕組みを導入。
ゲームプレイだけでなく、物語の進行というもう一つの楽しみを提供できるようになります。
スクリプトの変更
StoryManager.cs
以下のコード StoryManager.cs は、「ストーリー閲覧機能」を統括するマネージャークラスです。
主な役割
- ストーリーボタンを初期化し、押されたら該当ストーリーを表示
- ストーリーごとの**解放条件(到達ステージ)**を管理
- 未解放のストーリーはボタンをグレーアウトして押せないようにする
- ストーリータイトル・本文の表示更新
- ストーリーのスクロール管理や、ボタンの見た目変更 など
処理の大まかな流れ
- 初期化処理:
- 登録されているボタンやストーリーデータを読み込み。
- RecordManager を通じてプレイヤーの到達ステージ情報を取得。
- 解放チェックとUI更新:
- 各ストーリーに設定された requiredStage に到達しているかを確認。
- 未達成の場合は「???(ステージXで解放)」というテキストを表示し、ボタンを無効化。
- 表示処理:
- ストーリーボタンを押すと該当の StoryData を参照し、テキストを更新。
- 同時にスクロール位置やボタンの色なども更新してユーザビリティを向上。
- データ構造:
- StoryData にはタイトルと本文を格納。
- StoryUnlockCondition では、各ストーリーの解放条件(インデックス・必要ステージ)を管理。
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections.Generic;
public class StoryManager : MonoBehaviour
{
[Header("ストーリー選択ボタン")]
public List<Button> storyButtons = new List<Button>(); // ストーリー選択ボタンのリスト
public List<string> storyTitles = new List<string>(); // ストーリーのタイトルリスト
[Header("ストーリー表示UI")]
public ScrollRect storyScrollRect; // ストーリー表示用のスクロールビュー
public TextMeshProUGUI storyContentText; // ストーリー内容表示用のテキスト
public TextMeshProUGUI storyTitleText; // ストーリータイトル表示用のテキスト
[Header("ストーリーデータ")]
public List<StoryData> stories = new List<StoryData>(); // ストーリーデータのリスト
[Header("Manager References")]
public RecordManager recordManager; // RecordManagerへの参照
[Header("ストーリー解放設定")]
[Tooltip("各ストーリーの解放条件を設定")]
public List<StoryUnlockCondition> storyUnlockConditions = new List<StoryUnlockCondition>();
private int currentStoryIndex = 0; // 現在選択されているストーリーのインデックス
void Start()
{
InitializeStoryButtons();
InitializeStories();
InitializeStoryUnlockConditions();
UpdateStoryUnlockStatus();
// 最初のストーリーを表示
if (stories.Count > 0)
{
ShowStory(0);
}
}
// ストーリーボタンを初期化
private void InitializeStoryButtons()
{
for (int i = 0; i < storyButtons.Count; i++)
{
int index = i; // クロージャーのためにローカル変数を作成
if (storyButtons[i] != null)
{
storyButtons[i].onClick.AddListener(() => ShowStory(index));
// ボタンのテキストを設定
if (i < storyTitles.Count)
{
TextMeshProUGUI buttonText = storyButtons[i].GetComponentInChildren<TextMeshProUGUI>();
if (buttonText != null)
{
buttonText.text = storyTitles[i];
}
}
}
}
}
// ボタンの見た目を更新
private void UpdateButtonAppearance()
{
for (int i = 0; i < storyButtons.Count; i++)
{
if (storyButtons[i] != null)
{
bool isActive = (i == currentStoryIndex);
UpdateButtonAppearance(storyButtons[i], isActive);
}
}
}
// 個別のボタンの見た目を更新
private void UpdateButtonAppearance(Button button, bool isActive)
{
if (button == null) return;
ColorBlock colors = button.colors;
if (isActive)
{
// アクティブなボタンは明るく表示
colors.normalColor = Color.white;
colors.highlightedColor = Color.white * 1.1f;
colors.pressedColor = Color.white * 0.9f;
colors.selectedColor = Color.white;
}
else
{
// 非アクティブなボタンは暗く表示
colors.normalColor = Color.gray;
colors.highlightedColor = Color.gray * 1.1f;
colors.pressedColor = Color.gray * 0.9f;
colors.selectedColor = Color.gray;
}
button.colors = colors;
}
// 次のストーリーを表示
public void ShowNextStory()
{
int nextIndex = (currentStoryIndex + 1) % stories.Count;
ShowStory(nextIndex);
}
// 前のストーリーを表示
public void ShowPreviousStory()
{
int previousIndex = (currentStoryIndex - 1 + stories.Count) % stories.Count;
ShowStory(previousIndex);
}
// 現在のストーリーインデックスを取得
public int GetCurrentStoryIndex()
{
return currentStoryIndex;
}
// ストーリーの総数を取得
public int GetStoryCount()
{
return stories.Count;
}
// ストーリー解放状態を更新
public void UpdateStoryUnlockStatus()
{
if (recordManager == null) return;
int maxStageReached = recordManager.GetRecords().maxStageReached;
for (int i = 0; i < storyButtons.Count; i++)
{
if (storyButtons[i] != null)
{
bool isUnlocked = IsStoryUnlocked(i, maxStageReached);
UpdateButtonUnlockStatus(storyButtons[i], isUnlocked, i);
}
}
}
// ストーリー解放条件を初期化
private void InitializeStoryUnlockConditions()
{
// デフォルトの解放条件を設定(Inspectorで設定されていない場合)
if (storyUnlockConditions.Count == 0)
{
storyUnlockConditions.Add(new StoryUnlockCondition { storyIndex = 0, requiredStage = 1, description = "はじまりの物語" });
storyUnlockConditions.Add(new StoryUnlockCondition { storyIndex = 1, requiredStage = 4, description = "魔法の森の秘密" });
storyUnlockConditions.Add(new StoryUnlockCondition { storyIndex = 2, requiredStage = 7, description = "闇の王の復活" });
}
}
// 指定されたストーリーが解放されているかチェック
private bool IsStoryUnlocked(int storyIndex, int maxStageReached)
{
// storyUnlockConditionsから該当する条件を検索
foreach (var condition in storyUnlockConditions)
{
if (condition.storyIndex == storyIndex)
{
return maxStageReached >= condition.requiredStage;
}
}
// 条件が見つからない場合は解放されていないとする
return false;
}
// ボタンの解放状態を更新
private void UpdateButtonUnlockStatus(Button button, bool isUnlocked, int storyIndex)
{
if (button == null) return;
// ボタンの有効/無効を設定
button.interactable = isUnlocked;
// ボタンのテキストを取得
TextMeshProUGUI buttonText = button.GetComponentInChildren<TextMeshProUGUI>();
if (buttonText != null)
{
if (isUnlocked)
{
// 解放済み:通常のテキスト
if (storyIndex < storyTitles.Count)
{
buttonText.text = storyTitles[storyIndex];
}
}
else
{
// 未解放:解放条件を表示
int requiredStage = GetRequiredStageForStory(storyIndex);
buttonText.text = $"??? (ステージ{requiredStage}で解放)";
}
}
// ボタンの色を更新
ColorBlock colors = button.colors;
if (isUnlocked)
{
// 解放済み:通常の色
colors.normalColor = Color.white;
colors.highlightedColor = Color.white * 1.1f;
colors.pressedColor = Color.white * 0.9f;
colors.selectedColor = Color.white;
}
else
{
// 未解放:グレーアウト
colors.normalColor = Color.gray;
colors.highlightedColor = Color.gray;
colors.pressedColor = Color.gray;
colors.selectedColor = Color.gray;
}
button.colors = colors;
}
// 指定されたストーリーを表示(解放チェック付き)
public void ShowStory(int storyIndex)
{
if (storyIndex < 0 || storyIndex >= stories.Count)
{
Debug.LogWarning($"無効なストーリーインデックス: {storyIndex}");
return;
}
// 解放チェック
if (recordManager != null)
{
int maxStageReached = recordManager.GetRecords().maxStageReached;
if (!IsStoryUnlocked(storyIndex, maxStageReached))
{
int requiredStage = GetRequiredStageForStory(storyIndex);
Debug.Log($"ストーリー{storyIndex}はまだ解放されていません。ステージ{requiredStage}に到達してください。");
return;
}
}
currentStoryIndex = storyIndex;
StoryData story = stories[storyIndex];
// ストーリー内容を表示
if (storyContentText != null)
{
storyContentText.text = story.content;
}
// ストーリータイトルを表示
if (storyTitleText != null)
{
storyTitleText.text = story.title;
}
// スクロール位置を一番上に設定
if (storyScrollRect != null)
{
storyScrollRect.verticalNormalizedPosition = 1f;
}
// ボタンの見た目を更新
UpdateButtonAppearance();
Debug.Log($"ストーリーを表示: {story.title}");
}
// 指定されたストーリーの必要ステージ数を取得
private int GetRequiredStageForStory(int storyIndex)
{
foreach (var condition in storyUnlockConditions)
{
if (condition.storyIndex == storyIndex)
{
return condition.requiredStage;
}
}
return 999; // 条件が見つからない場合は非常に高い値を返す
}
// 外部から呼び出し可能なストーリー解放状態更新メソッド
public void CheckAndUpdateStoryUnlocks()
{
UpdateStoryUnlockStatus();
}
}
// ストーリーデータクラス
[System.Serializable]
public class StoryData
{
public string title; // ストーリーのタイトル
public string content; // ストーリーの内容
}
// ストーリー解放条件クラス
[System.Serializable]
public class StoryUnlockCondition
{
[Tooltip("ストーリーのインデックス(0から開始)")]
public int storyIndex;
[Tooltip("解放に必要なステージ到達数")]
public int requiredStage;
[Tooltip("ストーリーの説明(デバッグ用)")]
public string description;
}
Unityでの設定
Unityエディタ上では以下の手順で簡単に設定できます:
1.空のGameObjectを作成して StoryManager.cs をアタッチ。
2.ボタンを複数作成し、storyButtons リストにアサイン。
3.Scroll View を作成して、本文やタイトルの表示エリアをセット。

4.StoryData と解放条件をインスペクターで入力。
※この構造なら、後からストーリー数を増やしたり、解放条件を調整したりするのも簡単です。

動作確認
- ストーリーボタンを押すと正しくストーリーが表示されました!
- 到達していないステージのストーリーは「???」で表示され、ボタンも無効になっていました。
これにより、「ストーリーを読みたければ先に進まないと!」という自然な動機づけが実現できました。
まとめ
- ストーリー閲覧機能を無事実装!
- 解放型の構成により、プレイヤーの成長と物語の進行がリンク。
- ゲーム世界への没入感を強化する大きな一歩となりました。
ストーリーの中身はこれから本格的に練っていきますが、まずは土台ができたということで大きな前進です。
進捗
今回はレアモンスターの追加をしました!
- (済)オート戦闘:ボタン操作なしでもバトルが進むようにする。
- (済)オートスキル発動:スキルも自動で発動する機能。
- (済)UIのブラッシュアップ:デザインとレイアウトを整えて見やすくする。
- (済)ステージ数の拡充(最低20):遊びごたえを高めるためにボリュームアップ。
- 復活機能(広告視聴):ゲームオーバー時、広告視聴で復活できる仕組み。
- 広告報酬でダイヤ獲得:無課金でもダイヤが手に入る仕組み。
- (済)G強化・アーティファクト・転生の拡張:各システムにさらなる深みを持たせる。
- (済)回復薬の追加:ステージを進めやすくする。
- アニメーションの追加:ゲーム画面にもっと動きを持たせる。
- (済)レアモンスターの追加:わくわく感を追加する。
- 放置要素の追加:プレイしていない時間も強くなる。
- (仮済)メニューの内容追加:ストーリー、記録、ガイドなどプレイヤーが遊びやすくする。
次回予告
次回は「記録機能」を追加予定です!
プレイヤーのバトルの履歴や転生回数などを記録していくことで、達成感を深める機能となる予定です。お楽しみに!

