
前回、3人の仲間のコマンドを順番に選べるようになりました。ただ、今のままだと「今だれのコマンドを選んでいるのか」が、上のメッセージを読まないと分かりません。RPGのバトルでは、画面のどこを見ればいいか、だれの番なのかが、ひと目で分かることが大事です。今回は、選んでいる仲間を光らせて点滅させ、ほかの仲間を暗くして、静止画でもパッと分かるようにしてみたいと思います!
- シリーズの目次:ターン制RPGの作り方
- 前回:#4 コマンドをデータにして、メニューに並べよう
今回作るもの
- 選んでいる仲間を、光らせて点滅させる
- ほかの仲間を暗くする
- ステータスのウインドの名前を黄色にする
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| UnitView | 光らせる・暗くするメソッドを足す | 仲間3人・敵のプレハブ |
| StatusRow | 名前を黄色くするメソッドを足す | StatusRowのプレハブ |
| BattleLineup | 選んでいる人を光らせ、ほかを暗くする | Battle |
選んでいる仲間を目立たせよう
だれのコマンドを選んでいるのかが、静止画でもひと目で分かるように、2つのことをします。
- 選んでいる仲間を光らせて点滅させる
- ほかの仲間を暗くする

光らせるのは、光らせたい人にだけ「光って」「やめて」と言えばいい形にします。あとで全体まほうのように何人もまとめて選ぶときも、対象の全員に「光って」と言うだけで済みます。暗くするのは、まわりを少し落ち着かせて、光っている人に目が行くようにするためです。
白く光らせるマテリアル
UIのImageのColorは「絵の色に掛け算」なので、白(255,255,255)にしても元の絵のままで、白くは光りません。そこで、絵の形だけを使って1色で塗るマテリアルを使います。
- Projectウインドの TurnBasedRpg/Effects で右クリック>Create>Material、名前をWhiteFlash
- Shader:GUI/Text Shader

