【ノベルゲーム制作】④Unity Naninovel カスタムUI、カスタム変数【まほだん】
Unityのことを完全に理解した
完全に過言ですが、ちょっとずつこなれてきている感じがします。またまた時間が開いてしまいましたが、進捗がないというよりは、機能を色々と追加していて、せっかく動画にするならちょっとまとまったところまでやろう、という感じで溜め込んでいました。AC6ももりもりやりましたが。
というわけで、何はともあれまず成果物です。
見ての通り、画面が動いたり、タイマーが表示されたり、カーソルが切り替わったりしてます。あと、Naninovelのバージョンがv1.18とそれ以前で大きく変わっていて、特に、TextからText Mesh Proに変わったこととかもあります。全部一つの記事にまとめると大変なので、いくつかに記事を分けます。
Naninovelというか、Unityをやっててわかったのは、まあオブジェクト指向なんですね。そりゃそうなんですけど、いや、今更って話なんですけど、なんか作りながらわかっていけばいいか~という気持ちで触れてたので、なんというかそういうことが腑に落ちたという感じです。インスペクターでぽちぽちしてるけど、基本的にはとりあえず、UnityEventを使って、呼び出す関数と、関数を呼び出す仕組みを作ればまあ色々できるというのを、GUIで便利にしてるんだな、というのがわかってきました。それを使って、タイマーを作ったり、アニメーションに合わせてSEを入れたり、カーソルを変えたりというのを作ってます。
それと、ラジオは一応毎月かかさずとってるので、有料のプランの方は聴いてみてね。
特別な演出
目的 ―― Naninovelにない演出や特別な処理をしたい
Naninovelにもいろんな効果や表現がデフォルトで実装されてますし、それだけでそれなりにリッチな表現ができると思うんですが、基本的にプレーンで、特定のスタイルを押し出したものではないし、冒頭の動画のようなタイマーみたいな処理は用意されていません。しかしNaninovelはUnityのアセットなので、プログラムすればそれこそUnityで出来ることは何でもできると言ってもいいでしょう。
魔法少女確殺弾では、タイトルの通り魔法少女確殺弾で、それを撃つ、撃たないというのが重要な選択になっていきます。その重要な選択を強調するのに、特別な演出を実装することになりました。
まほだん制作開始時ではそんな予定ではなかったんですが、専用のUIをこんな感じでやるといいんじゃないか、とみるきさんと話し合って、やることになりました。
カスタムUI ―― NaninovelとUnityの自由さの接合面
Naninovelでそういう色々な機能を追加したいときには、カスタムUIというものを使います。
WARNING
カスタムUIを作成したり、既存のUIを変更したりする前に、まずUnityのUIシステム 🡕(uGUI)をよく確認してください。以下で利用できるUIカスタマイズのチュートリアル動画とサンプルプロジェクトがありますが、Unityの組み込みツールについて追加のガイダンスまたはサポートは提供していませんので、ご注意してください。詳細は サポートページ をご覧ください。
って書いてありますが、Unityで好きにあれこれやるための機能なので、これを使えばかなりのことがNaninovelと結合できるよ、ということです。他のノベルゲームのエンジンについて何も詳しくないのであれなんですけど、UnityのPrefabやAnimatorの表現力や自由度ってかなりのものだと思っていて、わざわざUnity使ってノベルゲーム作る人って、そのあたりに魅力を感じているからではないでしょうか。たぶん。
カスタムUIの機能を使うと、動画の、黒い枠がにゅーっと出てきたり、タイマーを呼び出したりとか、カーソルが変わったりの演出が、
こんなかんじに、
@showUI MahodanUI (枠がでるほう)
@showUI TimerUI (タイマーのほう)
とか
@hideUI TimerUI
@hideUI MahodanUI
とかのコマンドで出し入れできるようになります。
Naninovel公式の説明動画だと、コマンドでカレンダーを呼び出してるのがわかりますね。私はだいたいこの動画を真似して実装していきました。
カスタムUIの作り方 ―― 無垢なカスタムUIの赤ちゃん
カスタムUIを作って使えるようにするだけなら、かなり簡単にできるようになっています。といっても、それだけだと何も機能のないものになりますが。
こんなかんじで、Projectの中を右クリックして、Create→Naninovel→CustomUIをすると、NewCustomUIというPrefabが作成されます。このカスタムUIの赤ちゃんに名前をつけて、Configulation→Naninovel→UIから登録すれば、それだけでオッケーです。
こんなかんじでMahodanUIとTimerUIが登録されてます。
カスタムUI ―― かわいいベイビーが動いたり、数を数えるようにする
Prefabを呼び出せるので、それだけで好きなオブジェクトを追加できて、枠なりなんなりはいくらでも作れます。枠や画像を出すだけじゃなくて、Prefabなのでアニメーションを入れたりもしてます。にゅーっと黒い枠で狭まる効果は、MahodanUIが呼び出されると共にアニメーションさせています。
これは実装したMahodanUIのCustomUIというコンポーネントです。項目は色々ありますが、重要なのはOn Show()とOn Hide()で、その通り、NaninovelからShowしたときとHideしたときにイベントを呼び出せるので、これによってアニメーションを操作してます。このあたりの処理は口パクのほうで結構書いたので、参考になれば。
Timerはこんな感じで、自作のscriptを追加しました。
ちょっと試行錯誤したままのコードなので恥ずかしいのですが、こんな感じです。
using UnityEngine;
using System.Collections;
using TMPro;
using Naninovel;
using UnityEngine.Events;
public class Timer : MonoBehaviour
{
private float totalTime;
private float seconds; // 秒数の変数
private float timeSpeed = 1f; // timeSpeed の初期値を設定
private bool timerStarted = false; // タイマーが開始されたかどうか
[SerializeField]
private TextMeshProUGUI TimerText;
// 3秒以下の時に呼び出す
[SerializeField]
private UnityEvent onThreeSecondsLeftEvent;
// 1秒以下の時に呼び出す
[SerializeField]
private UnityEvent onOneSecondLeftEvent;
// タイマー終了時に呼び出す
[SerializeField]
private UnityEvent onTimerFinishedEvent;
public void Start()
{
timerStarted = false;
// 外部の変数マネージャーから秒数を取得し、floatに変換して代入
var variableManager = Engine.GetService<ICustomVariableManager>();
string timerString = variableManager.GetVariableValue("timer");
if (float.TryParse(timerString, out float timerValue))
{
totalTime = timerValue;
}
else
{
totalTime = 0f; // または適切なデフォルト値を設定
}
// タイマー表示用UIテキストを更新
UpdateTimerText();
StartCoroutine(UpdateTimerColor());
}
public void StartTimer()
{
timerStarted = true;
}
private void UpdateTimerText()
{
TimerText.text = totalTime.ToString("F2");
}
private IEnumerator UpdateTimerColor()
{
while (totalTime > 0f)
{
if (totalTime <= 1f)
{
TimerText.color = Color.red;
onOneSecondLeftEvent.Invoke();
timeSpeed = 0.8f;
}
else if (totalTime <= 3f)
{
TimerText.color = Color.yellow;
onThreeSecondsLeftEvent.Invoke();
timeSpeed = 0.9f;
}
else
{
TimerText.color = Color.white;
timeSpeed = 1f;
}
yield return null; // 次のフレームまで待機
}
// タイマー終了時に timeSpeed を元に戻す
timeSpeed = 1f;
}
void Update()
{
if (!timerStarted || totalTime <= 0f)
{
return;
}
totalTime -= Time.deltaTime * timeSpeed;
if (totalTime < 0f)
{
totalTime = 0f;
}
// タイマー表示用UIテキストを更新
UpdateTimerText();
if (totalTime <= 0f)
{
Debug.Log("制限時間終了");
onTimerFinishedEvent.Invoke();
}
}
}
こんなふうに自分で作った関数を呼び出せるので、カスタムUIでかなりのことができるなと思いました。同じ感じでカーソルが拳銃の照準に変わるのとかも、実装してます。
あとは、animatorの方にEventhandlerを持たせて、アニメーションに合わせてSEを鳴らしたりとかも実装しました。
カスタム変数 ―― Naninovelの方から変数に触る
ここで、タイマーの時間設定なんですが、8秒に設定されています。この決定は、自作のスクリプトの方ではなく、Naninovelの方で設定できるようになっています。Naninovelにはカスタム変数という機能があって、Naninovelのスクリプトの方からある程度変数に触れるようになっています。
公式のカスタムUIの日付表示の動画サンプルも、カスタム変数で実装しているので、この動画を見るとわかりやすいです。
タイマーの時間セットは、@set timer=8
@showUI TimerUI
@wait {timer+2}
という形でNaninovelのスクリプトの方で動くようになっています。
このtimerというカスタム変数を登録するだけなら、
こんなかんじでマニュアル通り、Custom Variablesに項目を追加するだけでできます。ここで作ったTimerというカスタム変数は、上記のコードだとstart()内の
var variableManager = Engine.GetService<ICustomVariableManager>();
string timerString = variableManager.GetVariableValue("timer");
で呼び出しており、設定したカスタム変数名を入れるだけで呼び出せます。
というわけで、今回はカスタムUIとカスタム変数について触れました。UnityそのものやC#についてはもっと詳しい記事がいっぱいあると思うので、そのあたりについてはかなり省略しました。
ゲームの制作って地道ですね。一つ一つ機能を追加したり、パラメータを調整したりして、いい感じになるように頑張っています。それでは!