SRPGのバトルシステムは、複雑に見えます。
しかし、処理フローを分解すれば、実装できます。
この記事では、設計から実装まで詳しく解説します。
✨ この記事でわかること
- プレイヤーターンと敵ターンの管理方法
- 行動選択UIの実装
- 攻撃判定とダメージ計算の実装
- ステートマシンを使った設計
- 実装例とコード

バトルシステムは、ステートマシンで管理すると整理しやすくなります。まずは状態を明確にしましょう。
\あなたにピッタリのシミュレーションゲーム制作講座を見つけよう!/
おすすめ第1位
経営シミュレーション×
農場ゲームの作り方講座
Unity6対応・農場×経営の2ジャンル融合。AIエージェントを独自実装できる唯一の講座。未経験でも完成まで到達できる丁寧な解説が魅力。
本格派・高難易度
UnityシミュレーションRPG
の作り方講座(SRPG)
本格SRPGのAI設計・グリッドシステムを全16回で習得。制作難易度が高いSRPGを作れるスキルは、他と大きく差がつく強みになります。
初心者にもおすすめ
Unity ノンフィールドRPG
+スレスパ風JRPG講座
Slay the Spire風デッキ構築×JRPGをUnityで実装。Unity6・スマホ化対応で、初心者がゲーム開発の第一歩を踏み出すのに最適な講座です。
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
バトルシステムの基本フロー

