お疲れ様です、poppyです。本日もこちらのブログを見に来てくださりありがとうございます。
今回もUnityでアセットを使わずにノベルゲームを作るところをお見せいたします。
第8回は「簡易メニュー編」ということで、AUTO(自動)とSKIP(スキップ)機能を実装します。
制作環境
制作環境は第1~7回と同様です。また、変数についても第1~7回のものを使い回すのでご注意ください。
- バージョン:2022.3.24f1 (日本語化パッチ適用済み)
- テンプレート:Universal 2D
- スクリプト:Visual Studio 2022 (Windows)
今回は「宴」などのアセットは用いませんのでご了承ください。(多分使ったほうが早くできるとは思うけど)
当ブログの記載内容や情報の信頼性については可能な限り十分注意をしておりますが、その完全性、正確性、妥当性及び公正性について保証するものではありません。
情報の誤りや不適切な表現があった場合には予告なしに記事の編集・削除を行うこともございます。あくまでもご自身の判断にてご覧頂くようにお願い致します。
当ブログの記載内容によって被った損害・損失については一切の責任を負いかねます。ご了承ください。
あくまでも実装当時のコードを掲載しております。現在はバグなどを潰すために修正を行っているため本記事とは異なるコードになっておりますが、
こちらに反映する予定はございません。大変申し訳ありません。あくまでも参考程度に見ていただければ幸いです。
今回の目標
第8回では以下のことができるようにします。
・ディスプレイに移る簡易メニューの作成
・AUTOモードの実装
・SKIPモードの実装
ボタンとオブジェクト・スクリプトの用意
前回と同じく、あらかじめの画像を用意しておく必要があります。今回はボタンが押されている状態(Active)とそうでない状態(Inactive)の2種類の状態が必要なのでAUTOとSKIPでそれぞれ2種類ずつ用意しました。そして空のオブジェクト「DisplayMenu」を作成し、ヒエラルキーのMessageWindowよりも下に配置します(ただし下図にあるようにMessageWindowの子ではなくGameの子オブジェクトであることに注意)。また、同名のスクリプトDisplayMenu.csを作成しアタッチします。スクリプト実効化と同じくGameManagerへの接続も行っておきましょう。
次にボタンも作成します。これも前回と同じく「UI→ボタン(TextMeshPro)」からボタンを生成します。私の場合はAUTO(以下「AutoButton」)、SKIP(以下「SkipButton」)と書かれている画像を作ったので不要なText(TMP)は消しました。今回はソース画像はなしで大丈夫です(スクリプトから画像を差し込む命令を入れるため)
DisplayMenuの編集
続いてDisplayMenu.csの編集を行います。ここでは以下を実装します。
・AUTO、SKIPボタンの生成
・クリックされるとAUTO、SKIPの表示と状態(Active⇔Inacitive)切り替え
・AUTOモード、SKIPモードがActiveかどうかの検知
実際のAUTO・SKIPの効果はMainTextControllerの領分なのでここでは記述しません。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace NovelGame
{
public class DisplayMenu : MonoBehaviour
{
[SerializeField] private GameObject _displayMenuObject; // 親オブジェクト
[SerializeField] private GameObject _autoButtonPrefab; // オートボタンのプレハブ
[SerializeField] private GameObject _skipButtonPrefab; // スキップボタンのプレハブ
[SerializeField] private Sprite _autoButtonInactiveSprite;
[SerializeField] private Sprite _autoButtonActiveSprite;
[SerializeField] private Sprite _skipButtonInactiveSprite;
[SerializeField] private Sprite _skipButtonActiveSprite;
private bool _isActiveAuto = false;
private bool _isActiveSkip = false;
private bool _isActiveLog = false;
private GameObject _autoButtonInstance;
private GameObject _skipButtonInstance;
void Start()
{
// ボタンのインスタンスを生成
_autoButtonInstance = Instantiate(_autoButtonPrefab, _displayMenuObject.transform);
_skipButtonInstance = Instantiate(_skipButtonPrefab, _displayMenuObject.transform);
// ボタンの位置を設定(ハードコードされているため、エディタで設定する方が良い)
RectTransform autoButtonRectTransform = _autoButtonInstance.GetComponent<RectTransform>();
autoButtonRectTransform.anchoredPosition = new Vector2(600, 450); // AutoButton の位置
RectTransform skipButtonRectTransform = _skipButtonInstance.GetComponent<RectTransform>();
skipButtonRectTransform.anchoredPosition = new Vector2(820, 450); // SkipButton の位置
// ボタンの初期表示を設定
DisplaySprite();
// ボタンのクリックイベントを登録
Button autoButtonComponent = _autoButtonInstance.GetComponent<Button>();
autoButtonComponent.onClick.AddListener(ToggleAuto);
Button skipButtonComponent = _skipButtonInstance.GetComponent<Button>();
skipButtonComponent.onClick.AddListener(ToggleSkip);
}
public bool IsAutoActive()
{
return _isActiveAuto;
}
public bool IsSkipActive()
{
return _isActiveSkip;
}
void DisplaySprite()
{
// AutoButton の表示を切り替え
Image autoButtonImage = _autoButtonInstance.GetComponent<Image>();
if (autoButtonImage != null)
{
autoButtonImage.sprite = _isActiveAuto ? _autoButtonActiveSprite : _autoButtonInactiveSprite;
}
// SkipButton の表示を切り替え
Image skipButtonImage = _skipButtonInstance.GetComponent<Image>();
if (skipButtonImage != null)
{
skipButtonImage.sprite = _isActiveSkip ? _skipButtonActiveSprite : _skipButtonInactiveSprite;
}
if (GameManager.Instance != null)
{
GameManager.Instance.gameUpdateManager.ToggleToNovelMode();
}
}
void ToggleAuto()
{
if (GameManager.Instance != null)
{
GameManager.Instance.gameUpdateManager.ToggleToWaitMode();
}
_isActiveAuto = !_isActiveAuto; // 状態を切り替える
DisplaySprite(); // ボタンの表示を更新
}
void ToggleSkip()
{
if (GameManager.Instance != null)
{
GameManager.Instance.gameUpdateManager.ToggleToWaitMode();
}
_isActiveSkip = !_isActiveSkip; // 状態を切り替える
DisplaySprite(); // ボタンの表示を更新
}
}
}
そしてインスペクタには以下のようにいれていきます。(実際には次回のスクリプトもまとめて書いており、それが反映されてしまっているのでそこは塗りつぶしています)。
MainTextController.csの編集
続いてAUTOやSKIPがActiveであるときに実際にAUTOやSKIPになるようにMainTextControllerをいじります。clickedについては後述します。
public class MainTextController : MonoBehaviour
{
bool clicked = false;
float autoWaitPhaseTime = 1.25f;
float currentAutoWaitTime = 0f;
float skipWaitPhaseTime = 0.1f;
float currentSkipWaitTime = 0f;
//他のメソッドは中略
// ノベルモード時のupdate
public void novelUpdate()
{
// 文章を1文字ずつ表示する
_time += Time.deltaTime;
if (_time >= _feedTime)
{
_time -= _feedTime;
if (!CanGoToTheNextLine())
{
_displayedSentenceLength++;
_mainTextObject.maxVisibleCharacters = _displayedSentenceLength;
}
}
// NovelButtonがクリックされたとき、次の行へ移動
if (clicked || (GameManager.Instance.displayMenu.IsAutoActive() && currentAutoWaitTime > autoWaitPhaseTime) || (GameManager.Instance.displayMenu.IsSkipActive() && currentSkipWaitTime > skipWaitPhaseTime))
{
clicked = false;
currentAutoWaitTime = 0f;
currentSkipWaitTime = 0f;
if (CanGoToTheNextLine())
{
GameManager.Instance.userScriptManager.GoToTheNextLine();
}
else
{
_displayedSentenceLength = _sentenceLength;
}
}
if (GameManager.Instance.displayMenu.IsSkipActive())
{
Time.timeScale = 10.0f;
}
else
{
Time.timeScale = 1.0f;
}
if (CanGoToTheNextLine() && (GameManager.Instance.displayMenu.IsAutoActive() || GameManager.Instance.displayMenu.IsSkipActive()))
{
currentAutoWaitTime += Time.deltaTime;
currentSkipWaitTime += Time.deltaTime;
}
}
}
このようにすることでAUTOモード起動中は全文表示から一定時間(ここでは1.25秒)経つと次のテキストが送られるようになり、SKIPモードにすると10倍速になります。ただ、これだと問題点があって普通の文字送りができなくなります(今まではクリックしたら次のテキストに進めるようになっていたがその部分をclickedに置き換えたため)。
今まではAUTO・SKIPなどがなく、ノベルモードにおけるクリック=次のテキストに進むだったので問題なかったですが、AUTO・SKIPを導入したことによってそれができないため、代わりに透明の「ノベルボタン」を用意し、次のテキストに送る処理をそちらに移します。
まず、UI→ボタン(TextMeshPro)」からボタンを生成し、それをNovelButtonとします。ヒエラルキーはメッセージウィンドウの下、選択肢の上に配置しましょう。
インスペクタは以下にします。透明にしましょう。
そしてMainTextControllerを以下のように再編集すれば良いと思います。
//前略
[SerializeField] GameObject _NovelButton; // ノベルボタン
void Start()
{
// ボタンのクリックイベントを登録
Button novelButtonComponent = _NovelButton.GetComponent<Button>();
novelButtonComponent.onClick.AddListener(NovelButtonClicked);
//中略
}
// NovelButtonがクリックされたかどうかをチェックするメソッド
private void NovelButtonClicked()
{
// イベントシステムからクリックイベントを取得
if (EventSystem.current.IsPointerOverGameObject())
{
PointerEventData eventData = new PointerEventData(EventSystem.current);
eventData.position = Input.mousePosition;
// クリックされたオブジェクトを取得
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);
// NovelButtonがクリックされたかどうかを判定
foreach (RaycastResult result in results)
{
if (result.gameObject == _NovelButton)
{
clicked = true; // NovelButtonがクリックされた瞬間、trueを設定
break;
}
}
}
}
MainTextに忘れずにNovelButtonをいれましょう。
これでAUTOとSKIPが実装できたと思います。
おわりに
ということで今回はノベルゲーム開発の第8回ということで簡易メニューの実装を行いました!
それではまた!