現場レベルのゲーム制作が、すべてここで学べます。
この講座ではUnityを使って「箱庭経営シミュレーション×農場ゲーム」を作成していきます。
今回はその第8回目です。
前回は商品棚を作成してお客さんが商品を購入してくれるようにしました。
前回の記事:

これまでで農園経営シミュレーションゲームの基盤は整いましたが、ただの繰り返しゲームになると単調になってプレイヤーが飽きてしまいます。
そこで、第8回ではプレイヤーに対するお邪魔システムとして、作物に群がり腐らせるハエを追加してみましょう。
また、虫よけスプレーのようなものを作成してハエを追い払えるようにしましょう。
畑の作物が腐るデバフ効果をUnityで実装する
ハエを追加する前に畑の作物が腐る仕組みを実装していきます。
まずは、作物が腐った状態を表すプレハブを用意します。以前作成した FieldStage_0~FieldStage_4 と同じ要領で、腐敗状態のモデルを追加していきましょう。

腐った状態を用意しよう
Projectウィンドウを開き、Prefabsフォルダに移動します。
その中にある FieldStage_1 を選択し、Ctrl + D を押してプレハブを複製してください。
複製されたプレハブの名前を FieldStage_Dead に変更しておきましょう。

複製した FieldStage_Dead をダブルクリックし、プレハブ編集モードに入りましょう。

シーンビューが 下の画像のような表示になっていればプレハブ編集モードに入れています。
まず Projectウィンドウから、次のパスを順番にたどってください。
Assets > AssetHunts! > GameDev Starter Kit – Farming > Asset > Farm Crop Step > Farm_Crop_Carrot_Dead
この中にある Farm_Crop_Carrot_Dead を見つけたら、Hierarchyウィンドウへドラッグ&ドロップして配置しましょう。

配置した Farm_Crop_Carrot_Dead を選択し、Inspector から Transform の値を次のように設定しましょう。

これで、オブジェクトの準備は完了です。
最後に、Hierarchyウィンドウ左上にある矢印ボタンをクリックして、プレハブ編集モードを終了しましょう。

ハエが作物を腐らせるように畑スクリプトの準備をしよう
次は、スクリプト側で作物が腐る処理を追加していきましょう。
これまで FieldPlot スクリプトで畑の状態を管理していました。
そのため今回の腐敗処理も FieldPlot に追記して実装していきます。
今回は、作物が成長途中の段階にあるときだけ腐るようにします。
対象となるのは、FieldStage_1 ~ FieldStage_3 の状態です。
つまり、まだ収穫前の成長途中で、なおかつ侵食された場合のみ腐敗状態に変わるという仕組みにしていきます。

Projectウィンドウから「Scripts」フォルダを開き、「FieldPlot」をダブルクリックしてスクリプトを開きましょう。

