SRPGの敵AIは、単純に見えますが、実装は複雑です。
ターゲット選択と行動判断が、難易度を左右します。
この記事では、実装方法を詳しく解説します。
✨ この記事でわかること
- シンプルなルールベースAIの実装
- ターゲット選択のアルゴリズム
- 行動候補のスコアリング
- 安全度評価の実装
- 段階的に賢くする方法

敵AIは、最初はシンプルに実装しましょう。後から、より賢い判断を追加していけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
シンプルなルールベースAI

最初は、シンプルなルールベースAIから始めます。
実装方法を紹介します。
基本的な敵AI
|
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 93 94 95 96 97 98 99 100 101 102 103 104 |
using UnityEngine; using System.Collections.Generic; public class SimpleEnemyAI : MonoBehaviour { private Unit enemyUnit; private GridMap gridMap; private List<Unit> playerUnits; void Start() { enemyUnit = GetComponent<Unit>(); gridMap = FindObjectOfType<GridMap>(); playerUnits = FindPlayerUnits(); } public void ExecuteTurn() { // 1. 最も近いプレイヤーユニットを探す Unit target = FindNearestPlayerUnit(); if (target == null) return; // 2. 攻撃範囲内にいるかチェック if (IsInAttackRange(target)) { Attack(target); } else { // 3. 移動して近づく MoveTowards(target); } } Unit FindNearestPlayerUnit() { Unit nearest = null; float minDistance = float.MaxValue; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - enemyUnit.gridX) + Mathf.Abs(player.gridY - enemyUnit.gridY); if (distance < minDistance) { minDistance = distance; nearest = player; } } return nearest; } bool IsInAttackRange(Unit target) { int distance = Mathf.Abs(target.gridX - enemyUnit.gridX) + Mathf.Abs(target.gridY - enemyUnit.gridY); return distance <= enemyUnit.stats.attackRange; } void Attack(Unit target) { FindObjectOfType<BattleSystem>().ExecuteAttack(enemyUnit, target); } void MoveTowards(Unit target) { // 簡易的な移動:目標に向かって1マス進む int dx = target.gridX - enemyUnit.gridX; int dy = target.gridY - enemyUnit.gridY; int moveX = 0; int moveY = 0; if (Mathf.Abs(dx) > Mathf.Abs(dy)) { moveX = dx > 0 ? 1 : -1; } else { moveY = dy > 0 ? 1 : -1; } int newX = enemyUnit.gridX + moveX; int newY = enemyUnit.gridY + moveY; if (gridMap.IsValidPosition(newX, newY) && !gridMap.HasObstacle(newX, newY)) { enemyUnit.SetPosition(newX, newY, gridMap); } } List<Unit> FindPlayerUnits() { List<Unit> players = new List<Unit>(); // プレイヤーユニットを取得 return players; } } |
このコードで、基本的な敵AIが実装できます。
最も近いプレイヤーユニットを探し、攻撃範囲内なら攻撃、範囲外なら移動します。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
ターゲット選択のアルゴリズム

ターゲット選択は、敵AIの核心です。
より賢い選択方法を紹介します。
スコアリングによるターゲット選択
|
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 |
using UnityEngine; using System.Collections.Generic; using System.Linq; public class TargetSelector { public Unit SelectBestTarget(Unit enemy, List<Unit> playerUnits, GridMap gridMap) { Unit bestTarget = null; float bestScore = float.MinValue; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; float score = CalculateTargetScore(enemy, player, gridMap); if (score > bestScore) { bestScore = score; bestTarget = player; } } return bestTarget; } float CalculateTargetScore(Unit enemy, Unit target, GridMap gridMap) { float score = 0f; // 1. 距離スコア(近いほど高い) int distance = Mathf.Abs(target.gridX - enemy.gridX) + Mathf.Abs(target.gridY - enemy.gridY); score += 100f / (distance + 1); // 2. HPスコア(低いほど高い) float hpRatio = (float)target.stats.currentHP / target.stats.maxHP; score += (1f - hpRatio) * 50f; // 3. 攻撃力スコア(高いほど高い、優先的に倒す) score += target.stats.attack * 0.5f; // 4. 防御力スコア(低いほど高い、倒しやすい) score -= target.stats.defense * 0.3f; return score; } } |
このコードで、スコアリングによるターゲット選択が実装できます。
距離・HP・攻撃力・防御力を考慮して、最適なターゲットを選びます。
✅ ターゲット選択の基準
- 距離:近いほど優先(移動コストが少ない)
- HP:低いほど優先(倒しやすい)
- 攻撃力:高いほど優先(脅威度が高い)
- 防御力:低いほど優先(ダメージが出やすい)
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
行動候補のスコアリング

行動候補をスコアリングして、最適な行動を選びます。
実装方法を紹介します。
行動候補の生成
|
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
using UnityEngine; using System.Collections.Generic; using System.Linq; [System.Serializable] public class ActionCandidate { public ActionType actionType; public Vector2Int targetPosition; public Unit targetUnit; public float score; } public enum ActionType { Move, Attack, Wait, Skill } public class ActionScorer { public ActionCandidate SelectBestAction(Unit enemy, List<Unit> playerUnits, GridMap gridMap) { List<ActionCandidate> candidates = new List<ActionCandidate>(); // 1. 移動候補を生成 GenerateMoveCandidates(enemy, playerUnits, gridMap, candidates); // 2. 攻撃候補を生成 GenerateAttackCandidates(enemy, playerUnits, gridMap, candidates); // 3. 待機候補を生成 candidates.Add(new ActionCandidate { actionType = ActionType.Wait, score = 0f }); // 4. 最高スコアの行動を選択 return candidates.OrderByDescending(c => c.score).First(); } void GenerateMoveCandidates(Unit enemy, List<Unit> playerUnits, GridMap gridMap, List<ActionCandidate> candidates) { // 移動範囲内の全マスをチェック for (int x = -enemy.moveRange; x <= enemy.moveRange; x++) { for (int y = -enemy.moveRange; y <= enemy.moveRange; y++) { int distance = Mathf.Abs(x) + Mathf.Abs(y); if (distance <= enemy.moveRange) { int targetX = enemy.gridX + x; int targetY = enemy.gridY + y; if (gridMap.IsValidPosition(targetX, targetY) && !gridMap.HasObstacle(targetX, targetY)) { float score = ScoreMovePosition(enemy, new Vector2Int(targetX, targetY), playerUnits, gridMap); candidates.Add(new ActionCandidate { actionType = ActionType.Move, targetPosition = new Vector2Int(targetX, targetY), score = score }); } } } } float ScoreMovePosition(Unit enemy, Vector2Int position, List<Unit> playerUnits, GridMap gridMap) { float score = 0f; // 1. 最も近いプレイヤーへの距離 float minDistance = float.MaxValue; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - position.x) + Mathf.Abs(player.gridY - position.y); minDistance = Mathf.Min(minDistance, distance); } score += 100f / (minDistance + 1); // 2. 安全度(プレイヤーからの距離) float safetyScore = CalculateSafety(position, playerUnits); score += safetyScore * 20f; return score; } void GenerateAttackCandidates(Unit enemy, List<Unit> playerUnits, GridMap gridMap, List<ActionCandidate> candidates) { foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - enemy.gridX) + Mathf.Abs(player.gridY - enemy.gridY); if (distance <= enemy.stats.attackRange) { float score = ScoreAttack(enemy, player); candidates.Add(new ActionCandidate { actionType = ActionType.Attack, targetUnit = player, score = score }); } } } float ScoreAttack(Unit enemy, Unit target) { float score = 0f; // 1. 与えられるダメージ int damage = enemy.stats.attack - target.stats.defense; score += damage * 10f; // 2. ターゲットのHP(低いほど高い) float hpRatio = (float)target.stats.currentHP / target.stats.maxHP; score += (1f - hpRatio) * 50f; // 3. ターゲットの攻撃力(高いほど高い、優先的に倒す) score += target.stats.attack * 5f; return score; } float CalculateSafety(Vector2Int position, List<Unit> playerUnits) { float minDistance = float.MaxValue; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - position.x) + Mathf.Abs(player.gridY - position.y); minDistance = Mathf.Min(minDistance, distance); } return minDistance; } } |
このコードで、行動候補をスコアリングできます。
移動・攻撃・待機の各候補を評価し、最適な行動を選びます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
安全度評価の実装

安全度評価は、敵AIが生き残るために重要です。
実装方法を紹介します。
安全度計算
|
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 |
public class SafetyEvaluator { public float EvaluateSafety(Unit enemy, Vector2Int position, List<Unit> playerUnits, GridMap gridMap) { float safetyScore = 0f; // 1. 最も近いプレイヤーへの距離 float minDistance = float.MaxValue; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - position.x) + Mathf.Abs(player.gridY - position.y); minDistance = Mathf.Min(minDistance, distance); } safetyScore += minDistance * 10f; // 2. 攻撃範囲内にいるプレイヤーの数 int attackersInRange = 0; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - position.x) + Mathf.Abs(player.gridY - position.y); if (distance <= player.stats.attackRange) { attackersInRange++; } } safetyScore -= attackersInRange * 50f; // 3. 受ける可能性のあるダメージ float potentialDamage = 0f; foreach (var player in playerUnits) { if (player.stats.currentHP <= 0) continue; int distance = Mathf.Abs(player.gridX - position.x) + Mathf.Abs(player.gridY - position.y); if (distance <= player.stats.attackRange) { int damage = player.stats.attack - enemy.stats.defense; potentialDamage += Mathf.Max(1, damage); } } safetyScore -= potentialDamage * 5f; return safetyScore; } } |
このコードで、安全度を評価できます。
プレイヤーからの距離、攻撃範囲内のプレイヤー数、受ける可能性のあるダメージを考慮します。

安全度評価は、敵AIが無謀な行動を避けるために重要です。ただし、安全すぎると、プレイヤーが退屈します。バランスを取りましょう。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
段階的に賢くする方法

敵AIは、段階的に賢くしていきます。
実装方法を紹介します。
- 週1基本的なルールベースAI(所要10時間)
最も近いプレイヤーを探し、攻撃範囲内なら攻撃、範囲外なら移動するシンプルなAIを実装します。
- 週2スコアリングによる判断(所要10時間)
ターゲット選択と行動選択にスコアリングを導入します。距離・HP・攻撃力などを考慮します。
- 週3安全度評価の追加(所要10時間)
安全度評価を追加し、無謀な行動を避けるようにします。プレイヤーの攻撃範囲も考慮します。
合計30時間で、基本的な敵AIが完成します。
1日2時間なら、約2週間で完成します。
難易度別のAI調整
|
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 |
public enum AIDifficulty { Easy, // 簡単:ランダム行動 Normal, // 普通:基本的なルールベース Hard // 難しい:スコアリング + 安全度評価 } public class EnemyAI : MonoBehaviour { public AIDifficulty difficulty = AIDifficulty.Normal; public void ExecuteTurn() { switch (difficulty) { case AIDifficulty.Easy: ExecuteEasyAI(); break; case AIDifficulty.Normal: ExecuteNormalAI(); break; case AIDifficulty.Hard: ExecuteHardAI(); break; } } void ExecuteEasyAI() { // ランダムに行動 int random = Random.Range(0, 3); if (random == 0) { MoveRandomly(); } else if (random == 1) { AttackRandomTarget(); } else { Wait(); } } void ExecuteNormalAI() { // 基本的なルールベースAI Unit target = FindNearestPlayerUnit(); if (IsInAttackRange(target)) { Attack(target); } else { MoveTowards(target); } } void ExecuteHardAI() { // スコアリング + 安全度評価 ActionScorer scorer = new ActionScorer(); ActionCandidate bestAction = scorer.SelectBestAction(enemyUnit, playerUnits, gridMap); ExecuteAction(bestAction); } } |
難易度別にAIを調整できます。
プレイヤーのスキルに応じて、難易度を選択できます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
実装例:完全な敵AIシステム

実際に使える、完全な敵AIシステムの実装例を紹介します。
|
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 |
using UnityEngine; using System.Collections.Generic; using System.Linq; public class CompleteEnemyAI : MonoBehaviour { private Unit enemyUnit; private GridMap gridMap; private List<Unit> playerUnits; private TargetSelector targetSelector; private ActionScorer actionScorer; private SafetyEvaluator safetyEvaluator; void Start() { enemyUnit = GetComponent<Unit>(); gridMap = FindObjectOfType<GridMap>(); playerUnits = FindPlayerUnits(); targetSelector = new TargetSelector(); actionScorer = new ActionScorer(); safetyEvaluator = new SafetyEvaluator(); } public void ExecuteTurn() { // 1. 最適なターゲットを選択 Unit target = targetSelector.SelectBestTarget(enemyUnit, playerUnits, gridMap); if (target == null) return; // 2. 行動候補を生成してスコアリング ActionCandidate bestAction = actionScorer.SelectBestAction(enemyUnit, playerUnits, gridMap); // 3. 安全度を考慮して最終決定 if (bestAction.actionType == ActionType.Move) { float safety = safetyEvaluator.EvaluateSafety(enemyUnit, bestAction.targetPosition, playerUnits, gridMap); // 安全度が低すぎる場合は、より安全な位置を探す if (safety < -50f) { bestAction = FindSaferPosition(); } } // 4. 行動を実行 ExecuteAction(bestAction); } void ExecuteAction(ActionCandidate action) { switch (action.actionType) { case ActionType.Move: enemyUnit.SetPosition(action.targetPosition.x, action.targetPosition.y, gridMap); break; case ActionType.Attack: FindObjectOfType<BattleSystem>().ExecuteAttack(enemyUnit, action.targetUnit); break; case ActionType.Wait: // 待機 break; } } ActionCandidate FindSaferPosition() { // より安全な位置を探す // 実装は省略 return new ActionCandidate { actionType = ActionType.Wait }; } List<Unit> FindPlayerUnits() { // プレイヤーユニットを取得 return new List<Unit>(); } } |
このコードで、完全な敵AIシステムが実装できます。
ターゲット選択、行動スコアリング、安全度評価を統合しています。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
よくある質問(FAQ)

Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
まとめ

敵AIは、段階的に実装すれば必ず完成します。
最初はシンプルに、後から機能を追加していきましょう。
✅ 今日から始める3ステップ
- ステップ1:基本的なルールベースAIを実装する(所要3時間)
- ステップ2:ターゲット選択にスコアリングを追加する(所要2時間)
- ステップ3:行動選択にスコアリングを追加する(所要3時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる



コメント