
RPGには、仲間や敵がたくさん出てきます。名前・HP・攻撃力・すばやさ・・・。それをスクリプトの中に直接書いていたら、敵を1体増やすたびにコードを書き換えることになります。今回は、キャラクター1人分・敵1体分の情報をScriptableObjectのアセットにして、データを差し替えるだけで並ぶキャラクターと敵が変わる形を作ってみたいと思います!
- シリーズの目次:ターン制RPGの作り方
- 前回:#1 プロジェクトの準備とバトル画面のレイアウト
今回作るもの
- 6つの能力をまとめた
Stats - 仲間1人分のデータ
CharacterDataと、敵1体分のデータEnemyData - 4人の仲間と、地下1階の敵3体のデータ
- データから見た目を作る
UnitViewと、並べるBattleLineup
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| Stats | HP・MP・こうげき・ぼうぎょ・すばやさ・まりょく | (付けない。ほかのデータの中で使う) |
| CharacterData | 仲間1人分の名前・待機の絵・能力・成長 | (付けない。アセットを作る) |
| EnemyData | 敵1体分の名前・絵・能力・経験値・お金 | (付けない。アセットを作る) |
| UnitView | データの絵を表示する | 3人の仲間・敵のプレハブ |
| IdleBounce | 待機のコマ(#1で作ったものに1つ足す) | 3人の仲間 |
| BattleLineup | データから仲間と敵を並べる | Battle |
ScriptableObjectの基本は、12-8 ScriptableObjectでデータを作ろうで紹介しています。
キャラクターに何が要るか考えよう
作り始める前に、仲間と敵がそれぞれ何を持っていればいいかを書き出してみます。
| 仲間 | 敵 | |
|---|---|---|
| 名前 | ○ | ○ |
| 絵 | 待機の3コマ(攻撃やダメージの絵は、使う回で足す) | 1枚 |
| 能力 | Lv1の能力 | 能力 |
| 成長 | 1レベル上がるごとに増える分 | - |
| 倒したとき | - | 経験値・お金 |
能力は、仲間も敵も同じ6つです。
| 能力 | 変数名 | 使いみち |
|---|---|---|
| HP | maxHp | 0になると倒れる |
| MP | maxMp | まほうを使うと減る(#13) |
| こうげき | attack | たたかうのダメージ(#7) |
| ぼうぎょ | defense | 受けるダメージを減らす |
| すばやさ | speed | 行動の順番(#9)・にげる(#11) |
| まりょく | magic | まほうの強さ(#13) |
同じ6つを仲間と敵の両方で使うので、6つをまとめた小さなクラスを1つ作っておきます。
スライムまほうやどうぐは、まだ入れないんですか?



使う回で足していくよ!最初から全部入れると、どこで使うのか分からない項目ばかりになっちゃうからね
能力をまとめたStatsを作ろう
- TurnBasedRpg/Scriptsに「Stats」スクリプトを作り、中身を次のように書き換える
// HP・MP と 4つの能力をまとめたもの(仲間と敵の両方で使う)
[System.Serializable]
public class Stats
{
public int maxHp; // HP
public int maxMp; // MP
public int attack; // こうげき
public int defense; // ぼうぎょ
public int speed; // すばやさ
public int magic; // まりょく
}
- [System.Serializable]
- このクラスを、ほかのスクリプトやScriptableObjectの中に入れたときに、Inspectorに中身が表示されるようにする印
- MonoBehaviourでもScriptableObjectでもない、普通のクラスです
- public の変数
- 能力は「読むだけ」ではなく、#12のレベルアップや#19の装備で足し算するので、ここではpublicにしています
CharacterDataとEnemyDataを作ろう
続けて、仲間のデータと敵のデータを作ります。
- 「CharacterData」スクリプトを作る
using UnityEngine;
// 仲間1人分のデータ
[CreateAssetMenu(fileName = "NewCharacter", menuName = "TurnBasedRpg/Character Data")]
public class CharacterData : ScriptableObject
{
[SerializeField] private string characterName;
[SerializeField] private Sprite[] idleFrames; // 待機の3コマ(walk_61・62・63)
[SerializeField] private Stats baseStats; // Lv1の能力
[SerializeField] private Stats growth; // 1レベル上がるごとに増える分
public string CharacterName => characterName;
public Sprite[] IdleFrames => idleFrames;
public Stats BaseStats => baseStats;
public Stats Growth => growth;
}
- 「EnemyData」スクリプトを作る
using UnityEngine;
// 敵1体分のデータ
[CreateAssetMenu(fileName = "NewEnemy", menuName = "TurnBasedRpg/Enemy Data")]
public class EnemyData : ScriptableObject
{
[SerializeField] private string enemyName;
[SerializeField] private Sprite sprite;
[SerializeField] private Stats stats;
[SerializeField] private int exp; // 倒したときの経験値
[SerializeField] private int gold; // 倒したときのお金
public string EnemyName => enemyName;
public Sprite Sprite => sprite;
public Stats Stats => stats;
public int Exp => exp;
public int Gold => gold;
}
- [CreateAssetMenu]
- Projectウインドの右クリック>Create に、このデータを作るメニューを足す。menuNameの「/」で区切った分だけ、メニューが階層になる
- [SerializeField] private と、=>のプロパティ
- Inspectorでは書き換えられるけど、ほかのスクリプトからは読むだけにしておく形。バトル中にうっかりデータのHPを減らしてしまう事故を防げます(バトル中のHPは、#7で別に持ちます)
- Stats baseStats / Stats growth
- さっき作ったStatsを、Lv1の能力と成長の2つ分使っている
ScriptableObjectのアセットは、再生中にスクリプトから値を書き換えると、再生を止めても書き換わったまま残ります。バトルで減ったHPなどをデータに書き込まないように、データは「読むだけ」にしておきましょう。
4人と、最初の敵3体を作ろう
データのファイル名(Slime・SpilledInkなど)は英語、画面に出す名前(スライム・こぼれたインク)は日本語、と分けておきます。ファイル名は、#25のセーブでデータを探すときの目印にも使います。
- TurnBasedRpg/Dataの中に、Characters・Enemiesの2つのフォルダを作る
- Charactersフォルダで右クリック>Create>TurnBasedRpg>Character Data を4回して、名前を「Slime」「Fairy」「Jiangshi」「Medusa」にする
- Enemiesフォルダで右クリック>Create>TurnBasedRpg>Enemy Data を3回して、名前を「SpilledInk」「PaperPlane」「Bookworm」にする


仲間の能力
それぞれのアセットを選んで、Inspectorで値を入れます。Idle Framesには、#1と同じように<名前>_walk_61〜63の3枚をドラッグします。
| 名前 | Character Name | HP | MP | こうげき | ぼうぎょ | すばやさ | まりょく |
|---|---|---|---|---|---|---|---|
| Slime | スライム | 38 | 8 | 12 | 11 | 9 | 6 |
| Fairy | フェアリー | 26 | 20 | 6 | 7 | 12 | 12 |
| Jiangshi | キョンシー | 32 | 6 | 14 | 8 | 15 | 4 |
| Medusa | メドゥーサ | 28 | 18 | 7 | 7 | 8 | 14 |
成長(Growth)は、1レベルごとに増える分です。#12のレベルアップで使います。
| 名前 | HP | MP | こうげき | ぼうぎょ | すばやさ | まりょく |
|---|---|---|---|---|---|---|
| Slime | 5 | 1 | 2 | 2 | 1 | 1 |
| Fairy | 3 | 3 | 1 | 1 | 2 | 2 |
| Jiangshi | 4 | 1 | 3 | 1 | 2 | 0 |
| Medusa | 3 | 3 | 1 | 1 | 1 | 3 |


4人の役割は、数字にそのまま表れています。
- スライムちゃん:HPとぼうぎょが高い、打たれ強いバランス型
- フェアリー:MPとすばやさが高く、回復のまほうで支える
- キョンシー:こうげきとすばやさが高い。まほうは苦手
- メドゥーサ:まりょくが一番高い、攻撃のまほうの担当
敵の能力
| 名前 | Enemy Name | Sprite | HP | MP | こうげき | ぼうぎょ | すばやさ | まりょく | Exp | Gold |
|---|---|---|---|---|---|---|---|---|---|---|
| SpilledInk | こぼれたインク | enemy_spilled_ink | 18 | 0 | 9 | 4 | 5 | 0 | 3 | 4 |
| PaperPlane | 紙ひこうき | enemy_paper_plane | 12 | 0 | 8 | 2 | 16 | 0 | 3 | 3 |
| Bookworm | 本の虫 | enemy_bookworm | 22 | 0 | 10 | 6 | 7 | 0 | 5 | 6 |


- 紙ひこうきは、HPは低いけど、すばやさが16で4人のだれよりも速い



紙ひこうきって、キョンシーよりも速いんですね・・・。先に動かれちゃいそう
- 本の虫は、3体の中では一番かたい。#15で「どく」の攻撃をしてくるようになる
数字は、このあと実際に戦ってみながら直していきます。
データから見た目を作ろう
データが出来たので、画面に並べてみます。#1では3人の絵をImageに直接入れていましたが、これをデータから入れる形に変えます。
IdleBounceに、コマを差し替えるメソッドを足す
#1で作ったIdleBounceは、Inspectorで入れた3枚をくり返していました。データの3枚に差し替えられるように、メソッドを1つ足します。
// 待機のコマを差し替える(データから見た目を作るときに呼ぶ)
public void SetFrames(Sprite[] newFrames)
{
frames = newFrames;
image.sprite = frames[frameOrder[orderIndex]];
}
UnitView
- 「UnitView」スクリプトを作る
using UnityEngine;
using UnityEngine.UI;
// 仲間や敵の見た目。データを受け取って、絵を表示する
public class UnitView : MonoBehaviour
{
[SerializeField] private Image image;
[SerializeField] private IdleBounce idleBounce; // 仲間だけ。敵は空のまま
// 仲間のデータで見た目を作る
public void SetupCharacter(CharacterData data)
{
name = data.CharacterName;
idleBounce.SetFrames(data.IdleFrames);
}
// 敵のデータで見た目を作る
public void SetupEnemy(EnemyData data)
{
name = data.EnemyName;
image.sprite = data.Sprite;
}
}
- name = …
- GameObjectの名前を、データの名前にする。再生中のHierarchyで、だれがだれだか分かりやすくなる
- SetupCharacter / SetupEnemy
- 仲間は待機の3コマ、敵は1枚の絵。持っているデータが違うので、メソッドを分けています
3人の仲間(Fairy・Slime・Jiangshi)にUnitViewを付けて、ImageとIdle Bounceに自分自身をドラッグします。
敵をプレハブにする
敵は戦いごとに数が変わるので、プレハブにしてスクリプトから並べます(7-3 スクリプトからプレハブを生成)。
- #1で置いたSpilledInkの名前を「Enemy」に変え、UnitViewを付けて、Imageに自分自身をドラッグ(Idle Bounceは空のまま)
- EnemyをTurnBasedRpg/Prefabsにドラッグしてプレハブにする
- シーンのEnemyは消す


BattleLineup
- 「BattleLineup」スクリプトを作る
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; // 敵の横の間隔
private void Start()
{
for (int i = 0; i < partyViews.Length; i++)
{
partyViews[i].SetupCharacter(party[i]);
}
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]);
}
}
}
- (i – (enemies.Length – 1) / 2f) * enemySpacing
- 敵の数が何体でも、真ん中にそろうように並べる計算。3体なら -1・0・1 倍、2体なら -0.5・0.5 倍の位置になる
- (i % 2 == 0) ? 60f : -60f
- 1体おきに上下へずらす。横一列より、奥行きがあるように見えます
- (RectTransform)view.transform
- UIの位置は
anchoredPositionで決めるので、TransformをRectTransformとして使う
- UIの位置は
BattleLineupは、データから並べるところだけを受け持つ、仮の入れ物です。#9でバトルの流れを作るときに、BattleManagerの中に移します。
Canvasの外に空のGameObject「Battle」を作り、BattleLineupを付けて設定します。
- Party:Slime・Fairy・Jiangshi(データ)
- Party Views:PartyAreaのSlime・Fairy・Jiangshi(シーンのオブジェクト)
- PartyとParty Viewsは同じ順番にする(1番目のデータが1番目の見た目に入る)
- Enemies:SpilledInk・PaperPlane・Bookworm
- Enemy Prefab:PrefabsのEnemy
- Enemy Area:EnemyArea


動かして確認!
再生すると、仲間3人と敵3体が並びます。


試しに、データを入れ替えてみましょう。
- BattleのPartyの2番目をFairyからMedusaにする
- Enemiesを2つに減らして、両方SpilledInkにする
再生すると、2人目がメドゥーサになり、こぼれたインクが2体、真ん中にそろって並びます。スクリプトを1行も変えずに、並ぶキャラクターと敵が変わりました。


#2の動作確認
- 仲間3人が、データの待機の絵で弾んでいる
- 敵が3体、真ん中にそろって並び、1体おきに上下にずれている
- 再生中のHierarchyで、仲間と敵の名前がデータの名前になっている
- PartyやEnemiesを入れ替えると、並ぶキャラクターと敵が変わる
確認が終わったら、Partyは「Slime・Fairy・Jiangshi」、Enemiesは「SpilledInk・PaperPlane・Bookworm」に戻しておいてください。
あとがき
今回は、仲間と敵をデータにして、データから画面に並べる形を作りました。まだ並んでいるだけですが、このあとの回で増えていく敵やまほうも、同じようにデータを足していくだけで済むようになります。次回は、右下のウインドに仲間のHPとMPを表示する、ステータスのウインドを作りたいと思います!
コメント
コメント一覧 (1件)
[…] 前回:#2 キャラクターと敵のデータをScriptableObjectで作ろう […]