開けたら、以下のマーカー部分を書き込んでください。
FieldPlot.cs
|
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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
using System.Collections; using System.Collections.Generic; using UnityEngine; // 畑の状態 public enum FieldState { Empty, // 何も植えていない Growing, // 成長中 Ready // 収穫可能 } public class FieldPlot : MonoBehaviour { [Header("成長ステージのプレハブ")] [Tooltip("0: 何もない / 1: 種植え / 2: 芽 / 3: 成長中 / 4: 収穫可能")] [SerializeField] private List<GameObject> growthStagePrefabs = new(); [Header("腐った見た目")] [SerializeField] private GameObject DeadStagePrefab; [Header("ステージごとの成長時間(秒)")] // 各ステージへ進むまでの待ち時間 [SerializeField] private List<float> stageDurations = new() { 5f, 5f, 5f}; // 現在表示しているステージのプレハブ実体 private GameObject currentStageInstance; // 成長処理用のコルーチン private Coroutine growthRoutine; // 現在の畑の状態 public FieldState CurrentState { get; private set; } = FieldState.Empty; // 現在表示中のステージ番号 public int CurrentStageIndex { get; private set; } = 0; // ステージの最大インデックス private int LastIndex => (growthStagePrefabs == null) ? 0 : Mathf.Max(0, growthStagePrefabs.Count - 1); // 腐ったかどうか public bool IsInfested { get; private set; } = false; // 最後段階(収穫段階)かどうか public bool IsHarvestStage => growthStagePrefabs != null && growthStagePrefabs.Count > 0 && CurrentStageIndex == growthStagePrefabs.Count - 1; private void Start() { // 初期状態は空の畑を表示 ResetToEmptyVisual(); } /// 作物を植えて成長を開始する public bool Plant() { // すでに何か植えられている場合は失敗 if (CurrentState != FieldState.Empty) return false; IsInfested = false; CurrentState = FieldState.Growing; // 念のため既存の成長処理を停止 if (growthRoutine != null) StopCoroutine(growthRoutine); // 成長処理を開始 growthRoutine = StartCoroutine(GrowthRoutine()); return true; } //腐らせられるか確認 public void MarkInfested() { // 空の畑は絶対に侵食させない(これが「空なのに腐りが出る」を潰す) if (CurrentState == FieldState.Empty) return; // 収穫段階はターゲットにしない前提だが、念のためここでも守る if (IsHarvestStage) return; IsInfested = true; } /// スコップなどで畑を完全にリセットする public void ClearField() { // 成長中ならコルーチンを停止 if (growthRoutine != null) { StopCoroutine(growthRoutine); growthRoutine = null; } IsInfested = false; CurrentState = FieldState.Empty; ResetToEmptyVisual(); } /// 成長ステージを時間経過で進める処理 private IEnumerator GrowthRoutine() { int maxIndex = LastIndex; // ステージ1から最後まで順に進める for (int stage = 1; stage <= maxIndex; stage++) { //ここで腐らせるか判断 if (stage == maxIndex && IsInfested && DeadStagePrefab != null) { SpawnDeadAsHarvest(); } else { SpawnStageInstance(stage); } // 対応する成長時間を取得 int tIndex = Mathf.Clamp(stage - 1, 0, stageDurations.Count - 1); float duration = stageDurations[tIndex]; // 指定時間待機 float timer = 0f; while (timer < duration) { timer += Time.deltaTime; yield return null; } } // 最終ステージに到達 CurrentState = FieldState.Ready; growthRoutine = null; } /// 空の畑の見た目に戻す private void ResetToEmptyVisual() { SpawnStageInstance(0); CurrentStageIndex = 0; } private void SpawnDeadAsHarvest() { if (currentStageInstance != null) { Destroy(currentStageInstance); currentStageInstance = null; } CurrentStageIndex = LastIndex; currentStageInstance = Instantiate(DeadStagePrefab, transform); currentStageInstance.transform.localPosition = Vector3.zero; currentStageInstance.transform.localRotation = Quaternion.identity; } /// 指定したステージのプレハブを生成する private void SpawnStageInstance(int index) { // 既存の表示を削除 if (currentStageInstance != null) { Destroy(currentStageInstance); currentStageInstance = null; } // 範囲外チェック if (index < 0 || index >= growthStagePrefabs.Count) return; GameObject prefab = growthStagePrefabs[index]; if (prefab == null) return; CurrentStageIndex = index; // プレハブを生成して畑の子にする currentStageInstance = Instantiate(prefab, transform); currentStageInstance.transform.localPosition = Vector3.zero; currentStageInstance.transform.localRotation = Quaternion.identity; } // Inspectorから成長テスト用 [ContextMenu("TEST/Plant (Start Growth)")] public void StartGrowthTest() { Plant(); } //腐る演出をテスト [ContextMenu("TEST/Dead")] public void StartDeadTest() { MarkInfested(); } } |
スクリプトの解説
① 腐った状態のプレハブを用意する変数
|
1 2 |
[Header("腐った見た目")] [SerializeField] private GameObject DeadStagePrefab; |
ここでは、作物が腐ったときに表示するプレハブを設定できるようにしています。
② 腐ったかどうかを管理する変数
|
1 2 |
// 腐ったかどうか public bool IsInfested { get; private set; } = false; |
この変数は、畑が侵食されて腐る状態になったかどうかを記録するフラグです。
- false → 正常
- true → 腐る対象になった
③ 畑を腐らせる処理
|
1 2 3 4 5 6 7 8 9 10 11 |
//腐らせられるか確認 public void MarkInfested() { // 空の畑は絶対に侵食させない(これが「空なのに腐りが出る」を潰す) if (CurrentState == FieldState.Empty) return; // 収穫段階はターゲットにしない前提だが、念のためここでも守る if (IsHarvestStage) return; IsInfested = true; } |
このメソッドは、畑を「腐る対象」にする処理です。ただし、次の2つの場合は腐らないように制御しています。
空の畑は腐らない
|
1 2 |
// 空の畑は絶対に侵食させない(これが「空なのに腐りが出る」を潰す) if (CurrentState == FieldState.Empty) return; |
何も植えていない畑が腐ると不自然なため、空の状態では処理を中断します。
収穫段階では腐らない
|
1 2 |
// 収穫段階はターゲットにしない前提だが、念のためここでも守る if (IsHarvestStage) return; |
収穫できる状態まで育った作物は、腐敗対象にしないようにしています。
条件を満たした場合だけ腐敗フラグを立てる
|
1 |
IsInfested = 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 |
/// 成長ステージを時間経過で進める処理 private IEnumerator GrowthRoutine() { int maxIndex = LastIndex; // ステージ1から最後まで順に進める for (int stage = 1; stage <= maxIndex; stage++) { //ここで腐らせるか判断 if (stage == maxIndex && IsInfested && DeadStagePrefab != null) { SpawnDeadAsHarvest(); } else { SpawnStageInstance(stage); } // 対応する成長時間を取得 int tIndex = Mathf.Clamp(stage - 1, 0, stageDurations.Count - 1); float duration = stageDurations[tIndex]; // 指定時間待機 float timer = 0f; while (timer < duration) { timer += Time.deltaTime; yield return null; } } // 最終ステージに到達 CurrentState = FieldState.Ready; growthRoutine = null; } |
次の条件をすべて満たした場合に、作物を腐った状態に変更します。
条件
- 最終ステージに到達した
- 腐敗フラグが立っている
- 腐敗プレハブが設定されている
この3つがそろった場合だけ、
|
1 |
SpawnDeadAsHarvest(); |
が実行され、腐った作物が表示されます。
⑤ 腐った作物を生成する処理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private void SpawnDeadAsHarvest() { if (currentStageInstance != null) { Destroy(currentStageInstance); currentStageInstance = null; } CurrentStageIndex = LastIndex; currentStageInstance = Instantiate(DeadStagePrefab, transform); currentStageInstance.transform.localPosition = Vector3.zero; currentStageInstance.transform.localRotation = Quaternion.identity; } |
1 今表示されている作物を削除
|
1 |
Destroy(currentStageInstance); |
成長途中の作物を消します。
2 腐った作物を生成
|
1 |
currentStageInstance = Instantiate(DeadStagePrefab, transform); |
DeadStagePrefab を生成して、畑の子オブジェクトとして配置します。
3 位置と回転をリセット
|
1 2 |
currentStageInstance.transform.localPosition = Vector3.zero; currentStageInstance.transform.localRotation = Quaternion.identity; |
⑥ デバック用の処理を用意する
|
1 2 3 4 5 6 |
//腐る演出をテスト [ContextMenu("TEST/Dead")] public void StartDeadTest() { MarkInfested(); } |
これによりインスペクターから腐るを呼び出すことが可能になります。
作成したスクリプトを組み立てる
まずは、「FieldPlot」が正しく保存されていることを確認してください。
次に、Hierarchy から MapRoot > Field > 3D_Tile_Ground_05 を見つけましょう。
次に、3D_Tile_Ground_05 の右側にある矢印アイコンをクリックして、プレハブ編集モードに入ります。

