
ここまでは、バトルが終わると同じバトルを最初からやり直していました。せっかくレベルが上がっても、次のバトルではLv1に戻ってしまいます・・・。今回は、まほう図書館の「ロビー」を作って、バトルとロビーを行き来できるようにしたいと思います。減ったHPや、もらったお金はそのまま持ち越し、休憩室でお金を払うと元気になりますよ!
- シリーズの目次:ターン制RPGの作り方
- 前回:#16 どうぐとインベントリ
今回作るもの

- まほう図書館の「ロビー」のシーン(
Lobby)を作る。メニューは「地下へ」「休憩室」「売店」「なかま」「セーブ」- 今回動くのは「地下へ」と「休憩室」です。売店は#18、なかまは#20、セーブは#25で作ります(それまでは灰色で押せないボタン)
- 仲間(経験値・今のHPとMP)、持ち物、お金を、シーンをまたいで持ち越す(
GameSession) - シーンを切り替えるときは、画面を暗くしてから読み込み、新しいシーンを明るくして始める(
SceneFader) - バトルが終わったら「ロビーへ」で戻る。ぜんめつしたら、お金が半分になってロビーで目をさます
- 休憩室:10ゴールドで、みんなのHPとMPが満タンになる(倒れた仲間も起きる)
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| PartyMember | 仲間1人分の、バトルが終わっても残るもの | (付けない) |
| GameSession | シーンをまたいで持ち越すもの(仲間・持ち物・お金) | Lobby の GameSession |
| SceneFader | 暗くしてから、シーンを切り替える | Lobby・Battle の SceneFade |
| BattleUnit・BattleLineup・BattleManager | 持ち越したものでバトルを始めて、結果を書き戻す | - |
| LobbyMenu | ロビーのメニューと休憩室 | Lobby の Lobby |
| CharacterData・UnitView | 休憩室で うとうとするポーズ | - |
持ち越すものをまとめよう

この節では、シーンを切り替えても消えずに残る入れ物を作ります。
シーンを切り替えると、前のシーンのオブジェクトは全部消えます。BattleLineupの中の仲間(BattleUnit)も、持ち物も一緒に消えてしまいますね。そこで、持ち越したいものをGameSessionという1つのオブジェクトにまとめて、シーンを切り替えても消えないようにします。
- シーンの切り替えと
DontDestroyOnLoadは、8-3 スクリプトでシーンを切り替えようでやりました。今回のGameSessionは、そこで作ったものに仲間と持ち物を足した形です
PartyMember

バトル中の1人(BattleUnit)は、絵(UnitView)やステータスの行とつながっているので、バトルのシーンと一緒に消えてしまいます。そこで、バトルが終わっても残したいものだけを、別のクラスにします。
- 「PartyMember」スクリプトを作る
// 仲間1人分の、バトルが終わっても残るもの(経験値・今の HP と MP)
public class PartyMember
{
public CharacterData Data { get; }
public int TotalExp { get; set; } // もらった経験値の合計
public int Hp { get; set; }
public int Mp { get; set; }
public int Level => LevelTable.LevelFor(TotalExp);
public Stats Stats => Stats.AtLevel(Data.BaseStats, Data.Growth, Level); // 今のレベルの能力
public bool IsDead => Hp <= 0;
// 最初は HP・MP を満タンにしておく
public PartyMember(CharacterData data, int totalExp)
{
Data = data;
TotalExp = totalExp;
RestoreAll();
}
// HP・MP を満タンにする(休憩室・ぜんめつしたとき)
public void RestoreAll()
{
Hp = Stats.maxHp;
Mp = Stats.maxMp;
}
}
- 覚えておくのは、経験値の合計・今のHP・今のMPの3つだけです
- レベルと能力は、経験値から計算できるので覚えません。#12 経験値とレベルアップの
LevelTableとStats.AtLevelをそのまま使います - 覚えたまほうも、レベルから決まるので覚えません(BattleUnitを作るときに
LearnSkillsで調べ直します) MonoBehaviourではない、ふつうのクラスです。持ち物のInventoryと同じですね
CharacterData(ScriptableObject)に今のHPを書き込まないの?
ScriptableObjectのアセットに値を書き込んでも、シーンを切り替えて残ります。ただ、エディタで再生しているときは、書き込んだ値が再生を止めても戻りません。遊ぶたびにアセットのHPが減っていき、気づかないうちにデータが壊れてしまいます。
ScriptableObjectは「最初の能力」「成長」のような、遊んでも変わらないデータだけにして、遊んで変わるものはPartyMemberのような別のクラスに持たせましょう。ScriptableObjectについては12-8 ScriptableObjectでデータを作ろうを見てください。
GameSession

