SRPGのキャラクター移動は、一見シンプルに見えますが、実際にはいくつもの処理が組み合わさって成り立っています。
たとえば、
- ユニットを選択すると移動できる範囲が表示される
- 行きたいマスをクリックすると、最短ルートで移動する
といった動きは、すべて内部で計算されています。
この記事では、「ユニットを選ぶ → 移動できる範囲を計算して表示する → 行き先をクリックする → ルートを計算して移動する」という、SRPGの基本的な移動の流れを、順番に実装していきます。
難しい理論や数式から入るのではなく、実際のゲーム画面で起きている処理を1つずつ作っていく構成なので、SRPG制作が初めての方でも理解しやすくなっています。
✨ この記事で最終的にできるようになること
- ユニットの移動力から「移動できるマス」を計算できる
- 移動可能なマスを画面上にわかりやすく表示できる
- クリックしたマスまでの移動ルートを自動で求められる
- 地形によって移動コストが変わる、SRPGらしい移動を実装できる

最初は「移動できる範囲をどう計算するか」だけを作ります。そこから表示、ルート計算、実際の移動へと、少しずつ発展させていきましょう。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
SRPGの移動範囲を計算する仕組み|まずは「どこまで行けるか」を決める

ユニットを選択した瞬間に移動範囲が表示される――この当たり前の動きは、あらかじめ移動できるマスの一覧を計算していることで実現しています。
この章では、SRPGでよく使われるシンプルな考え方を使って、移動範囲を計算する方法を解説します。
ここを理解できると、「移動範囲表示」「ルート選択」など、後の処理が一気につながってきます。
移動範囲の考え方:マンハッタン距離を使う
多くのSRPGでは、キャラクターは縦・横方向に1マスずつ移動します。
斜め移動がない場合、移動範囲の計算にはマンハッタン距離という考え方が非常に相性が良くなります。
マンハッタン距離とは、「縦に何マス、横に何マス移動したか」を合計した距離のことです。
たとえば、マス (0,0) から (3,2) へ移動する場合は、
・横に3マス
・縦に2マス
となるため、移動距離は 5 になります。
この方法を使えば、「移動力が5のユニットは、距離5以内のマスに移動できる」というルールを、シンプルに実装できます。
実装例:移動できるマスをすべて取得する
では実際に、ユニットの現在位置を中心に、移動できるマスをすべて集める処理を実装してみましょう。
以下のコードでは、
・ユニットの移動力以内か
・マップの外にはみ出していないか
をチェックしながら、移動可能なマスをリストに追加しています。
|
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 |
using UnityEngine; using System.Collections.Generic; public class MoveRangeCalculator : MonoBehaviour { public List<Vector2Int> CalculateMoveRange(Unit unit, GridMap gridMap, int moveRange) { List<Vector2Int> reachableCells = new List<Vector2Int>(); // 現在位置を中心に、移動力の範囲をチェック for (int x = -moveRange; x <= moveRange; x++) { for (int y = -moveRange; y <= moveRange; y++) { int distance = Mathf.Abs(x) + Mathf.Abs(y); // 移動力以内なら候補にする if (distance <= moveRange) { int targetX = unit.gridX + x; int targetY = unit.gridY + y; // マップ内かどうかを確認 if (gridMap.IsValidPosition(targetX, targetY)) { reachableCells.Add(new Vector2Int(targetX, targetY)); } } } } return reachableCells; } } |
この処理によって、「このユニットが今のターンで移動できるマス一覧」を取得できます。
あとは、この結果を使って
・マスをハイライト表示する
・クリックできる範囲を制限する
といった、SRPGらしい演出につなげていきます。

この段階では、まだ障害物や地形コストは考慮していません。次のステップで、それらを少しずつ追加していきます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
移動可能マスをハイライト表示する|「ここまで動ける」が一目でわかる

SRPGでは、ユニットを選択した瞬間に移動可能なマスが色付きで表示されるのが一般的です。
この演出があるだけで、操作性と分かりやすさが大きく向上します。
この章では、先ほど計算した「移動できるマス」を使って、ハイライト表示する方法を実装します。
ここまでできると、画面が一気にSRPGらしくなり、作っている実感も強くなります。
考え方:移動できるマスに「目印」を置くだけ
ハイライト表示の仕組みは、とてもシンプルです。
やっていることは、
– 移動できるマスの座標を取得する
– その位置に、半透明のオブジェクトを置く
これだけです。
今回は、ハイライト用のPrefabをマスの上に生成する方法で実装していきます。
実装例:移動範囲をハイライト表示するスクリプト
以下のスクリプトでは、ユニットが選択されたときに、移動可能なマスすべてにハイライト用Prefabを表示します。
|
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 |
using UnityEngine; using System.Collections.Generic; public class MoveRangeDisplay : MonoBehaviour { public GameObject highlightPrefab; private List<GameObject> highlights = new List<GameObject>(); public void ShowMoveRange(Unit unit, GridMap gridMap) { // すでに表示されているハイライトを消す ClearHighlights(); // 移動可能なマスを取得 List<Vector2Int> reachableCells = CalculateMoveRange(unit, gridMap); // 各マスにハイライトを表示 foreach (var cell in reachableCells) { Vector3 position = gridMap.GetWorldPosition(cell.x, cell.y) + Vector3.up * 0.1f; GameObject highlight = Instantiate(highlightPrefab, position, Quaternion.identity); highlights.Add(highlight); } } // 移動可能なマスを計算(障害物を考慮) List<Vector2Int> CalculateMoveRange(Unit unit, GridMap gridMap) { List<Vector2Int> reachableCells = new List<Vector2Int>(); for (int x = -unit.moveRange; x <= unit.moveRange; x++) { for (int y = -unit.moveRange; y <= unit.moveRange; y++) { int distance = Mathf.Abs(x) + Mathf.Abs(y); if (distance <= unit.moveRange) { int targetX = unit.gridX + x; int targetY = unit.gridY + y; if (gridMap.IsValidPosition(targetX, targetY)) { // 障害物があるマスは除外 if (!gridMap.HasObstacle(targetX, targetY)) { reachableCells.Add( new Vector2Int(targetX, targetY) ); } } } } } return reachableCells; } // ハイライトをすべて削除 void ClearHighlights() { foreach (var highlight in highlights) { Destroy(highlight); } highlights.Clear(); } } |
このスクリプトによって、移動できるマスだけがハイライト表示されるようになります。
障害物のあるマスは最初から除外しているため、「見た目上は移動できそうなのに、実際には行けない」といった混乱も防げます。
ハイライト用Prefabの作成方法
最後に、ハイライト表示に使うPrefabを用意します。
Unityエディタ上で、次の手順を行ってください。
- Hierarchyで右クリック → 3D Object → Quad
- 名前を MoveHighlight に変更
- Scale を (0.9, 0.9, 1) に設定
- 半透明のマテリアルを作成し、色を設定
- Prefabsフォルダにドラッグ&ドロップ
作成したPrefabを、MoveRangeDisplay の highlightPrefab に設定すれば準備完了です。

次のステップでは、このハイライトされたマスをクリックして、実際にどのルートで移動するかを決める処理を作っていきます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
BFS(幅優先探索)で移動ルートを決める|クリックしたマスまで自動で道を作る

SRPGでは、プレイヤーがマスをクリックすると、ユニットが自動で最短ルートを選んで移動します。
この章では、その「最短ルートを自動で見つける仕組み」を実装します。
ここで使うのが、ゲーム開発では定番のアルゴリズムBFS(幅優先探索)です。
まずは考え方:BFSは「近いところから順番に調べる」
BFSと聞くと難しく感じるかもしれませんが、やっていることはとても単純です。
考え方は次の通りです。
- 今いるマスから移動できるマスを調べる
- まだ調べていないマスだけを記録する
- ゴールに到達するまで、順番に広げていく
この方法を使うと、必ず最短距離のルートが見つかるという特徴があります。
SRPGのように「1マス移動=同じコスト」のゲームでは、BFSが非常に相性の良い探索方法になります。
実装例:BFSでスタートからゴールまでの経路を探す
以下は、開始マスから目的地マスまでの経路をBFSで探索するスクリプトです。
|
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 |
using UnityEngine; using System.Collections.Generic; public class Pathfinder : MonoBehaviour { public List<Vector2Int> FindPath( Vector2Int start, Vector2Int end, GridMap gridMap ) { Queue<Vector2Int> queue = new Queue<Vector2Int>(); Dictionary<Vector2Int, Vector2Int> cameFrom = new Dictionary<Vector2Int, Vector2Int>(); HashSet<Vector2Int> visited = new HashSet<Vector2Int>(); queue.Enqueue(start); visited.Add(start); // 上下左右の4方向 Vector2Int[] directions = new Vector2Int[] { new Vector2Int(1, 0), // 右 new Vector2Int(-1, 0), // 左 new Vector2Int(0, 1), // 上 new Vector2Int(0, -1) // 下 }; while (queue.Count > 0) { Vector2Int current = queue.Dequeue(); // ゴールに到達したら経路を作り直す if (current == end) { return ReconstructPath(cameFrom, start, end); } foreach (var direction in directions) { Vector2Int next = current + direction; // マップ外は無視 if (!gridMap.IsValidPosition(next.x, next.y)) continue; // すでに調べたマスは無視 if (visited.Contains(next)) continue; // 障害物があるマスは通れない if (gridMap.HasObstacle(next.x, next.y)) continue; queue.Enqueue(next); visited.Add(next); cameFrom[next] = current; } } // 経路が見つからなかった場合 return new List<Vector2Int>(); } // ゴールからスタートまでたどって経路を復元 List<Vector2Int> ReconstructPath( Dictionary<Vector2Int, Vector2Int> cameFrom, Vector2Int start, Vector2Int end ) { List<Vector2Int> path = new List<Vector2Int>(); Vector2Int current = end; while (current != start) { path.Add(current); current = cameFrom[current]; } path.Reverse(); return path; } } |
このコードを使うことで、クリックしたマスまでの最短ルートを自動で取得できるようになります。
BFSを使うメリット
BFSは、SRPGの移動処理と非常に相性の良いアルゴリズムです。
✔ BFSが向いている理由
移動コストがすべて同じ場合、
BFSは必ず最短ルートを見つけてくれます。
また、キューを使うだけなので、
アルゴリズム初心者でも実装しやすいのも大きな利点です。
ここまでできると、「マスをクリックすると、自然なルートでユニットが移動する」という、SRPGらしい動きが完成します。

次の章では、地形ごとに移動コストが違う場合に対応するコスト付き探索へ進んでいきます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
地形を考慮した移動ルートを作る|コスト付き探索の実装

ここまでで、「最短距離で移動するルート」は作れるようになりました。
しかし、SRPGを作っていると、必ず次の壁にぶつかります。
「森や山は、移動しづらくしたい」
「平地と同じ1マス扱いは不自然」
このように、地形によって移動の重さが変わる場合には、距離だけでなく 移動コスト を考慮した探索が必要になります。
この章では、SRPGで定番の「地形コストあり移動」を実現するために、ダイクストラ法を使った探索を実装します。
考え方:距離ではなく「合計コスト」でルートを選ぶ
これまで使ってきたBFSは、「何マスで行けるか」だけを基準にしていました。
一方、コスト付き探索では次のように考えます。
- 平地はスイスイ進める
- 森は少し疲れる
- 山はかなり大変
- 水はそもそも通れない
つまり、ゴールまでにかかる「合計コスト」が最小になるルートを探す、という考え方です。
この条件を満たす代表的なアルゴリズムがダイクストラ法です。
実装例:ダイクストラ法による経路探索
以下のスクリプトでは、地形ごとの移動コストを考慮しながら、移動可能な範囲内で最適なルートを探索します。
|
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 |
using UnityEngine; using System.Collections.Generic; using System.Linq; public class CostBasedPathfinder : MonoBehaviour { public List<Vector2Int> FindPath( Vector2Int start, Vector2Int end, GridMap gridMap, int maxMoveCost ) { Dictionary<Vector2Int, int> costSoFar = new Dictionary<Vector2Int, int>(); Dictionary<Vector2Int, Vector2Int> cameFrom = new Dictionary<Vector2Int, Vector2Int>(); List<Vector2Int> openSet = new List<Vector2Int>(); HashSet<Vector2Int> closedSet = new HashSet<Vector2Int>(); openSet.Add(start); costSoFar[start] = 0; Vector2Int[] directions = new Vector2Int[] { new Vector2Int(1, 0), new Vector2Int(-1, 0), new Vector2Int(0, 1), new Vector2Int(0, -1) }; while (openSet.Count > 0) { // 現在もっともコストが低いマスを選ぶ Vector2Int current = openSet.OrderBy(n => costSoFar[n]).First(); openSet.Remove(current); closedSet.Add(current); if (current == end) { return ReconstructPath(cameFrom, start, end); } foreach (var direction in directions) { Vector2Int next = current + direction; if (!gridMap.IsValidPosition(next.x, next.y)) continue; if (closedSet.Contains(next)) continue; if (gridMap.HasObstacle(next.x, next.y)) continue; int moveCost = gridMap.GetMoveCost(next.x, next.y); int newCost = costSoFar[current] + moveCost; // 移動力を超える場合は除外 if (newCost > maxMoveCost) continue; if (!costSoFar.ContainsKey(next) || newCost < costSoFar[next]) { costSoFar[next] = newCost; cameFrom[next] = current; if (!openSet.Contains(next)) { openSet.Add(next); } } } } return new List<Vector2Int>(); } // 経路を復元 List<Vector2Int> ReconstructPath( Dictionary<Vector2Int, Vector2Int> cameFrom, Vector2Int start, Vector2Int end ) { List<Vector2Int> path = new List<Vector2Int>(); Vector2Int current = end; while (current != start) { path.Add(current); current = cameFrom[current]; } path.Reverse(); return path; } } |
この実装により、「距離は短いが山を越えるルート」より「少し遠回りでも平地を通るルート」といった、自然な判断ができるようになります。
地形ごとの移動コストを設定する
次に、マップ側で「このマスはどれくらい移動が大変か」を定義します。
以下は、地形タイプごとに移動コストを返すシンプルな例です。
|
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 |
public class GridMap : MonoBehaviour { public enum TerrainType { Plain, // 平地(コスト1) Forest, // 森(コスト2) Mountain, // 山(コスト3) Water // 水(移動不可) } private TerrainType[,] terrainMap; public int GetMoveCost(int x, int y) { TerrainType terrain = terrainMap[x, y]; switch (terrain) { case TerrainType.Plain: return 1; case TerrainType.Forest: return 2; case TerrainType.Mountain: return 3; case TerrainType.Water: return 999; // 実質的に通行不可 default: return 1; } } } |
このように設定することで、地形によって移動しやすさが変わるSRPGらしい挙動を実現できます。
ここまで実装できれば、SRPGのキャラクター移動システムとしてはかなり完成度の高い状態です。

あとは、移動アニメーションやエフェクトを加えることで、さらに「遊べるゲーム」に近づいていきます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
実装例:SRPGでそのまま使える「完成形」の移動システム

ここまでの記事で、次の要素を順番に作ってきました。
移動範囲の計算、ハイライト表示、経路探索、そしてコスト付き探索。
この章では、それらをすべてつなぎ合わせた「実際のゲームでそのまま使える移動システム」を紹介します。
「ユニットを選択して、マスをクリックすると、自然なルートでスムーズに移動する」――SRPGでおなじみの操作感を、ここで完成させます。
この移動システムはどんな構成なのか
今回の実装は、次のような役割分担で作られています。
- Unit:ユニット自身の情報(座標や移動力)を管理
- GridMap:マスの位置や地形情報を管理
- MoveRangeDisplay:移動できるマスを表示
- Pathfinder:移動ルートを計算
- UnitMovement:それらをまとめて制御する司令塔
すべてを1つの巨大なスクリプトにせず、役割ごとに分けているのがポイントです。
この考え方はSRPGだけでなく、アクションゲームやRPGなど、ほとんどのゲームで共通して使われています。
実装例:移動の流れをすべて管理するスクリプト
以下が、「ユニット選択 → 移動範囲表示 → クリック → 経路探索 → 実際に移動」までをまとめた実装例です。
|
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 System.Collections; using System.Collections.Generic; public class UnitMovement : MonoBehaviour { public Unit unit; public GridMap gridMap; public MoveRangeDisplay moveRangeDisplay; public Pathfinder pathfinder; private List<Vector2Int> currentPath; private bool isMoving = false; // ユニットが選択されたとき public void OnUnitSelected() { // 移動可能範囲を表示 moveRangeDisplay.ShowMoveRange(unit, gridMap); } // マスがクリックされたとき public void OnCellClicked(int x, int y) { if (isMoving) return; Vector2Int start = new Vector2Int(unit.gridX, unit.gridY); Vector2Int target = new Vector2Int(x, y); // 経路を探索 currentPath = pathfinder.FindPath(start, target, gridMap); if (currentPath.Count > 0) { StartCoroutine(MoveAlongPath()); } } // 経路に沿って移動 IEnumerator MoveAlongPath() { isMoving = true; moveRangeDisplay.ClearHighlights(); foreach (var cell in currentPath) { Vector3 targetPosition = gridMap.GetWorldPosition(cell.x, cell.y) + Vector3.up * 0.5f; while (Vector3.Distance( transform.position, targetPosition) > 0.1f) { transform.position = Vector3.MoveTowards( transform.position, targetPosition, 5f * Time.deltaTime); yield return null; } unit.SetPosition(cell.x, cell.y, gridMap); } isMoving = false; unit.OnMoveComplete(); } } |
このコードで、SRPGに必要な基本的な移動体験がすべて揃います。
なぜコルーチンを使っているのか
移動処理に コルーチン を使っている理由は、「1マスずつ、時間をかけて移動させる」ためです。
もし一瞬で座標を書き換えるだけだと、ユニットはワープしたように見えてしまいます。
1マスずつ補間しながら動かすことで、プレイヤーは「ちゃんと移動している」と感じられます。
この考え方はSRPGだけでなく、ロックマンの移動や、2Dアクションの歩行処理でも同じように使われています。
この実装パターンは他のゲームでも使われている
今回の構成は、実はかなり汎用的です。
- SRPG:マス移動+経路表示
- ロックマン系:マスはないが、1フレームずつの移動制御
- ターン制RPG:位置管理と移動演出
- シミュレーションゲーム:ユニット単位の行動管理
特に「計算」と「表示」と「移動」を分ける設計は、規模が大きくなっても破綻しにくいのが特徴です。
初心者がつまずきやすいポイントと注意点
この移動システムで、初心者がよく詰まるポイントも押さえておきましょう。
まず多いのが、「移動中にもう一度クリックできてしまう」問題です。
これを防ぐために、isMoving フラグで操作を制限しています。
次に、グリッド座標とワールド座標を混同してしまうケースです。
必ず GridMap を通して変換するようにしましょう。
また、最初は「移動範囲内かどうかのチェック」を省略しても構いません。
完璧を目指さず、まず動くものを作るのが大切です。
ここまでできれば、SRPGの基礎は完成
この移動システムが動けば、SRPGの根幹部分はほぼ完成と言っても問題ありません。
あとは、
- 攻撃範囲の表示
- 行動後の待機処理
- 敵AIの移動
などを追加していくだけです。
「ユニットを選んで、考えて、動かす」その楽しさを支えているのが、まさにこの移動システムです。

ここまで実装できたら、もう立派なゲーム開発者です。
あとは少しずつ肉付けして、自分だけのSRPGに育てていきましょう。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
よくある質問(FAQ)

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

SRPGのキャラクター移動は、BFSで実装できます。
まずはシンプルな実装から始めて、徐々に機能を追加しましょう。
✅ 今日から始める3ステップ
- ステップ1:移動範囲の計算を実装する(所要2時間)
- ステップ2:ハイライト表示を実装する(所要1時間)
- ステップ3:BFSによる経路探索を実装する(所要3時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる



コメント