次に、3D_Tile_Ground_05 を選択します。
Inspector を確認し、FieldPlot コンポーネントの中にある DeadStagePrefab の欄に、Projectウィンドウの Prefabs フォルダにある「FieldStage_Dead」 をドラッグ&ドロップしてアタッチします。

設定ができたら、Hierarchyウィンドウ左上にある矢印アイコンをクリックして、プレハブ編集モードを終了しましょう。

ここまで作成できたら、一度ゲームを再生して動作を確認してみましょう。
畑を耕して作物を植え、成長途中の状態にします。
その状態で一度Escキーなどを押してゲーム画面からマウスコントロール権を取り戻し、一時停止します。

このように、今作物を植えた位置を含む3D_Tile_Ground_05系オブジェクトを選択し、FieldPlotコンポーネントで右クリックを行い、 Inspector に表示されている TEST > Dead を実行してみてください。
すると MarkInfested() が呼び出され、畑が腐敗対象としてマークされます。
そのまま成長が最後の段階まで進むと、通常の作物ではなく、腐った作物(FieldStage_Dead)が表示されるはずです。
このようにして、侵食された作物のみが最終段階で腐る仕組みになっています。

プレイヤーの邪魔をするハエを作成しよう
次に、ハエを作成していきます。
ハエを用意しよう
まず Hierarchyウィンドウで右クリックし、Create Empty を選択しましょう。
作成されたオブジェクトの名前を、FlyAgent に変更しておきましょう。

作成した FlyAgent を選択して Transform を以下のように設定しましょう。

次に、Inspector の Add Component ボタンをクリックし、Sphere Collider を追加しましょう。

