
バトルが長くなってくると、HPやMPが足りなくなったり、仲間が倒れたままになったりします。そんなときに頼りになるのが「どうぐ」!今回は、持ち物の一覧を開いて、ハーブティーやめざましどけいを使えるようにしたいと思います。使った仲間が飲んだり食べたりするポーズも付けますよ!
- シリーズの目次:ターン制RPGの作り方
- 前回:#15 状態異常と補助のまほう
今回作るもの

| どうぐ | 効果 | 使われた人のポーズ |
|---|---|---|
| ハーブティー | HPを30回復 | 飲む |
| まほうのクッキー | MPを10回復 | 食べる |
| どくけしキャンディ | どくを治す | 食べる |
| めざましどけい | 倒れた仲間を、HPを半分にして起こす | - |
- どうぐを、まほうと同じようにコマンドのデータにする(
ItemData) - 持ち物は、同じどうぐを個数でまとめる(
Inventory) - 「どうぐ」を選ぶと、持ち物の一覧が開く。名前は左、個数は右にそろえて出し、入りきらないときはスクロールする
- 前回お話しした「めざめの歌」(フェアリー Lv8)も、めざましどけいと一緒に作る
- どくけしキャンディを試せるように、本の虫の攻撃を「かみつく」(30%で どく)にする
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| ItemData | どうぐ1つ分のデータ(回復する量・治す状態・ポーズ) | (付けない) |
| BattleCommand | 仲間を1人選ぶ(倒れている仲間からも選べる) | - |
| BattleUnit・UnitView・CharacterData | MPの回復、状態を治す、起き上がる、飲む・食べるポーズ | - |
| ItemStack・Inventory | 持ち物(どうぐと個数) | (付けない) |
| CommandMenu | 一覧をスクロールする、ボタンの右はしに個数やMPを出す | ItemWindow・SkillWindow |
| ItemCommand | 「どうぐ」のコマンド。持ち物の一覧を開く | (付けない) |
| SkillCommand・MagicCommand | まほうの一覧の右はしに、使うMPを出す | (付けない) |
| ReviveSpellCommand | めざめの歌 | (付けない) |
| AttackCommand | 当たったときに、ときどき状態をかける | (付けない) |
どうぐのデータ

この節では、どうぐ1つ分のデータを作ります。
ItemData