仲間3人・持ち物・お金をまとめて持つ、GameSessionを作ります。
- 「GameSession」スクリプトを作る
using System.Collections.Generic;
using UnityEngine;
// シーンをまたいで持ち越すもの(仲間・持ち物・お金)。ロビーのシーンに1つ置く
public class GameSession : MonoBehaviour
{
[SerializeField] private CharacterData[] startParty; // 最初の仲間(3人)
[SerializeField] private ItemStack[] startItems; // 最初の持ち物
[SerializeField] private int startGold = 50; // 最初のお金
// どのシーンからでも、GameSession.Instance で使えるようにする
public static GameSession Instance { get; private set; }
public List<PartyMember> Party { get; } = new List<PartyMember>();
public Inventory Inventory { get; } = new Inventory();
public int Gold { get; set; }
private void Awake()
{
// ロビーに戻ってくると、シーンの GameSession がもう1つ作られる。先にあるほうを使い、新しいほうは消す
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // シーンを切り替えても消さない
foreach (CharacterData data in startParty)
{
Party.Add(new PartyMember(data, 0));
}
foreach (ItemStack stack in startItems)
{
Inventory.Add(stack.item, stack.count);
}
Gold = startGold;
}
}
- Instance:
staticなので、どのシーンのスクリプトからでもGameSession.Instance.Goldのように使えます - 2つ目を消す:GameSessionはLobbyシーンに置くので、ロビーに戻ってくるたびに新しいGameSessionが作られます。先にあるほう(遊んできた中身が入っているほう)を残して、新しいほうは消します
- 最初の仲間・持ち物・お金はInspectorで決めます。今まで#16でBattleLineupに書いていた最初の持ち物は、ここに引っ越します
暗くしてシーンを切り替えよう

この節では、画面を黒くしてからシーンを切り替える仕組みを作ります。いきなりパッと切り替わるより、ずっと「場所を移った」感じが出ます!
SceneFader

