Unity本格入門 実装編:Chapter 5 ゲームの舞台を作ってみよう
題材・出典: 技術評論社刊『作って学べる Unity本格入門[Unity 6対応版]』(賀好 昭仁 著)に基づく学習補助の解説です。書籍の代替提供を目的とせず、コード掲載は学習上必要な範囲にとどめます。本シリーズ目次
対象読者:Unity エディタの基本操作を理解しており、C# のコードを追いながら読める方
この記事では、いきのこバトル(IkinokoBattle)の MainScene を題材に、Terrain による地形・木草・水・Skybox・昼夜サイクルの実装を読み解きます。書籍 Chapter 5(5-1〜5-5) に対応します。
記事の目次
おすすめの読み方
セクション一覧
ポイント一覧
- ① Terrain の作成と基本設定(Size・Resolution)
- ② 起伏ブラシ(Raise/Lower・Smooth・Stamp)の使い方
- ③ Tree Painter と Detail Mesh で木・草を配置する
- ④ Water Shader の配置と Wind Zone の設定
- ⑤ Skybox マテリアルの作成と Lighting Settings への適用
- ⑥
RoundLightで Directional Light を回転させ昼夜を表現する - ⑦ Terrain のパフォーマンス上の弱点と Static Mesh との使い分け
題材の範囲
Chapter 5 で扱う内容は「ゲームが展開される 3D の舞台」を Unity 上で構築することです。いきのこバトルの MainScene には、書籍で学ぶ次の要素がすべて盛り込まれています。
| 書籍の節 | テーマ | IkinokoBattle での実装 |
|---|---|---|
| 5-1 | プロジェクト作成・Asset Store | パッケージのインポート済み |
| 5-2 | Terrain で地形を作る | MainScene の Terrain オブジェクト |
| 5-3 | 木・草を配置する | Foliage Creator Tools・GrassFlowers アセット |
| 5-4 | 水・風の演出 | Simple Water Shader・Wind Zone |
| 5-5 | Skybox と昼夜サイクル | Skybox マテリアル + RoundLight.cs |
この記事でとくに深掘りするポイント:書籍では「設定方法」を丁寧に解説しますが、この記事では「なぜそう設定するのか」という設計の意図に踏み込みます。とくに ⑥ RoundLight.cs は、わずか 6 行のコードで昼夜サイクルを実現する好例として詳しく読みます。
補足:IkinokoBattle には昼夜サイクルをさらに拡張した MainSceneController(ライトの輝度を DOTween で変化させる処理)も存在します。そちらは 機能別解説編・第2回 で詳しく扱っています。この記事では「光の向きを変える」RoundLight に集中します。
プロジェクトの構成(今回のファイル・アセット)
スクリプト(1ファイル)
| ファイル | 役割 |
|---|---|
RoundLight.cs |
Directional Light をY軸回転させて太陽の動きを表現 |
アセット(Unity エディタ上で設定)
| アセット名 | パッケージ | 用途 |
|---|---|---|
| Terrain | Unity 標準 | 地形の土台 |
| TerrainTexturesPackFree | 無料テクスチャパック | 地面のペイント |
| Conifers [BOTD] (Forst) | Foliage Creator Tools | 針葉樹の配置 |
| GrassFlowers | 無料アセット | 草・花の配置 |
| Simple Water Shader | IgniteCoders | 水面の表現 |
| Skybox マテリアル | Unity 標準 Shader | 空の背景 |
シーン
MainScene.unity― 地形・木草・水・Skybox・RoundLightがすべて含まれている
コードを全部見てみよう
Chapter 5 の実装をコードの側から表すスクリプトは RoundLight.cs の 1 ファイルです。
using UnityEngine;
public class RoundLight : MonoBehaviour
{
private void Update()
{
// Y軸に対して、1秒間に-12度回転させる
transform.Rotate(new Vector3(0, -12) * Time.deltaTime);
}
}わずか 6 行ですが、「毎フレーム・一定速度で・Y軸を中心に回す」という昼夜サイクルの核心を担っています。後述のポイント解説でこの 6 行を丁寧に読み解きます。
① Terrain の作成と基本設定(Size・Resolution)
Terrain とは:Unity に組み込まれた地形作成ツールです。「ハイトマップ(高さのデータの配列)」を内部で持ち、ブラシで山や谷を描くように地形を整えられます。
作成手順:メニューの GameObject → 3D Object → Terrain で配置されます。IkinokoBattle の MainScene にはすでに配置済みの Terrain があります。
設定で押さえるべき 2 つの数値
Inspector の Terrain Settings にある以下の値が舞台の「スケール感」を決めます。
| 項目 | IkinokoBattle の設定例 | 意味 |
|---|---|---|
| Terrain Width / Length | 500 前後 | 地形の平面サイズ(メートル換算) |
| Terrain Height | 100〜200 | 最大の盛り上がり高さ |
| Heightmap Resolution | 513 や 1025 | 高さデータの解像度。2ⁿ + 1 の値を推奨 |
なぜ Heightmap Resolution は 2ⁿ + 1 か:内部でテクスチャとして扱われるため、GPU に適した 2 の累乗サイズ(512, 1024)に「境界ピクセル」を 1 つ加えた値が推奨されています。
② 起伏ブラシ(Raise/Lower・Smooth・Stamp)の使い方
Terrain を選択して Inspector 上部に並ぶブラシアイコンが地形編集ツールです。よく使う 3 種類を整理します。
| ブラシ名 | 操作 | 使いどころ |
|---|---|---|
| Raise / Lower | ドラッグで盛り上げ / Shift+ドラッグで削る | 山・谷の大まかな形を作る |
| Smooth | ドラッグで隣接ピクセルを平均化 | 急な段差をなめらかにする |
| Stamp | クリックで固定高さを押し付ける | 台地や崖など一定高さの地形を作る |
| Set Height | 指定高さに揃える | 川床・池底など水平面を作る |
IkinokoBattle の地形を観察すると:中央に開けた平地(バトルエリア)があり、周囲に向かって緩やかに高くなっています。Smooth ブラシを多用した、なだらかなフィールドが特徴です。
③ Tree Painter と Detail Mesh で木・草を配置する
Terrain には「木」と「草」を専用ツールで大量配置する仕組みが備わっています。
木の配置(Tree Painter)
Inspector の「木のアイコン」タブを開き、Edit Trees → Add Tree で Prefab を登録します。IkinokoBattle では Conifers [BOTD](Foliage Creator Tools パッケージ)の針葉樹 Prefab を使っています。
登録後はブラシを使ってドラッグするだけで木が密度に応じて自動分散配置されます。パラメータのポイントは以下の通りです。
| パラメータ | 役割 |
|---|---|
| Brush Size | 一度に塗る範囲の半径 |
| Tree Density | 範囲内に生成する木の密度 |
| Tree Height / Width | ランダムなスケールの範囲(変化をつけて自然に見せる) |
草の配置(Detail Mesh)
「草のアイコン」タブで Mesh または Texture を登録し、同じようにブラシで塗ります。IkinokoBattle では GrassFlowers アセットの草 Mesh を使用しています。
パフォーマンス注意:Detail Mesh はカメラに近い範囲だけ描画する
Detail Distanceの設定が重要です。大量の草はドローコール増加の原因になるため、Detail Densityを絞りDetail Distanceを適切に設定します。
④ Water Shader の配置と Wind Zone の設定
水の配置(Simple Water Shader)
IkinokoBattle では IgniteCoders の Simple Water Shader を使っています。Plane(平面メッシュ)に専用マテリアルを割り当てるだけで、波紋や反射を持つ水面を表現できます。
配置手順の概要:
GameObject → 3D Object → Planeを作成Simple Water Shaderマテリアルをアサイン- 水面の高さ(Y座標)を
Set Heightで作った地面の溝に合わせる
補足:Terrain の
Set Heightブラシで水底となる低地を先に作り、そこへ Plane を沈める形が自然に見えます。
風の表現(Wind Zone)
GameObject → 3D Object → Wind Zone で配置します。Wind Zone は Terrain の草・木と Particle System に影響します。
| パラメータ | 役割 |
|---|---|
| Mode | Directional(一方向の風)/ Spherical(球状に広がる風) |
| Main | 風の基本強度 |
| Turbulence | 乱れ具合(大きいほどランダムに揺れる) |
| Pulse Magnitude | 風の強弱の波 |
⑤ Skybox マテリアルの作成と Lighting Settings への適用
Skybox とは:シーンをすっぽり包む「巨大な球または立方体」にテクスチャを貼り、背景の空を表現する手法です。カメラがどこを向いても空が見えるのはこの仕組みによります。
マテリアルの作成手順
Assets上で右クリック →Create → Materialで新規マテリアルを作成- Inspector の
ShaderをSkybox/6 Sided(6面テクスチャ方式)またはSkybox/Procedural(プロシージャル生成)に変更 - 各テクスチャスロットにキューブマップ画像をアサイン
シーンへの適用
Window → Rendering → Lighting を開き、Environment タブの Skybox Material に作成したマテリアルをセットします。
Procedural Skybox の便利な点:テクスチャ不要で太陽の位置・大気の色・地平線の色をパラメータで調整できます。IkinokoBattle のように昼夜を動的に変化させる場合、
Directional Lightの向きに連動して空の色が自動で変わるため相性が良いです。
⑥ RoundLight で Directional Light を回転させ昼夜を表現する
Chapter 5 のメインスクリプトです。改めてコード全文を読みましょう。
public class RoundLight : MonoBehaviour
{
private void Update()
{
// Y軸に対して、1秒間に-12度回転させる
transform.Rotate(new Vector3(0, -12) * Time.deltaTime);
}
}transform.Rotate とは
transform.Rotate(回転量) は、毎フレーム「現在の向きからさらに指定した角度だけ回転させる」メソッドです。累積して回転し続けるため、光がぐるりと一周します。
new Vector3(0, -12) の意味
Vector3(x, y, z) で 3 軸それぞれの回転速度を指定します。ここでは x = 0, y = -12, z = 0 なので Y軸を中心に毎秒 -12 度回転します。マイナスなので反時計回り(右回り)です。
* Time.deltaTime が不可欠な理由
Time.deltaTime は「前フレームからの経過秒数」です。フレームレートが 60fps なら約 0.016、30fps なら約 0.033 になります。
1フレームの回転量 = -12度/秒 × 経過秒数Time.deltaTime を掛けることで、フレームレートに関係なく「1秒間に必ず -12 度回転する」が保証されます。これを省くとハイスペック PC では光が猛スピードで回転し、低スペック端末では遅くなってしまいます。
1周するのに何秒かかるか
360度 ÷ 12度/秒 = 30秒つまりこの設定では 30秒で太陽(Directional Light)が一周します。昼夜の表現として使うなら、実際のゲームではこの回転速度をゲーム内時間の進み方に合わせて調整することになります。
Procedural Skybox との連動
Skybox/Procedural マテリアルには「Sun Source」という設定項目があり、そこに Directional Light をアサインすると、ライトの回転に合わせて空の色も自動で変化します。朝焼け・昼の青空・夕焼け・夜空が自動的に演出されます。
IkinokoBattle の昼夜サイクルの全体像:
RoundLight.csはライトの「向き」を担当します。ライトの「輝度(intensity)」は別スクリプト(MainSceneController)が DOTween でアニメーションさせています。2つのスクリプトが役割を分担することで、向き・明るさをそれぞれ独立して制御できる設計です。
⑦ Terrain のパフォーマンス上の弱点と Static Mesh との使い分け
Terrain は手軽に広大な地形を作れる反面、いくつかのパフォーマンス上の特性があります。
Terrain の特性
| 特性 | 内容 |
|---|---|
| ドローコール | Terrain 自体は 1 ドローコール。ただしペイントしたテクスチャの枚数が増えるとシェーダーの処理が増加する |
| Detail Mesh(草) | カメラに近い範囲のみ描画するが、密度が高いと高負荷になる |
| Tree Collider | 木が多いと物理エンジンの計算コストが増加。Billboard 表示との組み合わせで遠景の木の頂点数を抑える |
| LOD | Terrain 自体に自動 LOD はないが、Tree には自動的に Billboard LOD が適用される |
Static Mesh との使い分け
競技ゲームやモバイルゲームでは、Terrain を使わずあらかじめ形を作った Static Mesh(3D モデル) を地面として使うことがあります。
| Terrain | Static Mesh | |
|---|---|---|
| 編集 | Unity エディタ上でブラシで直感的に編集 | DCC ツール(Blender 等)で作成 |
| パフォーマンス | 広大な地形を単一オブジェクトで扱える | 細かく分割し GPU インスタンシングで効率化 |
| 向き | 広いフィールド型のゲーム | アクション・競技系・モバイル向け |
IkinokoBattle のような探索・生存型のゲームには広いフィールドが必要なため、Terrain が適切な選択です。
コードの流れを整理しよう
ゲーム起動〜昼夜サイクルの流れ
MainScene 起動
└─ Directional Light(GameObject)に RoundLight コンポーネントをアタッチ
└─ Update():毎フレーム呼び出し
└─ transform.Rotate(new Vector3(0, -12) * Time.deltaTime)
└─ Directional Light が Y 軸を中心に毎秒 -12 度回転
└─ Procedural Skybox が Light の向きを検知して空の色を自動更新
└─ 朝 → 昼 → 夕 → 夜 → 朝 のサイクルを 30 秒で繰り返す地形・アセットの配置関係
MainScene
├─ Terrain(地形の土台)
│ ├─ TerrainTexturesPack(地面テクスチャ)
│ ├─ Conifers [BOTD](針葉樹 Tree Painter で配置)
│ └─ GrassFlowers(Detail Mesh で配置)
├─ Plane(水面)
│ └─ Simple Water Shader マテリアル
├─ Wind Zone(草・木の揺れ)
├─ Directional Light
│ └─ RoundLight.cs(回転)
└─ Skybox(Lighting Settings で設定)
└─ Procedural Skybox(Directional Light の向きと連動)自分でカスタマイズしてみよう!
課題 A:昼夜サイクルの速さを変える
RoundLight.cs の -12 を変えると 1 日の長さが変わります。
// 例:1日を 10 秒にする(360 ÷ 10 = 36 度/秒)
transform.Rotate(new Vector3(0, -36) * Time.deltaTime);
// 例:1日を 5 分(300 秒)にする(360 ÷ 300 = 1.2 度/秒)
transform.Rotate(new Vector3(0, -1.2f) * Time.deltaTime);課題 B:回転速度を Inspector から調整できるようにする
マジックナンバー -12 を SerializeField で外部から変えられるようにします。
public class RoundLight : MonoBehaviour
{
[SerializeField] private float degreesPerSecond = 12f;
private void Update()
{
transform.Rotate(new Vector3(0, -degreesPerSecond) * Time.deltaTime);
}
}Inspector のスライダーでリアルタイムに速さを調整できるようになります。
課題 C:昼夜の時刻をゲームから取得できるようにする
現在の「1日の中の何時か(0.0〜1.0)」を他のスクリプトから参照できるようにします。
public class RoundLight : MonoBehaviour
{
[SerializeField] private float degreesPerSecond = 12f;
/// <summary>
/// 1日の進行度(0.0 = 夜明け、0.5 = 正午、1.0 = 翌夜明け)
/// </summary>
public float TimeOfDay { get; private set; }
private float _totalRotation = 0f;
private void Update()
{
float delta = degreesPerSecond * Time.deltaTime;
_totalRotation = (_totalRotation + delta) % 360f;
TimeOfDay = _totalRotation / 360f;
transform.Rotate(new Vector3(0, -delta));
}
}RoundLight.Instance.TimeOfDay を参照することで、満腹度の減少スピードや敵のスポーン間隔を時間帯で変えるといった拡張ができます。
まとめ
| ポイント | キーワード |
|---|---|
| ① Terrain の Size・Resolution でフィールドのスケールを決める | Heightmap Resolution は 2ⁿ+1 |
| ② 起伏ブラシで山・谷・平地を造形する | Raise / Smooth / Stamp |
| ③ Tree Painter と Detail Mesh で植生を大量配置する | Density / Billboard LOD |
| ④ Water Shader(Plane)と Wind Zone で自然環境を演出する | Simple Water Shader |
| ⑤ Skybox マテリアルを Lighting Settings にセットする | Procedural Skybox |
⑥ transform.Rotate * Time.deltaTime で毎秒一定速度回転 |
フレームレート非依存 |
| ⑦ Terrain は広いフィールドに向くが、草・木の密度管理が重要 | Detail Distance / Density |
Chapter 5 の学習の核心は「舞台の見た目は多くのアセットが担い、動き(昼夜サイクル)はコードが担う」という役割分担です。RoundLight.cs の 6 行が示すように、シンプルなコードでも Update + Time.deltaTime の組み合わせは Unity の基本パターンとして繰り返し登場します。
← シリーズ目次に戻る
次の記事 → 第2回:Chapter 6 キャラクターを作ってみよう
最終更新:2026年4月