バトルシステムは、以下のフローで動作します。
それぞれの処理を理解しましょう。
- ターン開始:プレイヤーターンまたは敵ターン
- 行動選択:移動・攻撃・待機など
- 行動実行:選択した行動を実行
- 結果処理:ダメージ計算、HP更新
- ターン終了:次のターンへ
このフローを実装すれば、基本的なバトルシステムが完成します。
ステートマシンでの管理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public enum BattleState { PlayerTurn, // プレイヤーターン EnemyTurn, // 敵ターン ActionSelect, // 行動選択中 ActionExecute, // 行動実行中 BattleEnd // バトル終了 } public class BattleSystem : MonoBehaviour { public BattleState currentState = BattleState.PlayerTurn; void Update() { switch (currentState) { case BattleState.PlayerTurn: HandlePlayerTurn(); break; case BattleState.EnemyTurn: HandleEnemyTurn(); break; case BattleState.ActionSelect: HandleActionSelect(); break; case BattleState.ActionExecute: HandleActionExecute(); break; } } } |
ステートマシンで管理することで、状態が明確になります。
デバッグもしやすくなります。
プレイヤーターンと敵ターンの管理

ターン管理は、バトルシステムの核心です。
実装方法を紹介します。
ターン管理スクリプト
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
using UnityEngine; using System.Collections.Generic; public class TurnManager : MonoBehaviour { public BattleState currentState = BattleState.PlayerTurn; public List<Unit> playerUnits = new List<Unit>(); public List<Unit> enemyUnits = new List<Unit>(); private int currentPlayerIndex = 0; private int currentEnemyIndex = 0; public void StartBattle() { currentState = BattleState.PlayerTurn; currentPlayerIndex = 0; StartPlayerTurn(); } void StartPlayerTurn() { if (currentPlayerIndex >= playerUnits.Count) { // プレイヤーユニット全員が行動済み currentState = BattleState.EnemyTurn; currentPlayerIndex = 0; StartEnemyTurn(); } else { // 次のプレイヤーユニットのターン Unit currentUnit = playerUnits[currentPlayerIndex]; if (currentUnit.stats.currentHP > 0) { SelectUnit(currentUnit); } else { currentPlayerIndex++; StartPlayerTurn(); } } } void StartEnemyTurn() { if (currentEnemyIndex >= enemyUnits.Count) { // 敵ユニット全員が行動済み currentState = BattleState.PlayerTurn; currentEnemyIndex = 0; StartPlayerTurn(); } else { // 次の敵ユニットのターン Unit currentUnit = enemyUnits[currentEnemyIndex]; if (currentUnit.stats.currentHP > 0) { ExecuteEnemyAI(currentUnit); } else { currentEnemyIndex++; StartEnemyTurn(); } } } void SelectUnit(Unit unit) { currentState = BattleState.ActionSelect; // 行動選択UIを表示 } void ExecuteEnemyAI(Unit unit) { // 敵AIの処理 currentEnemyIndex++; StartEnemyTurn(); } public void EndTurn() { if (currentState == BattleState.PlayerTurn) { currentPlayerIndex++; StartPlayerTurn(); } } } |
このコードで、プレイヤーターンと敵ターンを管理できます。
各ユニットが順番に行動します。
行動選択UIの実装

行動選択UIは、プレイヤーの操作を受け付けます。
実装方法を紹介します。
行動選択UIスクリプト
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
using UnityEngine; using UnityEngine.UI; public class ActionMenuUI : MonoBehaviour { public GameObject menuPanel; public Button moveButton; public Button attackButton; public Button skillButton; public Button waitButton; private Unit selectedUnit; private TurnManager turnManager; void Start() { turnManager = FindObjectOfType<TurnManager>(); moveButton.onClick.AddListener(OnMoveClicked); attackButton.onClick.AddListener(OnAttackClicked); skillButton.onClick.AddListener(OnSkillClicked); waitButton.onClick.AddListener(OnWaitClicked); menuPanel.SetActive(false); } public void ShowMenu(Unit unit) { selectedUnit = unit; menuPanel.SetActive(true); // ユニットの状態に応じてボタンを有効/無効化 UpdateButtonStates(); } void UpdateButtonStates() { // 移動済みかどうかでボタンを制御 bool canMove = !selectedUnit.hasMoved; bool canAttack = !selectedUnit.hasActed; moveButton.interactable = canMove; attackButton.interactable = canAttack; skillButton.interactable = canAttack; } void OnMoveClicked() { menuPanel.SetActive(false); // 移動範囲を表示 FindObjectOfType<MoveRangeDisplay>().ShowMoveRange(selectedUnit); } void OnAttackClicked() { menuPanel.SetActive(false); // 攻撃範囲を表示 FindObjectOfType<AttackRangeDisplay>().ShowAttackRange(selectedUnit); } void OnSkillClicked() { menuPanel.SetActive(false); // スキル選択画面を表示 } void OnWaitClicked() { menuPanel.SetActive(false); selectedUnit.hasActed = true; turnManager.EndTurn(); } } |
このコードで、行動選択UIが実装できます。
ユニットの状態に応じて、ボタンを有効/無効化します。
ユニットの状態管理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Unit : MonoBehaviour { public UnitStats stats; public bool hasMoved = false; public bool hasActed = false; public void ResetTurn() { hasMoved = false; hasActed = false; } public void OnMoveComplete() { hasMoved = true; } public void OnActionComplete() { hasActed = true; } } |
ユニットの行動状態を管理します。
移動済み・行動済みのフラグで、行動を制限します。
攻撃判定とダメージ計算

攻撃判定とダメージ計算は、戦闘の核心です。
実装方法を紹介します。
攻撃範囲の計算
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using UnityEngine; using System.Collections.Generic; public class AttackRangeDisplay : MonoBehaviour { public GameObject attackHighlightPrefab; private List<GameObject> highlights = new List<GameObject>(); public void ShowAttackRange(Unit unit, GridMap gridMap) { ClearHighlights(); int attackRange = unit.stats.attackRange; for (int x = -attackRange; x <= attackRange; x++) { for (int y = -attackRange; y <= attackRange; y++) { int distance = Mathf.Abs(x) + Mathf.Abs(y); if (distance <= attackRange) { int targetX = unit.gridX + x; int targetY = unit.gridY + y; if (gridMap.IsValidPosition(targetX, targetY)) { Vector3 position = gridMap.GetWorldPosition(targetX, targetY) + Vector3.up * 0.1f; GameObject highlight = Instantiate(attackHighlightPrefab, position, Quaternion.identity); highlights.Add(highlight); } } } } } void ClearHighlights() { foreach (var highlight in highlights) { Destroy(highlight); } highlights.Clear(); } } |
ダメージ計算の実装
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
using UnityEngine; public class BattleCalculator { public static int CalculateDamage(Unit attacker, Unit defender) { // 基本ダメージ = 攻撃力 - 防御力 int baseDamage = attacker.stats.attack - defender.stats.defense; // 最小ダメージは1 baseDamage = Mathf.Max(1, baseDamage); // ランダム要素(±10%) float randomFactor = Random.Range(0.9f, 1.1f); int finalDamage = Mathf.RoundToInt(baseDamage * randomFactor); return finalDamage; } public static bool CalculateHit(Unit attacker, Unit defender) { // 命中率 = 基本命中率 - 回避率 float hitRate = 0.9f - (defender.stats.agility * 0.01f); hitRate = Mathf.Clamp01(hitRate); return Random.Range(0f, 1f) < hitRate; } public static bool CalculateCritical(Unit attacker) { // クリティカル率 = 運 × 0.01 float critRate = attacker.stats.luck * 0.01f; return Random.Range(0f, 1f) < critRate; } } |
このコードで、ダメージ計算が実装できます。
攻撃力から防御力を引いた値が基本ダメージです。
戦闘処理の実装
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class BattleSystem : MonoBehaviour { public void ExecuteAttack(Unit attacker, Unit defender) { // 1. 命中判定 if (!BattleCalculator.CalculateHit(attacker, defender)) { Debug.Log($"{attacker.name}の攻撃が外れた"); return; } // 2. ダメージ計算 int damage = BattleCalculator.CalculateDamage(attacker, defender); // 3. クリティカル判定 bool isCritical = BattleCalculator.CalculateCritical(attacker); if (isCritical) { damage = Mathf.RoundToInt(damage * 1.5f); Debug.Log("クリティカル!"); } // 4. ダメージ適用 defender.TakeDamage(damage); Debug.Log($"{attacker.name}が{defender.name}に{damage}のダメージを与えた"); // 5. 戦闘アニメーション PlayAttackAnimation(attacker, defender); } void PlayAttackAnimation(Unit attacker, Unit defender) { // アニメーション処理 } } |
このコードで、戦闘処理が実装できます。
命中判定→ダメージ計算→ダメージ適用の順で処理します。

ダメージ計算式は、バランス調整の核心です。表計算ソフトで試算してから実装しましょう。
ステートマシンを使った設計

ステートマシンを使えば、バトルシステムを整理できます。
実装方法を紹介します。
ステートベースの実装
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
public abstract class BattleStateBase { protected BattleSystem battleSystem; public BattleStateBase(BattleSystem system) { battleSystem = system; } public virtual void Enter() { } public virtual void Update() { } public virtual void Exit() { } } public class PlayerTurnState : BattleStateBase { public PlayerTurnState(BattleSystem system) : base(system) { } public override void Enter() { Debug.Log("プレイヤーターン開始"); battleSystem.SelectNextPlayerUnit(); } public override void Update() { // プレイヤーの操作を待つ } public override void Exit() { Debug.Log("プレイヤーターン終了"); } } public class EnemyTurnState : BattleStateBase { public EnemyTurnState(BattleSystem system) : base(system) { } public override void Enter() { Debug.Log("敵ターン開始"); battleSystem.ExecuteEnemyAI(); } public override void Update() { // 敵AIの処理 } public override void Exit() { Debug.Log("敵ターン終了"); } } public class BattleSystem : MonoBehaviour { private BattleStateBase currentState; void Start() { ChangeState(new PlayerTurnState(this)); } void Update() { currentState?.Update(); } public void ChangeState(BattleStateBase newState) { currentState?.Exit(); currentState = newState; currentState.Enter(); } } |
ステートマシンで管理することで、状態遷移が明確になります。
拡張もしやすくなります。
✅ ステートマシンのメリット
- 状態が明確:現在の状態が一目で分かる
- 拡張しやすい:新しい状態を追加しやすい
- デバッグしやすい:状態遷移を追跡できる
実装例:完全なバトルシステム

実際に使える、完全なバトルシステムの実装例を紹介します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
using UnityEngine; using System.Collections.Generic; public class CompleteBattleSystem : MonoBehaviour { public TurnManager turnManager; public ActionMenuUI actionMenu; public BattleCalculator calculator; private Unit currentUnit; private Unit targetUnit; public void StartBattle(List<Unit> players, List<Unit> enemies) { turnManager.playerUnits = players; turnManager.enemyUnits = enemies; turnManager.StartBattle(); } public void OnUnitSelected(Unit unit) { currentUnit = unit; actionMenu.ShowMenu(unit); } public void OnAttackTargetSelected(Unit target) { targetUnit = target; ExecuteAttack(currentUnit, targetUnit); } void ExecuteAttack(Unit attacker, Unit defender) { // 命中判定 if (!calculator.CalculateHit(attacker, defender)) { ShowMissMessage(); return; } // ダメージ計算 int damage = calculator.CalculateDamage(attacker, defender); // クリティカル判定 if (calculator.CalculateCritical(attacker)) { damage = Mathf.RoundToInt(damage * 1.5f); ShowCriticalMessage(); } // ダメージ適用 defender.TakeDamage(damage); ShowDamageMessage(attacker, defender, damage); // ターン終了 attacker.OnActionComplete(); turnManager.EndTurn(); } void ShowMissMessage() { Debug.Log("攻撃が外れた"); } void ShowCriticalMessage() { Debug.Log("クリティカル!"); } void ShowDamageMessage(Unit attacker, Unit defender, int damage) { Debug.Log($"{attacker.name}が{defender.name}に{damage}のダメージを与えた"); } } |
このコードで、完全なバトルシステムが実装できます。
各処理を分離することで、メンテナンスしやすくなります。
よくある質問(FAQ)

シミュレーションゲームを作りたいなら!Unity入門の森のシミュレーション制作講座で本格ゲーム開発に挑戦しよう
Unity入門の森には、経営・農場・SRPGなど幅広いシミュレーションゲームを作れる講座が揃っています。作りたいジャンルや目標スキルに合わせて選んでみてください。
経営シミュレーション×農場ゲームの作り方講座【Unity6対応!AIエージェント実装まで学べる唯一の講座!】

- 未経験でも完成まで到達できる丁寧な解説
- 農場×経営の2ジャンルを同時に作れる
- 賢く自律行動するAIエージェントを独自実装できる
- 完成後も街づくりゲームに応用可能な高い拡張性
- Unity6対応のモダンな開発手法が身につく
経営シミュレーション×農場ゲームの作り方講座は、シムシティ・牧場物語・どうぶつの森のようなゲームを自分で作れるようになる講座です。
農作物の育成・収穫・販売システムはもちろん、NavMeshを使ったお客さんAIの来店・購入・帰宅の自律行動や、ルールベースAIによる従業員エージェントの実装まで、本格的なゲームAI開発が学べます。
箱庭経営シミュレーションという複合的な題材を通して、Unity中級者・上級者に必要な幅広い開発スキルを一気に習得できる講座です。
Unity6対応・AIエージェント実装まで学べる
農場も経営もコレ1本で完成させよう
→ 経営シミュレーション×農場ゲーム講座を見てみる
応用・拡張性は無限大!自律行動するAIを実装して一歩先のゲーム開発へ!
UnityシミュレーションRPGの作り方講座(SRPG)【全16回!本格タクティクスSRPGをゼロから作れる!】

- ファイアーエムブレム風の本格タクティクスSRPGを0から開発
- 書籍でも情報が少ない戦術シミュレーションを丁寧に解説
- 難解なグリッドシステム・敵AI戦術を完全攻略できる
- 全文コメント入りソースコード付きで初心者でも理解しながら進められる
- Unity入門の森の最高傑作の一つ・解説の丁寧さはトップクラス
UnityシミュレーションRPGの作り方講座(SRPG)は、ファイアーエムブレム・タクティクスオウガ・FFタクティクスのようなターン制ストラテジーシミュレーションゲームを作るための講座です。
移動可能エリアの設定・ターン進行管理・コマンド選択型戦闘・敵AI戦術ストラテジーなど、本格SRPGに必要な機能をすべてゼロから開発します。開発難易度が高いシステムも、全文コメント入りのソースコードと丁寧な解説で確実に理解しながら進められます。
「SRPGを作れる」というスキルは希少価値が高く、Unityエンジニアとして中・上級者を目指す人に強くおすすめの一本です。
本格タクティクスSRPGをゼロから完成させる
難解なグリッドシステムと敵AIを完全攻略しよう
→ UnityシミュレーションRPG(SRPG)講座を見てみる
他では学べない当サイト最高傑作!エンジニアとして頭一つ抜ける希少スキルを今すぐ。
Unity ノンフィールドRPG+スレスパ風JRPG講座【Unity6対応!デッキ構築×JRPGをスマホ向けに作れる!】

- Unity6対応・スマホ化対応で最新環境のゲーム開発が学べる
- Slay the Spire風のデッキ構築システム×JRPGの組み合わせを実装
- 初心者でも取り組みやすい丁寧な解説構成
- ノンフィールドRPGとデッキ構築JRPGの2つを合わせて学ぶのがおすすめ
Unity ノンフィールドRPGの作り方講座+Slay the Spire風デッキ構築JRPGの作り方講座は、今もっともトレンドのデッキ構築型ゲームシステムをJRPGと組み合わせて実装する方法を学べる講座です。
Unity6対応・スマホ化対応の最新カリキュラムで、デッキ構築の核となるシステムをしっかり習得できます。シミュレーション系の設計思想とも親和性が高く、ゲーム開発の幅を広げたい方にもおすすめです。
「Slay the Spireみたいなゲームを自分でも作ってみたい!」という人の最初の一歩として最適な講座です。
Unity6対応・スマホ化対応の最新カリキュラム
トレンドのデッキ構築×JRPGを最速で実装しよう
→ Slay the Spire風デッキ構築JRPG講座を見てみる
SLGの設計思想とも親和性抜群!トレンドシステムを取り入れて開発の幅を広げよう!
まとめ

バトルシステムは、処理フローを分解すれば実装できます。
ステートマシンで管理することで、整理しやすくなります。
✅ 今日から始める3ステップ
- ステップ1:ステートマシンの基本構造を作る(所要2時間)
- ステップ2:ターン管理システムを実装する(所要3時間)
- ステップ3:ダメージ計算と戦闘処理を実装する(所要3時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。





コメント