Sphere Collider コンポーネント内の Center を以下のように設定しましょう

次に、ハエの飛ぶ音を用意します。
以下のリンクから 「ハエが飛ぶ.mp3」 をダウンロードし、
Unity の Projectウィンドウ内にある Sounds フォルダへ追加しましょう。

「ハエが飛ぶ.mp3」を追加できたら、Projectウィンドウからその音声ファイルを FlyAgent の Inspector にドラッグ&ドロップしてアタッチしましょう。
すると、自動的に Audio Source コンポーネントが追加されます。
追加された Audio Source を、次のように設定しましょう。

次に、Hierarchy にある FlyAgent を選択しましょう。
その状態で右クリックし、Effects → Particle System を選択して Particle System を追加します。

次に、作成した Particle System を選択し、Inspector から次のように設定しましょう。


次に Emission をクリックして展開し、以下のように設定しましょう。

Shape をクリックして展開し、以下のように設定しましょう。

次に Color over Lifetime の チェックボックスをオンにし、以下のように設定しましょう。

最後に Noise の チェックボックスをオンにし、以下のように設定しましょう。

これでハエのようなエフェクトが完成すると思います。

Effectの主な種類
Unityの Effect には、ゲーム内の演出を作るための様々なコンポーネントがあります。ここではよく使われるものを紹介します。
Particle System
小さな粒子(パーティクル)を大量に発生させて演出を作る仕組みです。
主に次のような表現に使われます。
- 煙
- 火
- 爆発
- 魔法エフェクト
- 虫やほこり
- 水しぶき
今回作成しているハエの演出も、この Particle System を利用しています。
Line Renderer
2点以上のポイントをつなぎ、線を描画するコンポーネントです。
例えば次のような表現に使われます。
- レーザー
- 電撃
- ワイヤー
- ビーム攻撃
- 移動経路の表示
線を描くだけでなく、太さや色、テクスチャを変更することで様々な演出が可能です。
Trail Renderer
オブジェクトが移動したときに、軌跡を残すエフェクトです。
例えば次のような表現に使われます。
- 剣を振ったときの軌跡
- 弾丸の残像
- 魔法の光の尾
- ダッシュしたキャラクターの残像
動きのあるオブジェクトに付けることで、スピード感や迫力を演出できます。
Particle System Force Field
Particle System Force Field は、パーティクルに対して力(Force)を加えるためのコンポーネントです。
通常の Particle System では、パーティクルは設定された方向や速度で動くだけですが、
Force Field を使うことで 周囲のパーティクルの動きを変化させることができます。
例えば次のような表現に使われます。
- 風に流れる煙
- 渦を巻く魔法エフェクト
- 爆発の衝撃波
- 吸い込まれるエネルギー
Force Field は、近くにあるパーティクルへ物理的な力を与える領域のようなものです。
そのため、複数の Particle System に影響を与えることもできます。
スクリプトからハエを制御しよう
今回作成するスクリプトではハエは以下のようなアルゴリズムで動かします。
- ハエはまず、腐らせることができる畑を探します。
- 見つかった場合、その畑へ向かって移動します。
- 畑の近くまで到着すると、その場にしばらくとどまります。
- 一定時間とどまると、その畑を腐敗状態にします。
- 腐らせた後は、また次の畑を探しに行きます。
もし腐らせる対象の畑が見つからない場合は、プレイヤーがいた場所へ移動し、その場で待機します。
Projectウィンドウで Assets を開き、Scripts フォルダを開きます。
フォルダ内で右クリックして Create > MonoBehaviour Script を選択し、名前を「FlyAgent」にしましょう。

作成できたら、そのスクリプトをダブルクリックで開き、以下の内容を書き込んでください。
まとめ

まず、畑の作物が腐る仕組みを作成しました。そしてハエがその腐敗を発生させるシステムにしました。
さらに、ハエが自動でスポーンする仕組みや、駆除するためのスプレーも追加し、ゲームに新しい要素を加えることができました。
これにより、ただ作物を育てて収穫するだけではなく、トラブルに対応しながら管理する面白さが生まれ、ゲーム性により深みが出てきたと思います。
ただし、どれだけ道具がそろっていても、すべてを人の手だけで管理し続けるのには限界があります。また、作業が増えてくるとどうしても単調に感じやすくなる場面も出てきます。
そこで次回は、従業員AIを雇える仕組みを作成し、作業を手伝ってもらえるようにしていきましょう。
次の記事:

現場レベルのゲーム制作が、すべてここで学べます。







コメント