メニュー

Unityでノベルゲームを作ろう!(第9回:バックログ)

  • URLをコピーしました!

お疲れ様です、poppyです。本日もこちらのブログを見に来てくださりありがとうございます。
今回もUnityでアセットを使わずにノベルゲームを作るところをお見せいたします。
第9回は「バックログ」ということで、LOG(バックログ)を実装します。

制作環境

制作環境は第1~8回と同様です。また、変数についても第1~8回のものを使い回すのでご注意ください。

  • バージョン:2022.3.24f1 (日本語化パッチ適用済み)
  • テンプレート:Universal 2D
  • スクリプト:Visual Studio 2022 (Windows)

今回は「宴」などのアセットは用いませんのでご了承ください。(多分使ったほうが早くできるとは思うけど)

免責事項

当ブログの記載内容や情報の信頼性については可能な限り十分注意をしておりますが、その完全性、正確性、妥当性及び公正性について保証するものではありません。
情報の誤りや不適切な表現があった場合には予告なしに記事の編集・削除を行うこともございます。あくまでもご自身の判断にてご覧頂くようにお願い致します。
当ブログの記載内容によって被った損害・損失については一切の責任を負いかねます。ご了承ください。

お詫び

あくまでも実装当時のコードを掲載しております。現在はバグなどを潰すために修正を行っているため本記事とは異なるコードになっておりますが、
こちらに反映する予定はございません。大変申し訳ありません。あくまでも参考程度に見ていただければ幸いです。

今回の目標

第9回では以下のことができるようにします。
・バックログ

LOGボタンの作成

まずLOGボタンを作ります。ですがこれは第8回でAUTO・SKIPボタンでやったのと全く同じなのでそちらを参照してください。
違いがあるとすれば、バックログを閉じるための画像を用意したほうが良いかもしれません。私は適当に×印の画像を用意しました。

スクロールビューの作成

バックログを実装するにはスクロールビューを作成するのが良いと思います。まず、空のオブジェクト「BacklogDisplay」を配置し、その下に「UI→スクロールビューの作成」でスクロールビューを作成してください。

スクロールビューを作成すると以下のようになっていると思います。

これを私は以下のように書き換えました。(BacklogCloseがPrefab化していますがこれは他の「閉じる」にこれを再利用したためなのであまり気にしないでください)

まず最初のBacklogですが、ここでは背景画像を設定するのと今回は水平スクロールは不要なのでその部分を削除しました。また、Backlog関係のスクリプトはここにアタッチします。
次にBacklogTitleですが、単に画像を配置しています(上の「バックログ」と書いてある画像)。
次のViewportはデフォルトのままで、その下のContentが重要です。ここには2つのコンポーネントをアタッチします。

Vertical Layout Group」はコンテンツ(=バックログでいうところの各会話や地の文など)を垂直要素に配置するコンポーネントです。これによってコンテンツを縦に並べます。
Content Size Fitter」はコンテンツのサイズを管理します。ここでは高さをPreferred Heightに設定します。
スクロール系は今回はいじっていません。
最後に付け足したBacklogCloseはいわゆる「閉じる」ボタンで、これをクリックするとバックログが消えるようにしています。

バックログのコンテンツPrefabの作成

続いてバックログのコンテンツに置くPrefabを作成します。つまり、会話文や地の文などをブロックしたものを作成します。画像で見たほうが早いかもしれません。

※便宜上ここでは「名前」「文章」と書いてありますが実際は消します。
背景がViewportBackground 1(Image)、名前がViewportName(TMPro)、文章がViewportSentence(TMPro)になっています。これらは単に画像とテキストをくっつけたもので特にスクリプトなどは必要なく作ることができます。これをPrefab化することでコンテンツの素材を生成できるようにします。
これを「ViewportContents」と呼びます。

バックログの生成

それではバックログ部分のスクリプトに移りたいと思いますが、その前にLOGボタンを押すとバックログが出るスクリプトを作ります。これはDisplayMenu.csに記述します。

public class DisplayMenu : MonoBehaviour
{
  //他のボタンは省略
    [SerializeField] private GameObject backlogDisplay; // 親オブジェクト(バッグログディスプレイ)
    [SerializeField] private GameObject backlogPrebab; // バッグログのプレハブ
     [SerializeField] private Sprite _logButtonInactiveSprite;
     [SerializeField] private Sprite _logButtonActiveSprite;
      private bool _isActiveLog = false;
      private GameObject _logButtonInstance;
      private GameObject _backlogInstance;
      
      void Start()
      {
      //省略:LogボタンのクリックでToggleLogが動作するようにLogボタンの生成・配置・クリックイベントの登録を行う
      }
      
      
      //表示なども省略(Logボタンの挙動も追加する
      
       public void ToggleLog()
       {
       GameManager.Instance.gameUpdateManager.TurnOnLogMode(); //ログモードに切り替える
       _backlogInstance = Instantiate(backlogPrebab, backlogDisplay.transform); //バックログを生成する
       _isActiveLog = !_isActiveLog; // 状態を切り替える
       DisplaySprite(); // ボタンの表示を更新
       }
       
       public void TurnOffLog()
       {
         _isActiveLog =  false; // 状態を切り替える
         DisplaySprite(); // ボタンの表示を更新
       }
 }