どうぐも、まほうと同じく「選んで、相手を決めて、すばやさの順に使う」ものです。そこで、どうぐのデータはBattleCommandを受け継いで作ります。こうしておくと、#13 まほうとMPで作ったまほうの一覧(CommandMenu)に、そのまま並べられます。
- 「ItemData」スクリプトを作る
using UnityEngine;
// どうぐを使われた人のポーズ
public enum ItemPose
{
None, // ポーズなし
Drink, // 飲む(drink_01〜03)
Eat, // 食べる(eat_01・02)
}
// どうぐ1つ分のデータ。コマンドと同じように、相手を選んで使う
[CreateAssetMenu(fileName = "NewItem", menuName = "TurnBasedRpg/Item")]
public class ItemData : BattleCommand
{
[SerializeField] private int hpAmount; // 回復する HP
[SerializeField] private int mpAmount; // 回復する MP
[SerializeField] private StatusEffectData cureEffect; // 治す状態(どくけしキャンディは どく)
[SerializeField] private bool revives; // チェックすると、倒れた仲間を起こす(めざましどけい)
[SerializeField] private ItemPose pose; // 使われた人のポーズ
[SerializeField] private ParticleSystem effectPrefab; // 使われた人のところに出す
public override async Awaitable<BattleAction> SelectAsync(BattleUnit user, BattleContext context)
{
// めざましどけい は倒れている仲間から、ほかは立っている仲間から選ぶ
return await SelectPartyMemberAsync(context, revives);
}
public override async Awaitable ExecuteAsync(BattleUnit user, BattleAction action, BattleContext context)
{
BattleUnit target = action.Target.Unit;
if (!revives && target.IsDead)
{
target = user; // 選んだ人が先に倒れていたら、自分に使う
}
// 同じターンに、ほかの仲間が最後の1つを使っていたら、もう無い
if (!context.Inventory.Remove(this))
{
await context.Message.ShowAsync($"{user.Name}は {CommandName}を さがしたが、もう なかった!");
return;
}
await context.Message.ShowAsync($"{user.Name}は {CommandName}を つかった!");
if (revives)
{
if (!target.IsDead)
{
await context.Message.ShowAsync("しかし なにも おこらなかった。");
return;
}
target.Revive();
PlayEffect(target.View);
await context.Effects.PlayHealAsync(target.View, target.Hp);
await context.Message.ShowAsync($"{target.Name}は めを さました!");
return;
}
// 使われた人が、飲む・食べる
if (pose == ItemPose.Drink)
{
await target.View.PlayDrinkAsync();
}
else if (pose == ItemPose.Eat)
{
await target.View.PlayEatAsync();
}
PlayEffect(target.View);
string message = "しかし なにも おこらなかった。";
if (hpAmount > 0)
{
int amount = target.Heal(hpAmount);
await context.Effects.PlayHealAsync(target.View, amount);
message = $"{target.Name}の HPが {amount} かいふくした!";
}
if (mpAmount > 0)
{
int amount = target.RestoreMp(mpAmount);
await context.Effects.PlayMpHealAsync(target.View, amount);
message = $"{target.Name}の MPが {amount} かいふくした!";
}
if (cureEffect != null && target.RemoveEffect(cureEffect))
{
context.Effects.ShowText(target.View, "なおった!", Color.white);
await Awaitable.WaitForSecondsAsync(0.5f);
message = $"{target.Name}の {cureEffect.EffectName}が なおった!";
}
target.View.ResumeIdle();
await context.Message.ShowAsync(message);
}
// 使われた人のところに、パーティクルを出す
private void PlayEffect(UnitView target)
{
if (effectPrefab != null)
{
Instantiate(effectPrefab, target.transform.position, Quaternion.identity);
}
}
}
- ItemPose:ポーズの種類に名前を付けたenumです(6-15 選択肢に名前を付けよう:enum(列挙型)とswitch文)。Inspectorでは、None・Drink・Eatから選べるリストになります
- どうぐの種類ごとにクラスを作らない:4つのどうぐは「回復する量」「治す状態」「起こすか」がちがうだけなので、#15の状態と同じく、1つのデータの形にまとめました
- もう無いとき:スライムちゃんとフェアリーが、同じターンに最後の1つを選ぶことがあります。使うときに
Removeして、もう無ければメッセージだけ出します - ポーズは使われた人:フェアリーがスライムちゃんにハーブティーを使ったら、飲むのはスライムちゃんです
- メッセージ:最後に起きたことを1つ出します。何も起きなかったとき(どくではない人に、どくけしキャンディを使ったとき)は「しかし なにも おこらなかった。」のままです
ItemDataが使うものを足す