UnitViewに、白く光る絵を重ねる
UnitViewを、次のように書き換えます。
using UnityEngine;
using UnityEngine.UI;
// 仲間や敵の見た目。データを受け取って、絵を表示する
public class UnitView : MonoBehaviour
{
[SerializeField] private Image image;
[SerializeField] private IdleBounce idleBounce; // 仲間だけ。敵は空のまま
[SerializeField] private Material flashMaterial; // 白く光らせるマテリアル(WhiteFlash)
[SerializeField] private Color flashColor = new Color(1f, 0.92f, 0.5f); // 光る色(白い敵でも分かるよう、少し黄色)
[SerializeField] private float blinkSpeed = 6f; // 点滅の速さ
[SerializeField] private float minFlash = 0.15f; // 一番弱いときの白さ
[SerializeField] private float maxFlash = 0.45f; // 一番強いときの白さ
[SerializeField] private Color dimColor = new Color(0.4f, 0.4f, 0.45f); // 暗くするときの色
private Image flashImage; // 自分の絵の上に重ねる、白いシルエット
private bool isHighlighted;
private void Awake()
{
// 自分の絵と同じ大きさの Image を子に作って、白く光らせるのに使う
GameObject flash = new GameObject("Flash", typeof(RectTransform), typeof(Image));
flash.transform.SetParent(transform, false);
RectTransform flashRect = (RectTransform)flash.transform;
flashRect.anchorMin = Vector2.zero;
flashRect.anchorMax = Vector2.one;
flashRect.offsetMin = Vector2.zero;
flashRect.offsetMax = Vector2.zero;
flashImage = flash.GetComponent<Image>();
flashImage.material = flashMaterial;
flashImage.preserveAspect = true;
flashImage.raycastTarget = false;
flashImage.enabled = false;
}
private void Update()
{
if (isHighlighted)
{
UpdateFlash();
}
}
// 光る絵を、体と同じ形・今の白さにする
private void UpdateFlash()
{
// 待機のコマが変わっても、同じ形で光るようにする
flashImage.sprite = image.sprite;
// Sin で 0〜1 をくり返し、白さを minFlash〜maxFlash の間で変える
float wave = (Mathf.Sin(Time.time * blinkSpeed) + 1f) / 2f;
Color color = flashColor;
color.a = Mathf.Lerp(minFlash, maxFlash, wave);
flashImage.color = color;
}
// 仲間のデータで見た目を作る
public void SetupCharacter(CharacterData data)
{
name = data.CharacterName;
idleBounce.SetFrames(data.IdleFrames);
}
// 敵のデータで見た目を作る
public void SetupEnemy(EnemyData data)
{
name = data.EnemyName;
image.sprite = data.Sprite;
}
// 選ばれている間、白く点滅させる
public void SetHighlight(bool isOn)
{
isHighlighted = isOn;
if (isOn)
{
// 表示する前に、形と色を入れておく(入れる前に表示すると、1フレームだけ白い四角が出る)
UpdateFlash();
}
flashImage.enabled = isOn;
}
// 暗くする(false で、もとの色に戻す)
public void SetDimmed(bool isDimmed)
{
image.color = isDimmed ? dimColor : Color.white;
}
}
- Awake で Flash を作る
- 自分の子に、同じ大きさのImageを作って、WhiteFlashのマテリアルを付ける。体の絵の上に重なる「白いシルエット」になる
- Hierarchyで子を作ってつなぐ手間がないので、仲間にも敵にも、UnitViewを付けるだけで使えます
- UpdateFlash()
- 光る絵を、体と同じ絵(
flashImage.sprite = image.sprite)と、今の白さにそろえる。仲間は待機のコマがくり返し変わるので、光っている間は毎フレーム呼ぶ
- 光る絵を、体と同じ絵(
- Mathf.Sin と Lerp
Sinは-1〜1をくり返す。1を足して2で割ると0〜1になる。その値で、白さ(Alpha)を0.15〜0.45の間で行ったり来たりさせる- 一番弱いときも0にしないので、どの瞬間を切り取っても、少し光って見えます
- flashColor
- 光る色。真っ白にすると、地下1階の白い紙ひこうきのような白っぽい敵では、光っても見分けがつかないので、少し黄色にしています
- SetHighlight(bool isOn)
- 光らせる・やめるを切り替える。呼ぶのは、選んでいる人にだけ
- 光らせるときは、表示する前に
UpdateFlash()で形と色を入れておく。Flashは最初、絵が空っぽで色が真っ白なので、そのまま表示すると、次のUpdateで形が入るまでの1フレームだけ、白い四角が見えてしまいます
- SetDimmed(bool isDimmed)
- ImageのColorは「絵の色に掛け算」なので、白なら元の色のまま、灰色(0.4)にすると全体が暗くなる。暗くするのは、色を変えるだけで出来ます
UnitViewを付けている仲間3人(Fairy・Slime・Jiangshi)と、PrefabsのEnemyのFlash Materialに、WhiteFlashをドラッグします。

Flash Materialを入れ忘れても、エラーにはなりません。ただ、ふつうのマテリアルで絵が重なるだけなので、白く光らず、少し濃く見えるだけになります。光らないときは、Flash Materialが入っているかを確かめましょう。
スライム光らせたい人にだけ命令すればいいので、分かりやすいですね
この「白いシルエットを重ねる」仕組みは、#8で攻撃が当たったときに、敵を一瞬だけ真っ白に光らせる演出にも使います。
ステータスの行も、選んでいる人の名前を黄色にします。#3で作ったStatusRowに次のメソッドを足します。
// 選んでいる人の名前を黄色にする
public void SetHighlight(bool isOn)
{
nameText.color = isOn ? new Color(1f, 0.86f, 0.35f) : Color.white;
}
キャラクターの動きと、ステータスの名前の色の2か所で「今の番」を見せています。キャラクターが重なって見えにくいときや、敵の攻撃の演出が入ったあとでも、どちらかで分かるようにしておくと親切です。
選んでいる人を光らせよう
#4のBattleLineupに、光らせる・暗くする処理を足します。
using TMPro;
using UnityEngine;
// データから仲間と敵を並べる(バトルの流れは #9 で BattleManager に移す)
public class BattleLineup : MonoBehaviour
{
[SerializeField] private CharacterData[] party; // バトルに出る3人
[SerializeField] private UnitView[] partyViews; // PartyArea の3人
[SerializeField] private EnemyData[] enemies; // 出てくる敵(1〜4体)
[SerializeField] private UnitView enemyPrefab;
[SerializeField] private RectTransform enemyArea;
[SerializeField] private float enemySpacing = 300f; // 敵の横の間隔
[SerializeField] private StatusWindow statusWindow;
[SerializeField] private CommandMenu commandMenu;
[SerializeField] private TMP_Text messageText;
private StatusRow[] statusRows;
private BattleContext context;
private async void Start()
{
statusRows = new StatusRow[party.Length];
for (int i = 0; i < partyViews.Length; i++)
{
CharacterData character = party[i];
partyViews[i].SetupCharacter(character);
statusRows[i] = statusWindow.AddRow(character);
}
for (int i = 0; i < enemies.Length; i++)
{
UnitView view = Instantiate(enemyPrefab, enemyArea);
// 真ん中をそろえて横に並べ、1体おきに上下へずらす
float x = (i - (enemies.Length - 1) / 2f) * enemySpacing;
float y = (i % 2 == 0) ? 60f : -60f;
((RectTransform)view.transform).anchoredPosition = new Vector2(x, y);
view.SetupEnemy(enemies[i]);
}
// コマンドに渡す道具をまとめる
context = new BattleContext();
context.MessageText = messageText;
try
{
while (true)
{
await SelectActionsAsync();
await Awaitable.WaitForSecondsAsync(2f, destroyCancellationToken);
}
}
catch (System.OperationCanceledException)
{
// オブジェクトが消えたら、そこで終わり
}
}
// 3人のコマンドを順に選ぶ(キャンセルで前の人に戻る)
private async Awaitable SelectActionsAsync()
{
BattleAction[] actions = new BattleAction[party.Length];
int index = 0;
while (index < party.Length)
{
messageText.text = $"{party[index].CharacterName}は どうする?";
partyViews[index].SetHighlight(true);
statusRows[index].SetHighlight(true);
DimPartyExcept(index);
BattleCommand command = await commandMenu.WaitForCommandAsync(party[index].Commands);
BattleAction action = null;
if (command != null)
{
action = await command.SelectAsync(context);
}
partyViews[index].SetHighlight(false);
statusRows[index].SetHighlight(false);
if (command == null)
{
// コマンドでキャンセルしたら、前の人に戻る(1人目なら、そのまま1人目)
if (index > 0)
{
index--;
}
continue;
}
if (action == null)
{
// 相手選びなどでキャンセルしたら、同じ人のコマンドを選び直す
continue;
}
actions[index] = action;
index++;
}
BrightenParty();
messageText.text = $"コマンドが きまった!({ActionText(actions[0])}・{ActionText(actions[1])}・{ActionText(actions[2])})";
}
// index の人以外の仲間を暗くする(index が -1 なら、全員暗くする)
private void DimPartyExcept(int index)
{
for (int i = 0; i < partyViews.Length; i++)
{
partyViews[i].SetDimmed(i != index);
}
}
// 仲間を全員、もとの明るさに戻す
private void BrightenParty()
{
foreach (UnitView view in partyViews)
{
view.SetDimmed(false);
}
}
// 画面に出す説明(相手がいれば、相手の名前も付ける)
private string ActionText(BattleAction action)
{
if (action.Target != null)
{
return $"{action.Command.CommandName}({action.Target.name})";
}
return action.Command.CommandName;
}
}
- 光らせる → コマンドを選ぶ → 光らせるのをやめる
- この順番が上から読んだとおりに動く。asyncで書くうれしさが出るところです
- DimPartyExcept(index)
- 3人それぞれに「選ばれている人以外か」(
i != index)を伝えて、選ばれている人は明るく、ほかの2人は暗くする - 次の人に移ったときも、この1行で明るさがまとめて入れ替わる
- 3人それぞれに「選ばれている人以外か」(
- BrightenParty()
- 3人分が決まったら、全員をもとの明るさに戻す
光らせるのも、やめるのも、選んでいる人(partyViews[index])にだけ命令しています。光らせる処理は「光らせたい人」だけ、暗くする処理は「まわり」だけを受け持つので、どちらかを変えても、もう一方に影響しません。
動かして確認!
再生して、キーボードで操作してみましょう。


#5の動作確認
- 再生すると、スライムちゃんが光って点滅し、フェアリーとキョンシーが暗くなる。ステータスの名前も黄色くなる
- コマンドを決めると、点滅と明るさが次の仲間に移る。Escで前の人に戻ると、前の人に戻る
- どの瞬間で止めても(静止画でも)、だれの番か分かる
- 3人分決まると、全員がもとの明るさに戻る



どこを見ればいいか、すぐ分かるようになりましたね



次の#6では「たたかう」で敵を選ぶよ。そのときは、仲間を全員暗くして、ねらっている敵を光らせるんだ
あとがき
今回は、だれの番かがひと目で分かるように、選んでいる仲間を光らせて、ほかの仲間を暗くしました。光らせたい人にだけ命令する形にしたので、敵を選ぶときや、全体まほうで何人もまとめて選ぶときにも、そのまま使えます。次回は「たたかう」を選んだあとに、どの敵をねらうかを選べるようにしたいと思います!
コメント