画面いっぱいの黒い板(Image)を置いて、その濃さを変えます。
- 「SceneFader」スクリプトを作る
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
// シーンの切り替え:黒い板で暗くしてから読み込む。新しいシーンは、真っ黒から明るくして始める
[RequireComponent(typeof(Image))]
public class SceneFader : MonoBehaviour
{
[SerializeField] private float fadeSeconds = 0.4f; // 暗くする・明るくするのにかける時間
private Image image;
private void Awake()
{
image = GetComponent<Image>();
SetAlpha(1f); // シーンの始めは真っ黒
}
private async void Start()
{
try
{
await FadeAsync(0f);
}
catch (System.OperationCanceledException)
{
// 明るくしている途中でシーンが切り替わったら、そこで終わり
}
}
// 暗くしてから、sceneName のシーンを読み込む
public async Awaitable LoadSceneAsync(string sceneName)
{
await FadeAsync(1f);
await SceneManager.LoadSceneAsync(sceneName);
}
// fadeSeconds 秒かけて、黒い板の濃さを targetAlpha(0〜1)にする
private async Awaitable FadeAsync(float targetAlpha)
{
float startAlpha = image.color.a;
float time = 0f;
while (time < fadeSeconds)
{
time += Time.deltaTime;
SetAlpha(Mathf.Lerp(startAlpha, targetAlpha, time / fadeSeconds));
await Awaitable.NextFrameAsync(destroyCancellationToken);
}
SetAlpha(targetAlpha);
}
// 濃さを変える。透明なときは Image を切って、ボタンのクリックをじゃましないようにする
private void SetAlpha(float alpha)
{
Color color = image.color;
color.a = alpha;
image.color = color;
image.enabled = alpha > 0f;
}
}
- LoadSceneAsync:暗くし終わってから、
SceneManager.LoadSceneAsyncで次のシーンを読み込みます。LoadSceneAsyncはawaitで待てるので、読み込みが終わるまで待ってから先に進みます(6-16 待つ処理をすっきり書こう:async/awaitとAwaitable) - Awake・Start:新しいシーンは真っ黒で始めて、
Startでだんだん明るくします。前のシーンの最後(真っ黒)とつながるので、切り替わる瞬間が見えません - SetAlpha:透明なときは
image.enabled = falseにしています。黒い板は画面いっぱいなので、透明でも残っているとボタンのクリックを横取りしてしまうからです - シーンが切り替わると、SceneFaderも消えます。そのとき
destroyCancellationTokenで待つのが止まってOperationCanceledExceptionが出るので、catchで受け止めています
BattleシーンにSceneFadeを置きます。
- HierarchyのCanvasを右クリック>UI>Imageで作り、名前を「SceneFade」にする
- Canvasの一番下に置く(ほかのウインドよりも上に描かれるように)
- Rect TransformのAnchor Presetsで、Altを押しながら右下のstretchを選ぶ(画面いっぱい)
- ImageのColorを黒、A(アルファ)は0にする
- 「SceneFader」スクリプトを付ける
スライムColorのアルファを0にしておくのは、編集しているときにGame・Sceneビューが真っ黒にならないようにするためです・・・



再生すると、Awakeで真っ黒にしてから明るくなるのね。
バトルからロビーへ帰ろう


この節では、バトルをGameSessionの仲間で始めて、終わったら結果を書き戻してロビーへ戻るようにします。
BattleUnit


BattleUnitを、PartyMemberから作るように変えます。
まず、持ち越す仲間のデータを覚えておくプロパティを足します(Rowの下)。
public PartyMember Member { get; } // 持ち越す仲間のデータ(仲間だけ。敵は null)
仲間のデータから作るコンストラクタ(BattleUnit(CharacterData data, UnitView view, int totalExp))を、次のように書き換えます。
// 仲間から作る。経験値と今の HP・MP は、持ち越してきたもの
public BattleUnit(PartyMember member, UnitView view)
{
Name = member.Data.CharacterName;
characterData = member.Data;
Member = member;
TotalExp = member.TotalExp;
Level = member.Level;
Stats = member.Stats;
Hp = member.Hp;
Mp = member.Mp;
LearnSkills();
View = view;
view.Unit = this;
Commands = member.Data.Commands;
}
// バトルの結果(経験値・今の HP と MP)を、持ち越す仲間のデータに書き戻す
public void SaveToMember()
{
Member.TotalExp = TotalExp;
Member.Hp = Hp;
Member.Mp = Mp;
}
- HPとMPは、満タンではなく持ち越してきた値で始めます
- バトル中は、今までどおりBattleUnitのHPを減らします。PartyMemberに書き戻すのは、バトルが終わったときの1回だけです(
SaveToMember) - BattleUnitの中のことは、PartyMemberに書き戻さなければバトルの外に出ません。どく・せきかなどの状態や、ぼうぎょアップは、バトルが終わったら消えます
ゲージを、減る演出なしで出す