ItemDataの中で使っている、仲間を選ぶ・起き上がる・飲む・食べるなどを足していきます。
BattleCommandに、仲間を1人選ぶメソッドを足します(FindTargetの上)。1行目にはusing System.Collections.Generic;を足してください。
// 仲間を1人選ぶ。isDown が true なら倒れている仲間から、false なら立っている仲間から
// 選べる人がいなければ、メッセージを出して null を返す
protected async Awaitable<BattleAction> SelectPartyMemberAsync(BattleContext context, bool isDown)
{
List<UnitView> candidates = new List<UnitView>();
foreach (UnitView view in context.Party)
{
if (view.Unit.IsDead == isDown)
{
candidates.Add(view);
}
}
if (candidates.Count == 0)
{
await context.Message.ShowAsync("つかえる なかまが いない!");
return null;
}
context.MessageText.text = "だれに つかう?";
UnitView target = await context.TargetSelector.SelectTargetAsync(candidates.ToArray(), context.Enemies);
if (target == null)
{
return null;
}
return new BattleAction(this, target);
}
view.Unit.IsDead == isDownで、倒れている人だけ・立っている人だけを集めています- だれも倒れていないのにめざましどけいを選んだときは、「つかえる なかまが いない!」で選び直しになります
BattleUnitに、MPの回復・状態を治す・起き上がるを足します(GainExpの上)。
// MP を回復する(最大MPより上にはしない)。実際に増えた量を返す
public int RestoreMp(int amount)
{
int before = Mp;
Mp = Mathf.Min(Stats.maxMp, Mp + amount);
return Mp - before;
}
// 状態を治す。かかっていたら true
public bool RemoveEffect(StatusEffectData data)
{
foreach (ActiveEffect effect in Effects)
{
if (effect.Data == data)
{
Effects.Remove(effect);
ShowEffects();
return true; // 消したらすぐ抜けるので、foreach の途中で Remove してもエラーにならない
}
}
return false;
}
// 倒れた人を起こす:HP を最大の半分にして、見た目を待機に戻す
public void Revive()
{
Hp = Mathf.Max(1, Stats.maxHp / 2);
View.Revive();
}
CharacterDataに、飲む・食べるポーズの欄を足します(bookFramesの下と、BookFramesの下)。
[SerializeField] private Sprite[] drinkFrames; // 飲む(drink_01・02・03)
[SerializeField] private Sprite[] eatFrames; // 食べる(eat_01・02)
public Sprite[] DrinkFrames => drinkFrames;
public Sprite[] EatFrames => eatFrames;
UnitViewに、変数を足して(bookFramesの下)、SetupCharacterの最後で受け取ります。
private Sprite[] drinkFrames; // 飲む(仲間だけ)
private Sprite[] eatFrames; // 食べる(仲間だけ)
drinkFrames = data.DrinkFrames;
eatFrames = data.EatFrames;
UnitViewに、起き上がる(ShowDownの下)と、飲む・食べる(PlayBookAsyncの下)を足します。
// 起き上がる:戦闘不能の絵をやめて、待機の弾みに戻す
public void Revive()
{
isDown = false;
idleBounce.enabled = true;
SetDimmed(isDimmed);
}
// どうぐ:正面を向いて、飲む
public async Awaitable PlayDrinkAsync()
{
await PlayFramesAsync(drinkFrames);
}
// どうぐ:正面を向いて、食べる
public async Awaitable PlayEatAsync()
{
await PlayFramesAsync(eatFrames);
}
- 待機の弾み(IdleBounce)を動かすと、次のコマから待機の絵に戻ります。
SetDimmedで、暗い色も元に戻しています
BattleEffectsに、MPの回復の数字を足します。欄はhealColorの下、メソッドはPlayHealAsyncの下です。
[SerializeField] private Color mpColor = new Color(0.45f, 0.75f, 1f); // MP の回復の数字の色(水色)
// MP を回復した:ステータスのゲージを増やして、水色の数字を出す
public async Awaitable PlayMpHealAsync(UnitView target, int amount)
{
BattleUnit unit = target.Unit;
if (unit.Row != null)
{
unit.Row.SetMp(unit.Mp, unit.Stats.maxMp);
}
ShowText(target, $"+{amount}", mpColor);
await Awaitable.WaitForSecondsAsync(0.5f, destroyCancellationToken);
}
- HPの回復は緑、MPの回復は水色にして、どちらが増えたか分かるようにしています(MPのゲージと同じ青系)
データを作ろう

- 4人のCharacterData(Data/Characters)に、ポーズの絵を入れる
- Drink Frames:
〇〇_drink_01・02・03(〇〇は slime・fairy・jiangshi・medusa) - Eat Frames:
〇〇_eat_01・02
- Drink Frames:
- TurnBasedRpg/Dataに「Items」フォルダを作り、Create>TurnBasedRpg>Item で4つ作る
| ファイル | Command Name | Hp Amount | Mp Amount | Cure Effect | Revives | Pose | Effect Prefab |
|---|---|---|---|---|---|---|---|
| HerbTea | ハーブティー | 30 | 0 | None | - | Drink | HealSparkles |
| MagicCookie | まほうのクッキー | 0 | 10 | None | - | Eat | HealSparkles |
| AntidoteCandy | どくけしキャンディ | 0 | 0 | Poison | - | Eat | HealSparkles |
| AlarmClock | めざましどけい | 0 | 0 | None | ✓ | None | HealSparkles |

持ち物を作ろう

