ランダムマップ生成は、RTSのリプレイ性を高めます。
毎回異なる地形で、戦略が変わります。
この記事では、実装方法を詳しく解説します。
✨ この記事でわかること
- ノイズ関数を使った地形生成
- タイル分類システムの実装
- 資源配置ロジックの実装
- 固定マップとの比較
- 実装例とコード

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

RTSのランダムマップ生成では、いきなり複雑なアルゴリズムを考える必要はありません。
まずは「地形を数値として扱い、その数値をもとにタイルを決める」という考え方を理解することが重要です。
ノイズ関数は、その地形の数値(高さ・起伏)を自動で作ってくれる仕組みです。
人が手で高さを決める代わりに、ノイズ関数がそれを担当します。
なぜノイズ関数を使うのか
完全なランダム(0〜1を完全にランダム生成)を使うと、地形はバラバラになりやすく、RTSとして遊びにくくなります。
ノイズ関数は、近くの値がなめらかにつながるため、自然な地形を作りやすいという特徴があります。
特にPerlinノイズは、「山の近くはまた山になりやすい」「平地がある程度まとまってできる」
といった、地形らしい傾向を簡単に作れるため、ゲーム開発でよく使われます。
Perlinノイズで地形の“元データ”を作る
ここでは、マップ全体に対してPerlinノイズを使い、0.0〜1.0の数値を持つ配列を作ります。
この数値は、あとで「水か、草原か、山か」を判断するための材料になります。
|
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 |
using UnityEngine; public class MapGenerator : MonoBehaviour { public int mapWidth = 100; public int mapHeight = 100; public float noiseScale = 0.1f; public int seed = 0; private float[,] noiseMap; void Start() { GenerateMap(); } void GenerateMap() { noiseMap = new float[mapWidth, mapHeight]; // シードを設定(0の場合はランダムに決定) if (seed == 0) { seed = Random.Range(0, 100000); } // Perlinノイズで地形用の数値を生成 for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { float sampleX = (float)x / mapWidth * noiseScale; float sampleY = (float)y / mapHeight * noiseScale; noiseMap[x, y] = Mathf.PerlinNoise(sampleX + seed, sampleY + seed); } } CreateMapVisualization(); } void CreateMapVisualization() { // 数値を色で可視化(理解しやすくするため) for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { float value = noiseMap[x, y]; Color color = Color.Lerp(Color.blue, Color.green, value); // タイルやマテリアルに色を反映 } } } } |
この時点では、まだ「水」「草原」「山」といった地形は決めていません。
あくまで地形のもとになる数値を作っている段階です。

初心者がつまずきやすいポイントとして、「このコードだけで完成した地形が出てくる」と誤解しがちですが、ここで得られるのは地形を判断するための数値データです。
複数オクターブで地形にメリハリを出す
Perlinノイズを1回だけ使うと、全体的にのっぺりした地形になりやすくなります。
そこで、複数のノイズを重ねる「オクターブ」という手法を使います。
簡単に言うと、大きな起伏(山と平地)+細かい起伏(でこぼこ)を合成するイメージです。
|
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 |
public class AdvancedMapGenerator : MonoBehaviour { public int octaves = 4; public float persistence = 0.5f; public float lacunarity = 2f; float GenerateNoise(int x, int y, float scale, int seed) { float amplitude = 1f; float frequency = 1f; float noiseValue = 0f; float maxValue = 0f; for (int i = 0; i < octaves; i++) { float sampleX = (float)x / mapWidth * scale * frequency; float sampleY = (float)y / mapHeight * scale * frequency; float perlinValue = Mathf.PerlinNoise(sampleX + seed, sampleY + seed) * 2f - 1f; noiseValue += perlinValue * amplitude; maxValue += amplitude; amplitude *= persistence; frequency *= lacunarity; } return noiseValue / maxValue; } } |
persistenceは「細かい起伏の強さ」、lacunarityは「起伏の細かさ」を調整するパラメータです。
ここで注意したいのは、数値を複雑にしすぎないことです。
RTSでは、地形がリアルすぎると逆にプレイしづらくなる場合があります。
まずは少ないオクターブで試し、「このくらいなら遊びやすい」と感じる地形を基準に調整していくのがおすすめです。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
タイル分類システムの実装

前の章で作成したノイズ値は、そのままではまだ「ただの数値」です。
この章では、その数値をもとにして、実際にゲームで使う地形(タイル)へ変換する仕組みを作ります。
RTSでは、地形ごとに移動のしやすさや通行可否が異なります。
そのため、「見た目」だけでなく「ゲームルールとしての意味」を持たせて分類することが重要です。
ノイズ値をタイルに変換する考え方
基本的な考え方はシンプルです。
ノイズ関数で生成した0.0〜1.0の数値を、あらかじめ決めた範囲ごとに区切り、それぞれを水・草原・山といったタイルに割り当てます。
ここで大切なのは、「リアルな地形を再現すること」よりもRTSとして遊びやすい地形になるかという視点です。
タイルタイプの定義
まずは、ゲーム内で使うタイルの種類をenumで定義します。
この段階では、細かく作りすぎず、役割がはっきりしたタイルに絞るのがおすすめです。
|
1 2 3 4 5 6 7 8 9 |
public enum TileType { Water, // 水:基本的に通行不可 Sand, // 砂:移動しづらい Grass, // 草原:基本地形 Forest, // 森:移動コスト高め Mountain // 山:さらに移動しづらい } |
初心者がつまずきやすいポイントとして、最初からタイルの種類を増やしすぎると、バランス調整が一気に難しくなります。
まずは5種類程度に抑え、あとから必要に応じて増やすほうが安全です。
ノイズ値からタイルを判定する
次に、ノイズ値を受け取り、どのタイルにするかを決定する処理を実装します。
ここでは、if文を使ったシンプルな判定を行います。
|
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 |
public class TileClassifier : MonoBehaviour { public TileType ClassifyTile(float noiseValue) { if (noiseValue < 0.3f) { return TileType.Water; } else if (noiseValue < 0.4f) { return TileType.Sand; } else if (noiseValue < 0.6f) { return TileType.Grass; } else if (noiseValue < 0.8f) { return TileType.Forest; } else { return TileType.Mountain; } } } |
この処理によって、「数値としての地形」が「意味を持つ地形」へと変わります。
ここで注意したいのは、この閾値に絶対的な正解はないという点です。
実際のゲームプレイを想定しながら、少しずつ調整していきます。
地形がゲーム性に与える影響を設定する
RTSでは、地形によってユニットの動きが変わることで戦略が生まれます。
そのため、タイルごとに移動コストを設定しておくと、地形を活かしたプレイがしやすくなります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public int GetMoveCost(TileType tileType) { switch (tileType) { case TileType.Water: return 999; // 実質的に移動不可 case TileType.Sand: return 2; case TileType.Grass: return 1; case TileType.Forest: return 2; case TileType.Mountain: return 3; default: return 1; } } |
水を完全に移動不可にすることで、マップに自然な分断やルート選択が生まれます。
ただし、分断しすぎると一方的な展開になりやすいため、水や山の割合には注意が必要です。
タイル分類の閾値の一例
水:0.0〜0.3(低地・通行不可)
砂:0.3〜0.4(海岸・移動しづらい)
草原:0.4〜0.6(基本となる平地)
森:0.6〜0.8(視界・移動に影響)
山:0.8〜1.0(高地・移動コスト大)
このように、ノイズ値 → タイル → 移動ルールという流れを作ることで、ランダムに生成された地形が、RTSとして意味のあるマップになります。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
資源配置ロジックの実装

地形が完成したら、次に考えるのが資源の配置です。
RTSにおいて資源は、ゲームのテンポや戦略を大きく左右する重要な要素になります。
ランダムマップ生成では、資源も完全にランダムに置いてしまうと、「一方だけが有利になる」「そもそも資源にたどり着けない」といった問題が起きやすくなります。
そのため、地形に応じた配置ルールをあらかじめ決めておくことが重要です。
資源配置で意識したい基本ルール
まずは、現実的で分かりやすいルールを設定します。
例えば、次のような考え方です。
金や石は山の近くにあるほうが自然ですし、木材は森の中やその周辺にあるほうが違和感がありません。
このように、どの資源が、どの地形に紐づくのかを先に決めておくと、ランダム生成でも破綻しにくくなります。
資源配置の全体構造
今回の実装では、次の流れで資源を配置します。
まず、マップ全体からランダムな座標を選び、その場所が「好ましい地形かどうか」をチェックします。
条件を満たしていれば、そこに資源を配置します。
条件に合わない場合はやり直し、一定回数試しても配置できなければ諦める、という仕組みです。
資源配置クラスの実装
|
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 |
using UnityEngine; using System.Collections.Generic; public class ResourcePlacer : MonoBehaviour { public GameObject goldNodePrefab; public GameObject woodNodePrefab; public GameObject stoneNodePrefab; public int goldNodeCount = 10; public int woodNodeCount = 15; public int stoneNodeCount = 8; public void PlaceResources(TileType[,] map) { // 金鉱を配置(山の近く) PlaceResourceNodes(goldNodePrefab, goldNodeCount, map, TileType.Mountain); // 木材を配置(森の中) PlaceResourceNodes(woodNodePrefab, woodNodeCount, map, TileType.Forest); // 石を配置(山の近く) PlaceResourceNodes(stoneNodePrefab, stoneNodeCount, map, TileType.Mountain); } } |
ここでは、資源ごとに「好ましい地形」を指定しています。
このルールがあることで、資源配置に一貫性が生まれます。
初心者がつまずきやすい点として、最初から「完全な公平配置」を目指す必要はありません。
まずは自然で分かりやすい配置を優先しましょう。
個別の資源配置ロジック
実際にマップ上へ資源を置く処理が、次のメソッドです。
|
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 |
void PlaceResourceNodes(GameObject prefab, int count, TileType[,] map, TileType preferredTile) { int placed = 0; int attempts = 0; int maxAttempts = count * 10; while (placed < count && attempts < maxAttempts) { int x = Random.Range(0, map.GetLength(0)); int y = Random.Range(0, map.GetLength(1)); // 好ましい地形か、その近くかをチェック if (map[x, y] == preferredTile || IsNearTileType(x, y, map, preferredTile)) { // 他の資源から近すぎないかチェック if (IsValidResourcePosition(x, y)) { Vector3 position = new Vector3(x, 0, y); Instantiate(prefab, position, Quaternion.identity); placed++; } } attempts++; } } |
ここで重要なのは、「必ず指定した数を配置しきる」ことよりも、無理な配置をしないことです。
maxAttemptsを設けているのは、条件に合う場所が見つからないまま無限ループに陥るのを防ぐためです。
周囲の地形を考慮する
指定した地形そのものが少ない場合でも、その近くであれば配置を許可すると、資源が極端に偏るのを防げます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
bool IsNearTileType(int x, int y, TileType[,] map, TileType tileType) { for (int dx = -2; dx <= 2; dx++) { for (int dy = -2; dy <= 2; dy++) { int nx = x + dx; int ny = y + dy; if (nx >= 0 && nx < map.GetLength(0) && ny >= 0 && ny < map.GetLength(1)) { if (map[nx, ny] == tileType) { return true; } } } } return false; } |
この処理によって、「山のど真ん中にしか資源がない」といった極端な配置を避けられます。
他の資源との距離チェック
IsValidResourcePositionでは、すでに配置された資源との距離をチェックします。
ここを省略すると、資源が一点に固まりすぎてしまい、RTSとしての戦略性が下がる原因になります。
最初は簡単な距離判定だけでも問題ありません。
重要なのは、「資源がばらける」という結果を作ることです。

資源配置は、見た目だけでなくゲームバランスにも直結します。まずは「自然で分かりやすい配置」を目指し、そのあとで公平性や対戦バランスを調整していきましょう。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
固定マップとの比較

ランダムマップと固定マップ、それぞれにメリットがあります。
比較しましょう。
| 項目 | ランダムマップ | 固定マップ |
| リプレイ性 | 高い(毎回異なる地形) | 低い(同じ地形) |
| バランス調整 | 難しい(地形が変わる) | 容易(固定されている) |
| 実装コスト | 高い(生成ロジックが必要) | 低い(手動で配置) |
| ストーリー性 | 低い(地形がランダム) | 高い(地形を設計できる) |
ランダムマップは、リプレイ性が高いです。
固定マップは、バランス調整とストーリー性に優れています。
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 |
using UnityEngine; public class CompleteMapGenerator : MonoBehaviour { public MapGenerator mapGenerator; public TileClassifier tileClassifier; public ResourcePlacer resourcePlacer; public int mapWidth = 100; public int mapHeight = 100; private TileType[,] tileMap; void Start() { GenerateCompleteMap(); } void GenerateCompleteMap() { // 1. ノイズで地形を生成 float[,] noiseMap = mapGenerator.GenerateNoiseMap(mapWidth, mapHeight); // 2. タイルを分類 tileMap = new TileType[mapWidth, mapHeight]; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { tileMap[x, y] = tileClassifier.ClassifyTile(noiseMap[x, y]); } } // 3. 資源を配置 resourcePlacer.PlaceResources(tileMap); // 4. マップを可視化 VisualizeMap(); } void VisualizeMap() { // タイルの種類に応じて、色やモデルを配置 } } |
このコードで、完全なランダムマップ生成システムが実装できます。
ノイズ生成、タイル分類、資源配置を統合しています。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
よくある質問(FAQ)

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

ランダムマップ生成は、ノイズ関数から始めましょう。
タイル分類と資源配置を組み合わせることで、自然な地形が生成できます。
✅ 今日から始める3ステップ
- ステップ1:Perlinノイズで地形を生成する(所要2時間)
- ステップ2:タイル分類システムを実装する(所要2時間)
- ステップ3:資源配置ロジックを実装する(所要2時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる



コメント