ステータスの行は、作ったときに満タン(StatusWindow.AddRow)で出して、そのあとで今のHPを入れています。今までは、バトルの始めはいつも満タンだったので問題ありませんでした。
ところが、HPを持ち越すと「満タン→12」と減ったことになり、#10で作った、遅れて縮む赤いゲージが動いてしまいます。シーンが始まったときに、ダメージを受けたように見えるのはおかしいですね。そこで、減る演出をせずに、すぐその長さにできるようにします。
GaugeのSetValueを、次のように書き換えます。
// isInstant を true にすると、減る演出をせずに、すぐその長さにする(シーンの始めに表示するとき)
public void SetValue(int newCurrent, int newMax, bool isInstant = false)
{
current = newCurrent;
max = newMax;
float newFill = max > 0 ? (float)current / max : 0f;
if (lag != null)
{
if (isInstant)
{
lag.fillAmount = newFill;
lagTimer = 0f;
}
else if (newFill < fill.fillAmount)
{
lagTimer = lagDelay; // 減ったら、白いゲージは少し待ってから縮む
}
else
{
lag.fillAmount = newFill; // 増えたときは、すぐにそろえる
}
}
fill.fillAmount = newFill;
valueText.text = $"{current}/{max}";
}
StatusRowのSetHp・SetMpにも、同じ引数を足して、Gaugeに渡します。
public void SetHp(int hp, int maxHp, bool isInstant = false)
{
hpGauge.SetValue(hp, maxHp, isInstant);
}
public void SetMp(int mp, int maxMp, bool isInstant = false)
{
mpGauge.SetValue(mp, maxMp, isInstant);
}
bool isInstant = falseのように、引数に= 値を書くと、呼ぶときに省略できます(省略したときはfalse)。バトル中のダメージや回復で呼んでいるところは、今までどおり演出つきのままで、直さなくて大丈夫ですtrueを渡すのは、シーンの始めに行を出すところ(このあとのBattleLineupのSetupと、ロビーのLobbyMenu)です
BattleLineup
BattleLineupは、GameSessionがあれば、その仲間と持ち物を使うようにします。
party・startItemsのコメントを「確認用」に直し、Inventoryを、あとから入れられるように変えます。
[SerializeField] private CharacterData[] party; // 確認用:Battle シーンから再生したときの3人
[SerializeField] private ItemStack[] startItems; // 確認用:Battle シーンから再生したときの持ち物
public Inventory Inventory { get; private set; } // 持ち物
Setupの、仲間を並べるところ(敵を並べるforより上)を、次のように書き換えます。#16で足した、最初の持ち物を入れるforeachは消します。
// 並べる(BattleManager が、バトルの始めに呼ぶ)
public void Setup()
{
List<PartyMember> members = LoadParty();
PartyUnits = new BattleUnit[members.Count];
StatusRows = new StatusRow[members.Count];
for (int i = 0; i < members.Count; i++)
{
PartyMember member = members[i];
UnitView view = partyViews[i];
view.SetupCharacter(member.Data);
StatusRow row = statusWindow.AddRow(member.Data);
BattleUnit unit = new BattleUnit(member, view);
unit.Row = row;
StatusRows[i] = row;
PartyUnits[i] = unit;
// レベルと能力を、ステータスの行に出す(持ち越した HP・MP は、減る演出をせずにそのまま)
row.SetLevel(unit.Level);
row.SetHp(unit.Hp, unit.Stats.maxHp, true);
row.SetMp(unit.Mp, unit.Stats.maxMp, true);
if (unit.IsDead)
{
view.ShowDown(); // ロビーから、倒れたまま来た
}
}
Setupの下に、仲間と持ち物を用意するメソッドを足します。
// ロビーから来たら、GameSession の仲間と持ち物を使う
// Battle シーンから再生したとき(GameSession が無い)は、確認用に Inspector の値で作る
private List<PartyMember> LoadParty()
{
if (GameSession.Instance != null)
{
Inventory = GameSession.Instance.Inventory;
return GameSession.Instance.Party;
}
List<PartyMember> members = new List<PartyMember>();
foreach (CharacterData data in party)
{
members.Add(new PartyMember(data, startExp));
}
Inventory = new Inventory();
foreach (ItemStack stack in startItems)
{
Inventory.Add(stack.item, stack.count);
}
return members;
}
- ロビーから来たときは、GameSessionの持ち物をそのまま使います。バトルでハーブティーを飲むと、GameSessionの持ち物が減るので、書き戻さなくても持ち越されます
- Battleシーンを開いて再生したときは、GameSessionがありません。そのときは今までどおり、Inspectorの3人・Start Exp・Start Itemsで始めます。バトルだけを確かめたいときに便利なので、残しておきます
- 倒れたままの仲間(にげたときなど)は、始めから倒れた絵にしておきます
BattleManager


BattleManagerに、SceneFaderの欄を足します(screenFadeの下)。using UnityEngine.SceneManagement;は、もう使わないので消します。
[SerializeField] private SceneFader sceneFader;
ShowVictoryAsyncで、もらったお金をGameSessionに足します(経験値のメッセージの下)。
await messageWindow.ShowAsync($"{exp}ポイントの けいけんちを かくとく!");
if (GameSession.Instance != null)
{
GameSession.Instance.Gold += gold; // お金は、持ち越すものに足す
}
ぜんめつしたときのウインドの文字を変えます。
await resultWindow.ShowAsync("ぜんめつ…", "おかねを はんぶん なくして\nロビーに もどされた");
最後に、RunAsyncのwhileを抜けたあと(#11で、同じシーンを読み込み直していたところ)を、次のように書き換えます。
// バトルの結果(経験値・HP・MP)を持ち越す
foreach (BattleUnit unit in lineup.PartyUnits)
{
unit.SaveToMember();
}
if (IsPartyDown())
{
// ぜんめつしたら、お金が半分になって、ロビーで目をさます(HP・MP は満タン)
foreach (BattleUnit unit in lineup.PartyUnits)
{
unit.Member.RestoreAll();
}
if (GameSession.Instance != null)
{
GameSession.Instance.Gold /= 2;
}
}
// 「ロビーへ」が押された:暗くして、ロビーに戻る
await sceneFader.LoadSceneAsync("Lobby");
- 勝ったとき・にげたとき・ぜんめつしたとき、どれも最後はここに来ます。まず3人の結果をPartyMemberに書き戻します
- ぜんめつしたら、ゲームオーバーにはせず、お金を半分なくして、満タンでロビーからやり直します。せっかく上げたレベル(経験値)は残るので、何度か挑戦すれば先に進めます
- 「お金が半分」は、休憩室の10ゴールドや、#18の売店の値段とのかね合いで決めた仮の値です。#24のバランス調整で見直します
Battleシーンを直します。
- BattleのBattle ManagerのScene Faderに、SceneFadeをドラッグする
- ResultWindowのRetryButtonの文字(Text (TMP))を「ロビーへ」にする
ロビーを作ろう


この節では、まほう図書館のロビーのシーンを作ります。
Battleを複製して、Lobbyにする
背景・仲間・メッセージ・ステータスのウインドなど、ロビーにもバトルと同じものがたくさんあります。そこで、Battleシーンを複製して、いらないものを消して作ります。
- ProjectのScenesフォルダでBattleを選び、Ctrl+Dで複製して、名前を「Lobby」にする
- Lobbyを開いて、Hierarchyから次のものを消す
- Canvasの外:Battle・Effects・TargetSelector
- Canvasの中:EnemyArea(Stageの中)・ItemWindow・SkillWindow・DamageVignette・PopupLayer・ResultWindow
- Backgroundの画像を bg_lobby にする(素材のzipに入っています)
- MessageWindowのMessageTextを「ここは まほう図書館の ロビー。」にする
- Stage>PartyAreaのPos Xを 800、Pos Yを 60 にする(部屋の真ん中あたりに立たせる)
- SceneFadeは、Battleから複製したものがそのまま使えます
メニューとお金のウインド


コマンドのウインドを、ロビーのメニューにします。
- CommandWindowの名前を「LobbyWindow」にして、Command Menuのコンポーネントを外す(Remove Component)
- PrefabsのCommandButtonを、LobbyWindowの中に5つドラッグして、名前と文字(Text (TMP))を次のようにする
- DungeonButton:地下へ
- RestButton:休憩室
- ShopButton:売店
- PartyButton:なかま
- SaveButton:セーブ
- ShopButton・PartyButton・SaveButtonは、ButtonのInteractableのチェックを外す(灰色になって押せない)
- LobbyWindowにはGrid Layout Groupが付いているので、2列に並びます
右上に、お金のウインドを作ります。
- Canvasを右クリック>UI>Imageで作り、名前を「GoldWindow」にする(StatusWindowのすぐ下に置く)
- Anchor Presetsで右上(top-right)を選び、Alt+Shiftを押しながらもう一度選ぶ(基準点も右上に)
- Pos X -60、Pos Y -180、Width 280、Height 90
- Source Imageに ui_window、Image Typeは Sliced
- GoldWindowを右クリック>UI>Text – TextMeshProで作り、名前を「GoldText」にする
- Anchor PresetsでAltを押しながらstretchを選び、Left・Rightを 32 にする
- 文字は「50 G」、Font Size 40、色は白、Alignmentは右寄せと上下の真ん中
GameSessionとLobbyMenu


ロビーのメニューを動かす、LobbyMenuを作ります。休憩室もここに書きます(次の節)。
- 「LobbyMenu」スクリプトを作る
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
// ロビー:仲間とお金を見せて、メニューのボタンで「地下へ」「休憩室」に行く
public class LobbyMenu : MonoBehaviour
{
[SerializeField] private UnitView[] partyViews; // PartyArea の3人
[SerializeField] private StatusWindow statusWindow;
[SerializeField] private TMP_Text goldText; // 右上のお金
[SerializeField] private MessageWindow messageWindow;
[SerializeField] private ScreenFade screenFade; // 休憩室で、画面を暗くする
[SerializeField] private SceneFader sceneFader;
[SerializeField] private Button dungeonButton; // 地下へ
[SerializeField] private Button restButton; // 休憩室
[SerializeField] private int restPrice = 10; // 休憩室のお金
[SerializeField] private float sleepSeconds = 2f; // 眠っている時間
private StatusRow[] rows;
private bool isBusy; // 休憩中・シーンの切り替え中は、ボタンを受け付けない
private void Start()
{
GameSession session = GameSession.Instance;
rows = new StatusRow[session.Party.Count];
for (int i = 0; i < session.Party.Count; i++)
{
PartyMember member = session.Party[i];
partyViews[i].SetupCharacter(member.Data);
rows[i] = statusWindow.AddRow(member.Data);
}
ShowParty();
dungeonButton.onClick.AddListener(OnDungeonClicked);
restButton.onClick.AddListener(OnRestClicked);
EventSystem.current.SetSelectedGameObject(dungeonButton.gameObject);
}
// 仲間のレベル・HP・MP と、お金を表示し直す(倒れている人は、戦闘不能の絵)
// HP・MP のゲージは、減る演出をせずにそのまま(バトルから戻ってきたとき、満タンから減って見えないように)
private void ShowParty()
{
GameSession session = GameSession.Instance;
for (int i = 0; i < session.Party.Count; i++)
{
PartyMember member = session.Party[i];
UnitView view = partyViews[i];
StatusRow row = rows[i];
row.SetLevel(member.Level);
row.SetHp(member.Hp, member.Stats.maxHp, true);
row.SetMp(member.Mp, member.Stats.maxMp, true);
if (member.IsDead)
{
view.ShowDown();
}
else
{
view.Revive();
}
}
goldText.text = $"{session.Gold} G";
}
private async void OnDungeonClicked()
{
if (isBusy)
{
return;
}
isBusy = true;
try
{
await sceneFader.LoadSceneAsync("Battle");
}
catch (System.OperationCanceledException)
{
// シーンが切り替わったら、そこで終わり
}
}
private async void OnRestClicked()
{
if (isBusy)
{
return;
}
isBusy = true;
try
{
await RestAsync();
}
catch (System.OperationCanceledException)
{
return;
}
isBusy = false;
}
}
- Start:GameSessionの仲間で、絵とステータスの行を作ります。ステータスのウインドは、バトルと同じStatusWindowです
- ShowParty:レベル・HP・MPとお金を出します。休憩室で満タンになったときも、これで出し直します。ゲージは
trueを渡して、減る演出なしで出します - isBusy:暗くなっている途中や、休憩している途中に、もう一度ボタンを押されないようにします
OnDungeonClickedは、シーンが切り替わったらそのまま終わるので、isBusyを戻しませんRestAsyncは、次の節で足します(ここではまだエラーになります)
GameSessionとLobbyを置きます。


- Hierarchyで、Canvasの外に空のオブジェクトを作り、名前を「GameSession」にして、「GameSession」スクリプトを付ける
- Start Partyに Slime・Fairy・Jiangshi、Start Itemsに HerbTea・3、Start Goldは 50
DontDestroyOnLoadは、親のいないオブジェクトにしか効きません。Canvasの中に入れないように気をつけましょう
- Canvasの外に空のオブジェクトをもう1つ作り、名前を「Lobby」にして、「LobbyMenu」スクリプトを付ける
- Party Viewsに、Stage>PartyAreaのSlime・Fairy・Jiangshi(Start Partyと同じ順番)
- ほかの欄に、上の図のようにドラッグする(Screen FadeはScreenFade、Scene FaderはSceneFadeです。名前が似ているので気をつけてください)
シーンの一覧に入れる


スクリプトからシーンを読み込むには、Build ProfilesのScene Listにシーンを入れておく必要があります(8-1 シーンの基本とゲーム構成の考え方)。
- File>Build Profilesを開き、左上のScene Listを選ぶ
- ProjectからLobbyとBattleをドラッグして、Lobbyを0番(一番上)にする
0番のシーンが、ビルドしたゲームで最初に開くシーンになります。
休憩室


この節では、お金を払って休むと、HPとMPが満タンになる休憩室を作ります。
うとうとするポーズ


素材のsleep_01・sleep_02を交互に出して、うとうとさせます。
CharacterDataに、うとうとする絵の欄を足します(eatFramesの下)。
[SerializeField] private Sprite[] sleepFrames; // うとうとする(sleep_01・02)
public Sprite[] SleepFrames => sleepFrames;
UnitViewにも、絵を覚えておく変数を足して(eatFramesの下)、SetupCharacterで入れます(eatFrames = data.EatFrames;の下)。
private Sprite[] sleepFrames; // うとうとする(仲間だけ)
sleepFrames = data.SleepFrames;
UnitViewに、うとうとするメソッドを足します(PlayEatAsyncの下)。
// 休憩室:正面を向いて、seconds 秒のあいだ うとうとする(sleep_01 と 02 を交互に)
public async Awaitable PlaySleepAsync(float seconds)
{
idleBounce.enabled = false;
float time = 0f;
int index = 0;
while (time < seconds)
{
image.sprite = sleepFrames[index % sleepFrames.Length];
index++;
await Awaitable.WaitForSecondsAsync(0.4f, destroyCancellationToken);
time += 0.4f;
}
}
- 待機の弾み(IdleBounce)を止めてから、0.4秒ごとに絵を変えます
- 終わったあとは、休憩のあとで喜ぶポーズ→
ResumeIdleで待機に戻ります


- Data/Charactersの4人(Slime・Fairy・Jiangshi・Medusa)のSleep Framesに、それぞれの sleep_01・sleep_02 を入れる
- Down Spriteで使っている sleep_03(倒れている絵)とは別です
RestAsync


LobbyMenuに、休憩室のメソッドを足します(OnRestClickedの下)。
// 休憩室:お金を払って眠ると、みんなの HP・MP が満タンになる(倒れた人も起きる)
private async Awaitable RestAsync()
{
GameSession session = GameSession.Instance;
if (session.Gold < restPrice)
{
await messageWindow.ShowAsync("おかねが たりない…");
return;
}
session.Gold -= restPrice;
goldText.text = $"{session.Gold} G";
await messageWindow.ShowAsync($"{restPrice}ゴールドで ひとやすみ。");
// 暗くしながら、みんなで うとうとする
Awaitable dark = screenFade.FadeAsync(0.6f, 0.5f);
Awaitable[] sleeps = new Awaitable[partyViews.Length];
for (int i = 0; i < partyViews.Length; i++)
{
sleeps[i] = partyViews[i].PlaySleepAsync(sleepSeconds);
}
await dark;
foreach (Awaitable sleep in sleeps)
{
await sleep;
}
await screenFade.FadeAsync(0f, 0.5f);
// 目がさめたら満タン。起きて喜ぶ
foreach (PartyMember member in session.Party)
{
member.RestoreAll();
}
ShowParty();
foreach (UnitView view in partyViews)
{
_ = view.PlayWinAsync(); // 喜ぶのは待たずに、メッセージと一緒に進める
}
await messageWindow.ShowAsync("みんな げんきに なった!");
foreach (UnitView view in partyViews)
{
view.ResumeIdle();
}
}
- お金が足りなければ、メッセージだけ出して終わります
- 部屋を暗くするのは、#11でぜんめつのときに使ったScreenFadeです。0.6まで暗くして、3人がうとうとし終わったら明るく戻します
- 暗くするのと、3人のうとうとは、先に全部始めてから待ちます。1つずつ
awaitすると、1人ずつ順番に眠ってしまいます - 満タンにするのは
PartyMember.RestoreAllです。倒れていた仲間もHPが満タンになるので、ShowPartyで起き上がった絵に戻ります



暗くなって、また明るくなると「ひと晩たった」感じがしますね・・・



起きたら3人で喜ぶのがいいわね!
動かして確認!


- Lobbyシーンを開いて再生する(GameSessionはLobbyにあるので、Lobbyから始めます)
- 「地下へ」でバトルへ。勝ったら「ロビーへ」で戻る
- もう一度「地下へ」を押して、減ったHPのまま始まることを確かめる。ロビーに戻ったときも、バトルが始まったときも、ゲージが満タンから減っていかない
- 「休憩室」で満タンにする。お金が10ゴールドより少ないと、休めません
- ぜんめつを試すときは、Data/Charactersの3人のBase StatsのMax Hpを5くらいにする(確かめたら戻す)
#17の動作確認
- Lobbyから再生すると、ロビーに3人が並び、右上に「50 G」、左下にメニューが出る(売店・なかま・セーブは灰色)
- 「地下へ」で画面が暗くなり、バトルが明るくなって始まる
- 勝つと「ロビーへ」のボタンが出て、押すと暗くなってロビーに戻る。もらったお金が右上に足される
- バトルで減ったHP・MP、上がったレベル、使ったどうぐは、ロビーでもそのまま。次のバトルにも持ち越される
- ロビーに戻ったとき・バトルが始まったときに、HPのゲージが満タンから減っていく演出は出ない(はじめから今のHPの長さ)
- 「休憩室」で10ゴールド減り、暗くなって3人がうとうとし、明るくなるとHP・MPが満タンになって喜ぶ
- ぜんめつすると「おかねを はんぶん なくして ロビーに もどされた」。ロビーではお金が半分、HP・MPは満タン
- Battleシーンを開いて再生したときは、今までどおりInspectorの3人・Start Exp・Start Itemsでバトルが始まる
あとがき
今回は、まほう図書館のロビーを作って、バトルとロビーを行き来できるようにしました。持ち越したいものだけをPartyMemberとGameSessionにまとめたので、バトルの中のしくみは、ほとんど変えずにすみましたね。暗くなってから切り替わるだけで、ぐっとゲームらしくなったと思います。次回は、ロビーの「売店」を作って、ためたお金でどうぐや装備を買えるようにしたいと思います!
コメント
コメント一覧 (1件)
[…] 前回:#17 ロビーと休憩室 […]