この節では、持っているどうぐと、その個数を覚えておく「持ち物」を作ります。
ItemStackとInventory
ハーブティーを3つ持っていたら、一覧に3行並べるのではなく、「ハーブティー × 3」の1行にまとめたいですね。そこで、どうぐと個数をセットにして持ちます。
- 「ItemStack」スクリプトを作る
// どうぐ1種類と、その個数(Inspector で最初の持ち物を書くのにも使う)
[System.Serializable]
public class ItemStack
{
public ItemData item;
public int count;
}
[System.Serializable]を付けると、Inspectorで中身を書けるようになります。#14のSkillLearningと同じです
- 「Inventory」スクリプトを作る
using System.Collections.Generic;
// 持ち物:どうぐと個数のリスト。同じどうぐは1つにまとめて、個数で数える
public class Inventory
{
private readonly List<ItemStack> stacks = new List<ItemStack>();
// 持っているどうぐの種類の数
public int Count => stacks.Count;
// どうぐを count 個ふやす(もう持っていたら、個数を足す)
public void Add(ItemData item, int count)
{
foreach (ItemStack stack in stacks)
{
if (stack.item == item)
{
stack.count += count;
return;
}
}
ItemStack newStack = new ItemStack();
newStack.item = item;
newStack.count = count;
stacks.Add(newStack);
}
// どうぐを1つへらす(0になったら、リストから消す)。持っていなければ false
public bool Remove(ItemData item)
{
for (int i = 0; i < stacks.Count; i++)
{
ItemStack stack = stacks[i];
if (stack.item == item)
{
stack.count--;
if (stack.count <= 0)
{
stacks.RemoveAt(i);
}
return true;
}
}
return false;
}
// 一覧に並べるどうぐ
public ItemData[] Items()
{
ItemData[] items = new ItemData[stacks.Count];
for (int i = 0; i < stacks.Count; i++)
{
items[i] = stacks[i].item;
}
return items;
}
}
- Add:もう持っているどうぐなら個数を足し、持っていなければ新しい行を足します。Inspectorに書いた最初の持ち物をそのまま使わず、
new ItemStack()で作り直しているので、遊んで個数が減ってもInspectorの値は変わりません - Remove:個数が0になったら、その行をリストから消します。一覧から消えるので、0個のどうぐは選べません
- Listの使い方は6-13 数が変わるデータを扱おう:Listを見てください
- ボタンに出す個数は、あとの「個数とMPを右はしにそろえる」で足します
- Inventoryは
MonoBehaviourではない、ふつうのクラスです。次の#17で、シーンをまたいで持ち越せるようにします
最初の持ち物