インスペクターは以下のようにします。

これでバックログの生成部分はOKです。次にバックログの部分を記述しましょう。

BacklogDisplayの記述

バックログ部分を記述します。もちろんスクリプト実効化(第2回参照)を忘れずに。
やることは以下です。
・バックログのテキストリスト(名前・文章)の取得
・バックログのContentと閉じるボタンの取得
・先ほど作ったViewportContentsに各テキスト(名前・文章)を代入して生成
・閉じるボタンを押すとDestroyしてバックログを削除

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

namespace NovelGame
{
  public class BacklogDisplay : MonoBehaviour
  {
    // バックログに表示するテキストのリスト
    private List<(int LineNumber, string Name, string Text)> backlogLogTextList;

    // ViewportContentsのプレハブ
    [SerializeField] private GameObject viewportContentsPrefab;


    // ContentのRectTransform
    private RectTransform contentRectTransform;

    // ScrollViewのRectTransform
    private RectTransform scrollViewRectTransform;

    // Start is called before the first frame update
    void Start()
    {
      // バックログのテキストリストを取得
      backlogLogTextList = GameManager.Instance.mainTextController.GetLogTextList();

      // バックログを見つける
      Transform backlog = transform;

      // ViewportとContentとbuttonを探す
      Transform viewport = null;
      Transform content = null;
      GameObject BacklogCloseObject = null;
      foreach (Transform child in backlog)
      {
        if(child.name == "BacklogClose")
        {
          BacklogCloseObject = child.gameObject;
        }
        if (child.name == "Viewport")
        {
          viewport = child;
          foreach (Transform grandchild in child)
          {
            if (grandchild.name == "Content")
            {
              content = grandchild;
              break;
            }
          }
        }
      }

      //閉じるボタンを取得
      Button BacklogCloseComponent = BacklogCloseObject.GetComponent<Button>();
      BacklogCloseComponent.onClick.AddListener(BacklogClose);



      // ViewportContentsのプレハブをContentの下にインスタンス化し、Contentの高さを要素数に応じて調整
      if (viewport != null && content != null)
      {
        // ContentのRectTransformを取得
        contentRectTransform = content.GetComponent<RectTransform>();

        // ScrollViewのRectTransformを取得
        scrollViewRectTransform = viewport.parent.GetComponent<RectTransform>();

        // ViewportContentsのプレハブをContentの下にインスタンス化
        string NameText = "";
        string SentenceText = "";
        for (int i = 0; i < backlogLogTextList.Count;i++)
        {
          NameText = backlogLogTextList[i].Name;
          SentenceText = backlogLogTextList[i].Text;
          InstanceViewportContents( NameText, SentenceText, content);
        }

      }
      ScrollRect scrollRect = GetComponent<ScrollRect>();
      scrollRect.verticalNormalizedPosition = 0;
    }

    // ViewportContentsをインスタンス化する
    private void InstanceViewportContents(string nameText, string SentenceText, Transform content)
    {
      GameObject newViewportContents = Instantiate(viewportContentsPrefab, content);
      
      GameObject ViewportName = newViewportContents.transform.GetChild(0).gameObject;
      GameObject ViewportSentence = newViewportContents.transform.GetChild(1).gameObject;
      TextMeshProUGUI nameTextMeshPro = ViewportName.GetComponent<TextMeshProUGUI>();
      TextMeshProUGUI sentenceTextMeshPro = ViewportSentence.GetComponent<TextMeshProUGUI>();
      nameTextMeshPro.text = nameText;
      sentenceTextMeshPro.text = SentenceText;
    }

    void BacklogClose()
    {
      GameManager.Instance.displayMenu.TurnOffLog();
      Destroy(this.gameObject); // バックログの親オブジェクトを破棄
    }

  }
}

ここで、backlogLogTextList = GameManager.Instance.mainTextController.GetLogTextList();とあるように、mainTextControllerの方からバックログの文章を拾ってくる必要があるのでその部分も記述します。


  public class MainTextController : MonoBehaviour
  {
    //略
    private List<(int LineNumber, string Name, string Text)> logTextList = new List<(int, string, string)>();

    //中略

    // テキストを表示
    public void DisplayText(string sentence, bool isStatement)
    {
 
      string[] words = sentence.Split(',');
      
      string namesentence = words[2];
      string textsentence = GameManager.Instance.variablesManager.ReplaceVariablesInExpression(words[3]);
      if(isStatement)
      {
        _mainTextObject.text = null;
        _nameTextObject.text = null;

      }
      else
      {
        _mainTextObject.text = textsentence;
        _nameTextObject.text = namesentence;
        // 行番号とともにリストに追加
        int lineNumber = GameManager.Instance.lineNumber;
        logTextList.Add((lineNumber, namesentence, textsentence));
      }

    }

    // ログテキストリストを取得するためのgetterメソッド
    public List<(int LineNumber, string Name, string Text)> GetLogTextList()
    {
      return new List<(int LineNumber, string Name, string Text)>(logTextList);
    }

  }

これでバックログが実装されたはずです。(思い出しながら書いているため、書き漏れがあれば大変申し訳ございません。)

終わりに

ということで今回はノベルゲーム開発の第9回ということでバックログの実装を行いました!
それではまた!

シェアよろしくお願いします!
  • URLをコピーしました!