BattleLineupに、最初の持ち物の欄と、持ち物を足します。欄はstartExpの下、プロパティはPartyViewsの下です。
[SerializeField] private ItemStack[] startItems; // 最初の持ち物(#17 からは、ロビーから持ち越す)
public Inventory Inventory { get; } = new Inventory(); // 持ち物
Setupの、敵を並べるforの上に、最初の持ち物を入れる行を足します。
foreach (ItemStack stack in startItems)
{
Inventory.Add(stack.item, stack.count);
}
BattleContextに、どうぐの一覧と持ち物を足します(SkillMenuの下)。
public CommandMenu ItemMenu; // どうぐの一覧
public Inventory Inventory; // 持ち物
BattleManagerにも欄を足して(skillMenuの下)、StartBattleAsyncでContextに渡します(context.SkillMenu = skillMenu;の下)。
[SerializeField] private CommandMenu itemMenu; // どうぐの一覧(ItemWindow)
context.ItemMenu = itemMenu;
context.Inventory = lineup.Inventory;
- BattleのBattle LineupのStart Itemsを4つにして、上の図のように入れる(確認用。持ち物の画面は#17で作ります)
どうぐの一覧を開こう

この節では、「どうぐ」を選んだら持ち物の一覧を開くようにします。
一覧のウインドは、#13のまほうの一覧(SkillWindow)と同じ大きさだと、3行しか入りません。どうぐが増えてくると、見渡しづらいですね。それに、まほうも、メドゥーサはLv7で4つ目を覚えて、ウインドからはみ出してしまいます・・・。そこで、ウインドを縦に大きくして6行見えるようにし、それでも入りきらないときはScroll Viewでスクロールできる一覧にします(9-6 Scroll ViewとLayout Groupでリストを作ろう)。
- GIFの「ためしの どうぐ1〜4」は、スクロールを確かめるために足したものです(作り方は「動かして確認!」)
一覧のウインド

- Canvasの子にItemWindow(UI (Canvas)>Scroll View)
- StatusWindowのすぐ下に置く
- アンカー:bottom-left、Pos X:690、Pos Y:524、Width:560、Height:448(ステータスのウインドのすぐ上から、6行入る高さ)
- Image:Source Image ui_window、Image Type:Sliced、Colorは白(Aも255)
- Scroll Rect:Horizontalのチェックを外す、Movement Type:Clamped、Scroll Sensitivity:20、VerticalのVisibility:Auto Hide
- Add Component>CommandMenu、Button PrefabにPrefabs/CommandButton、Scroll RectにItemWindow自身
- 子のScrollbar Horizontalは消す(横にはスクロールしないので)
- 子のViewport:Left 24、Top 24、Right 44、Bottom 24(右はスクロールバーの分だけ広く空ける)
- Viewportの子のContent
- Add Component>Grid Layout Group:Cell Size X 492・Y 60、Spacing Y 8、Constraint:Fixed Column Count、Constraint Count:1
- Add Component>Content Size Fitter:Vertical Fit:Preferred Size(ボタンの数に合わせて、Contentの高さが変わる)
- 子のScrollbar Vertical
- Pos X:-16、Top:24、Width:12、Bottom:24
- Image(背景)のColor:黒、A 90ぐらい。子のHandleのColor:うすい黄色(#F2E3B3)
- Scrollbar:NavigationをNoneにする
- Automaticのままだと、キーボードの→でスクロールバーが選ばれてしまいます
- ItemWindowのチェックを外して、隠しておく


まほうの一覧も、同じ形に作り直します。
- 前のSkillWindowを消す
- ItemWindowを複製(Ctrl+D)して、名前をSkillWindowにする
- 複製すると、Command MenuのScroll Rectは、ちゃんと複製したSkillWindow自身になります
- BattleのBattle ManagerのSkill MenuにSkillWindow、Item MenuにItemWindowをドラッグ
CommandMenuでスクロールする

CommandMenuに、Scroll Rectの欄を足します(buttonPrefabの下)。
[SerializeField] private ScrollRect scrollRect; // 一覧をスクロールするとき(まほう・どうぐ)。コマンドのウインドは空のまま
キーボードやゲームパッドで選ぶボタンが変わったら、そのボタンが見えるところまでスクロールします。Updateの最後のelseを、次のように書き換えます。
else
{
if (scrollRect != null && current != lastSelected)
{
ScrollTo((RectTransform)current.transform); // 選ぶボタンが変わったら、見えるところまでスクロール
}
lastSelected = current;
}
CreateButtonsの、buttons.Clear();の下に、ボタンの親を決める行を足します。
// スクロールする一覧は Content の子に、そうでなければ自分の子にボタンを作る
Transform parent = transform;
if (scrollRect != null)
{
parent = scrollRect.content;
scrollRect.content.anchoredPosition = Vector2.zero; // 一覧の一番上から見せる
}
- ボタンを作る行の
Instantiate(buttonPrefab, transform)は、Instantiate(buttonPrefab, parent)に変えます(この下の「個数とMPを右はしにそろえる」で、CreateButtonsの全体をのせます)
CreateButtonsの下に、ScrollToを足します。
// item のボタンが、見える枠(Viewport)からはみ出していたら、入るところまで一覧を動かす
private void ScrollTo(RectTransform item)
{
RectTransform viewport = scrollRect.viewport;
Vector3[] corners = new Vector3[4]; // 0:左下、1:左上、2:右上、3:右下
item.GetWorldCorners(corners);
float bottom = viewport.InverseTransformPoint(corners[0]).y; // ボタンの下(Viewport から見た高さ)
float top = viewport.InverseTransformPoint(corners[1]).y; // ボタンの上
Rect view = viewport.rect;
if (top > view.yMax)
{
scrollRect.content.anchoredPosition -= new Vector2(0f, top - view.yMax); // 上にはみ出した:一覧を下げる
}
else if (bottom < view.yMin)
{
scrollRect.content.anchoredPosition += new Vector2(0f, view.yMin - bottom); // 下にはみ出した:一覧を上げる
}
}
- ボタンの親:Scroll Viewでは、ボタンをContentの子に並べます。Scroll Rectが空のとき(コマンドのウインド)は、今までどおり自分の子です
- ScrollTo:
GetWorldCornersでボタンの四すみの位置を取り、InverseTransformPointでViewportから見た高さに直しています。はみ出した分だけContentを動かすので、↓キーで下へ進むと、1行ずつスクロールします - マウスのホイールでのスクロールは、Scroll Rectがやってくれます
スライムScroll Viewは、マウスで動かすぶんには何もしなくていいんですが、キーボードで選んだときは自分でスクロールさせる必要があります・・・



ゲームパッドで遊ぶ人もいるから、ここは大事ね!
個数とMPを右はしにそろえる


「ハーブティー ×3」のように、名前と個数を1つの文字にしてしまうと、名前の長さで個数の位置がずれてしまいます。それに、×9から×10に増えたときにも、見え方が変わってしまいますね。そこで、名前は左につめて、個数は右はしに、2けたの幅でそろえて出します。
まほうの一覧も、#13から「ヒール 3」のように名前とMPを1つの文字にしていたので、同じように直します。
ボタンの文字を2つに分けるので、一覧用のボタンのプレハブを作ります。
- Prefabs/CommandButtonを複製(Ctrl+D)して、名前をListButtonにする
- ListButtonを開いて、子のText (TMP)
- Alignment:左(Left)
- Extra SettingsのMarginsのLeft:44(▶のカーソルの分だけ空ける)
- Text (TMP)を複製(Ctrl+D)して、名前をValueTextにする(ListButtonの一番下の子にする)
- Textは空にする
- Alignment:右(Right)
- MarginsのLeftは0に戻して、Right:16
- ItemWindowとSkillWindowのCommand MenuのButton Prefabを、ListButtonに変える(コマンドのウインドはCommandButtonのまま)
Inventoryに、ボタンに出す個数を作るメソッドを足します(Itemsの下)。
// ボタンの右はしに出す個数(「× 3」「×12」のように、2けたの幅にそろえる)
public string[] Counts()
{
string[] counts = new string[stacks.Count];
for (int i = 0; i < stacks.Count; i++)
{
counts[i] = $"×<mspace=0.6em>{stacks[i].count,2}</mspace>";
}
return counts;
}
{stacks[i].count,2}:,2を付けると、2文字の幅になるように、左に空白を足します。3なら「 3」、12なら「12」です<mspace=0.6em>:TextMeshProのタグで、間の文字をすべて同じ幅(0.6文字分)で並べます。空白も数字と同じ幅になるので、「× 3」と「×12」の×と1の位が、縦にそろいます
CommandMenuのWaitForCommandAsyncに、右はしに出す文字を渡せるようにします。最初の4行を、次のように書き換えます。
// user のコマンドでボタンを作り、どれかが押されるまで待つ(キャンセルなら null)
// values を渡すと、ボタンの右はしにそれを出す(どうぐの個数、まほうの MP)
public async Awaitable<BattleCommand> WaitForCommandAsync(BattleCommand[] commands, BattleUnit user, string[] values = null)
{
// 前の人の決定(Enter など)が、まだこのフレームに残っていることがあるので、1フレーム待ってから始める
await Awaitable.NextFrameAsync(destroyCancellationToken);
CreateButtons(commands, user, values);
string[] values = nullのように= nullを付けると、渡さなくてもよい引数になります。コマンドのウインドは今までどおり2つだけ渡せばOKです
CreateButtonsは、次のようになります。
// コマンドの数だけボタンを作る(前のボタンは消す)。user が使えないコマンドは灰色にする
private void CreateButtons(BattleCommand[] commands, BattleUnit user, string[] values)
{
foreach (Button oldButton in buttons)
{
// Destroy はフレームの最後に消えるので、先に隠して、並びから外しておく
oldButton.gameObject.SetActive(false);
Destroy(oldButton.gameObject);
}
buttons.Clear();
// スクロールする一覧は Content の子に、そうでなければ自分の子にボタンを作る
Transform parent = transform;
if (scrollRect != null)
{
parent = scrollRect.content;
scrollRect.content.anchoredPosition = Vector2.zero; // 一覧の一番上から見せる
}
for (int i = 0; i < commands.Length; i++)
{
BattleCommand command = commands[i];
Button button = Instantiate(buttonPrefab, parent);
button.GetComponentInChildren<TMP_Text>().text = command.MenuLabel;
if (values != null)
{
// 一覧のボタン(ListButton)は、右はしの ValueText に出す
button.transform.Find("ValueText").GetComponent<TMP_Text>().text = values[i];
}
button.interactable = command.CanUse(user);
button.onClick.AddListener(() => Choose(command));
buttons.Add(button);
}
}
- 何番目の文字かを使うので、
foreachをforに変えています GetComponentInChildren<TMP_Text>()は、上から探して最初に見つかったText (TMP)を返します。ValueTextを一番下の子にしたのは、こちらが名前のほうにならないようにするためですtransform.Find("ValueText")で、子の中から名前でValueTextを探しています


まほうのMPも、右はしに出します。SkillCommandの、ボタンの文字を決めていた2行(MenuLabel)を、次のように書き換えます。
// 一覧の右はしに出す MP(「MP 3」「MP12」のように、2けたの幅にそろえる)
public string MpText => $"MP<mspace=0.6em>{mpCost,2}</mspace>";
- ボタンの左の文字は、BattleCommandの
MenuLabel(まほうの名前だけ)に戻ります
MagicCommandで、MPの文字を作って一覧に渡します。WaitForCommandAsyncの行を、次のように書き換えます。
string[] mpTexts = new string[user.Skills.Count];
for (int i = 0; i < user.Skills.Count; i++)
{
mpTexts[i] = user.Skills[i].MpText;
}
BattleCommand skill = await context.SkillMenu.WaitForCommandAsync(user.Skills.ToArray(), user, mpTexts);
- どうぐの
Countsと同じく、ボタンと同じ順番で、右はしに出す文字を並べています
どうぐのコマンド


#4 コマンドをデータにして、メニューに並べようで、中身を空のままにしていた「どうぐ」のコマンドを作ります。ItemCommandを、次のように書き換えます。
using UnityEngine;
// どうぐ:持ち物の一覧を開いて、選んだどうぐの相手を選ぶ
[CreateAssetMenu(fileName = "Item", menuName = "TurnBasedRpg/Commands/Item")]
public class ItemCommand : BattleCommand
{
public override async Awaitable<BattleAction> SelectAsync(BattleUnit user, BattleContext context)
{
while (true)
{
if (context.Inventory.Count == 0)
{
await context.Message.ShowAsync("どうぐを なにも もっていない!");
return null; // コマンドを選び直す
}
context.MessageText.text = $"どの {CommandName}を つかう?";
context.ItemMenu.gameObject.SetActive(true);
BattleCommand item = await context.ItemMenu.WaitForCommandAsync(context.Inventory.Items(), user, context.Inventory.Counts());
context.ItemMenu.gameObject.SetActive(false);
if (item == null)
{
return null; // 一覧でキャンセル:コマンドを選び直す
}
BattleAction action = await item.SelectAsync(user, context);
if (action != null)
{
return action; // どうぐと相手が決まった
}
// 相手を選ぶところでキャンセルしたら、もう一度どうぐの一覧を開く
}
}
}
- #13のMagicCommandとほとんど同じ形です。ちがうのは、一覧に持ち物を並べることと、個数を渡すことだけ
ItemData[]は、そのままBattleCommand[]として渡せます(ItemDataはBattleCommandを受け継いでいるので)- 何も持っていないときは、一覧を開かずにメッセージを出して、コマンドを選び直します
倒れた仲間を起こそう


この節では、めざましどけいと同じく倒れた仲間を起こす、フェアリーのまほう「めざめの歌」を作ります。
- Scripts/Commandsに「ReviveSpellCommand」スクリプトを作る
using UnityEngine;
// 倒れた仲間を起こすまほう(めざめの歌)
[CreateAssetMenu(fileName = "NewReviveSpell", menuName = "TurnBasedRpg/Skills/Revive Spell")]
public class ReviveSpellCommand : SkillCommand
{
public override async Awaitable<BattleAction> SelectAsync(BattleUnit user, BattleContext context)
{
return await SelectPartyMemberAsync(context, true); // 倒れている仲間から選ぶ
}
public override async Awaitable ExecuteAsync(BattleUnit user, BattleAction action, BattleContext context)
{
BattleUnit target = action.Target.Unit;
SpendMp(user);
await context.Message.ShowAsync($"{user.Name}は {CommandName}を うたった!");
await user.View.PlayCastAsync();
if (!target.IsDead)
{
// 同じターンに、ほかの仲間が先に起こしていた
user.View.ResumeIdle();
await context.Message.ShowAsync("しかし なにも おこらなかった。");
return;
}
target.Revive();
PlayEffect(target.View);
await context.Effects.PlayHealAsync(target.View, target.Hp);
user.View.ResumeIdle();
await context.Message.ShowAsync($"{target.Name}は めを さました!");
}
}
- 相手を選ぶのは、ItemDataと同じ
SelectPartyMemberAsyncです。BattleCommandに書いておいたので、まほうからも使えます - 起こすところ(
Revive→パーティクル→緑の数字)も、めざましどけいと同じです
データを作ります。
- Data/Skillsに、Create>TurnBasedRpg>Skills>Revive Spell で「WakeSong」
- Command Name:めざめの歌、Mp Cost:10、Effect Prefab:HealSparkles
- フェアリーのSkillsに、Lv8 WakeSongを足す


どくの かみつく


この節では、どくけしキャンディを試せるように、本の虫の攻撃を「かみつく」にします。当たったときに、30%で どくにする攻撃です。
AttackCommandに、欄を2つ足します(クラスの最初)。
[SerializeField] private StatusEffectData addEffect; // 当たったときに、ときどきかける状態(本の虫の かみつく は どく)
[SerializeField, Range(0, 100)] private int effectChance; // かける確率(%)
ExecuteAsyncの最後のif (target.IsDead)に、else ifを足します。
if (target.IsDead)
{
await ShowFallAsync(target, context);
}
else if (addEffect != null && GameRandom.Range(0f, 100f) < effectChance)
{
target.AddEffect(addEffect);
context.Effects.ShowText(target.View, addEffect.EffectName, addEffect.Color);
await context.Message.ShowAsync($"{target.Name}は {addEffect.EffectName}に なった!");
}
- かける確率は、#15のStatusSpellCommandと同じ形です
- Add Effectが空のとき(今までの「たたかう」)は、何も変わりません
データを作ります。
- Data/CommandsのAttackを複製(Ctrl+D)して、名前を「Bite」にする
- Command Name:かみつく、Add Effect:Poison、Effect Chance:30
- Data/EnemiesのBookwormのCommandsを、AttackからBiteに変える


- 仲間のどくも、#15と同じく、ターンのおわりに最大HPの4分の1のダメージです。かなり痛いので、どくけしキャンディの出番ですね
動かして確認!


- Start Itemsを、「最初の持ち物」の図のように4種類にしておく
- スクロールを試すときは、Data/ItemsのHerbTeaを4つ複製(Ctrl+D)して、TestItem1〜4(Command Name:ためしの どうぐ1〜4)を作り、Start Itemsの下に1つずつ足す(全部で8種類)
- たたかって、HPやMPが減ったら、ハーブティー・まほうのクッキーを使う
- 本の虫に かみつかれて どくになったら、どくけしキャンディを使う
- だれかが倒れたら、めざましどけいを使う
- めざめの歌は、Battle LineupのStart Expを190(Lv8)にすると、フェアリーが覚えています
#16の動作確認
- 「どうぐ」で持ち物の一覧が開き、名前は左、「× 3」のような個数は右はしにそろって出る
- 「まほう」の一覧も、名前は左、「MP 3」のようなMPは右はしにそろって出る
- ためしのどうぐを足して8種類にすると、↓キーで7つ目を選んだところで、一覧がスクロールする
- ハーブティーで飲む、クッキー・キャンディで食べるポーズになり、HPは緑・MPは水色の数字が出る
- 使うと個数が減り、0になると一覧から消える
- めざましどけい・めざめの歌で、倒れた仲間がHP半分で起き上がり、次のターンからコマンドを選べる
- 確かめたら、Start Expを0に、Start Itemsをハーブティー×3だけに戻す(TestItem1〜4は消してOK)
あとがき
今回は、どうぐをコマンドのデータにして、持ち物の一覧から使えるようにしました。まほうと同じBattleCommandを受け継いだので、相手を選ぶところや、すばやさの順に使うところは、何も足さずに動きましたね。次回は、図書館のロビーを作って、バトルで減ったHPや持ち物を持ち越せるようにしたいと思います!
コメント
コメント一覧 (1件)
[…] 前回:#16 どうぐとインベントリ […]