現場レベルのゲーム制作が、すべてここで学べます。
この講座ではUnityを使って「箱庭経営シミュレーションゲーム」を作成していきます。
今回はその第10回目です。
前回は3種類の従業員を作成して農場経営を自動化できるようにしました。
前回の記事:

ここまでで、ゲームに必要な基本的な仕組みは一通り作成してきました。
しかし、現時点ではまだ「ゲーム性」が弱く、ゲームとしては完成していません。
第10回からはこれまでに作成してきた仕組みを組み合わせ、実際にゲームとして遊べる形へと仕上げていきましょう。
具体的には貯めたお金を使ってショップで施設のレベルアップが行えるようにします。最初は小さな商品棚、狭い畑、なかなか来ないお客さんという厳しいスタートにします。
農作物を育てて売ってそのお金で施設を拡大していくことで品揃えを豊富にし、畑を広くし、お客さんが途切れずやってくる。
そんな農園経営の成長過程やインフレ要素を導入し、よりゲームらしくしていきます。
ショップで殺虫スプレーを購入できるようにしよう
ここでは、ショップから殺虫スプレーを購入できる仕組みを作成していきます。
Hierarchy内の「ShopTerminal_PC > Canvas > Display」にある「Bucket_Button」を選択し、Ctrl + Dで複製してください。

複製したボタンを選択し、Inspectorから以下のように設定しましょう。

次に、BugSpray_Buttonの子オブジェクトにある「Text(TMP)」を選択し、以下のように変更していきましょう。
|
1 2 3 |
殺虫 スプレー 5 |


次に、BugSprayItemをプレハブ化していきます。
ProjectウィンドウでPrefabsフォルダを開き、HierarchyにあるBugSprayItemをドラッグ&ドロップしてプレハブ化しましょう。

また、今後はショップから購入できるようになるため、Hierarchy上にあるBugSprayItemとProps_Bucket_01は削除しておきましょう。

次に、殺虫スプレー用のShopItemDataを登録します。
これにより、ShopTerminalスクリプトから殺虫スプレーを追加し、購入できるようになります。
ProjectウィンドウでItemsフォルダを開き、右クリックから「Create > Shop > Item」を選択して、新しく作成したアセットの名前を「BugSpray」に変更しましょう。

作成したBugSprayを選択し、Inspectorから以下のように設定していきましょう。

次に、HierarchyでShopTerminal_PCを選択し、ShopTerminalコンポーネントのItemsにBugSprayを追加しましょう。

最後に、BugSpray_Buttonを選択し、On ClickにBugSprayを設定しましょう。

これで、ショップから殺虫スプレーを購入できるようになります。

畑を拡張できるようにしよう
次は、ショップから畑を拡張できるようにしていきましょう。
最初から畑全体を使えるようにするのではなく、段階的に拡張できる仕組みにすることで、ゲームバランスを調整しやすくなります。
最初はあまりたくさん作物を作れないですが、お金を稼ぎ、施設を拡張することでより多くの農作物を一度に栽培できる。そんな”ゲーム性”を導入していきます。
具体的な方法ですが、畑に以下のようにフェンスを設置し、そのフェンスを少しずつ後ろへ下げていくことで機能拡張を実現していきます。最初は使えるスペースが狭いというわけですね。
ただし、最後まで拡張したときは、フェンス自体が不要になるため削除します。

また、単純にフェンスを後ろへ移動させるだけでは、フェンスの奥にある畑を従業員が目指してしまう可能性があります。
これまでNavigation AIでは、ベイク(焼き付け)によって移動可能な範囲を設定する方法を扱ってきましたが、今回はさらに発展させて、動的に変化するオブジェクトに対してナビゲーションをリアルタイムで反映させる方法についても学んでいきましょう。
フェンスを設置しよう
まずは、Hierarchyで右クリックをして「Create Empty」を選択し、名前を「Fence_Group」に変更しましょう。

その後、Fence_Groupを選択し、Inspectorから以下のように設定していきます。

Add ComponentからBox Colliderを追加し、Inspectorから以下のように設定しましょう。

次に、Add Componentから「Navigation > Nav Mesh Obstacle」を追加し、Inspectorから以下のように設定しましょう。

Carve(カービング)とは
CarveとはNavMeshという歩ける床に対して障害物の領域を削除して、AIが通れないようにすることです。
- Carve ON にすると、NavMeshに穴を開けてAIエージェントがその領域を通れないようにします
- OFF の場合は単に「動的な障害物」として扱われ、NavMeshは変化しません
| 項目 | 値 | 意味 |
|---|---|---|
| Move Threshold | 0.1 | 0.1m以上動いたらNavMeshを再計算 |
| Time To Stationary | 0.5 | 0.5秒静止したらCarve実行 |
| Carve Only Stationary | ✅ | 静止中のみNavMeshを切り抜く |
動く扉や可動式の障害物などによく使われる設定です。
NavMeshのリアルタイム再計算はCPU負荷が高いため、
-
動いている間もずっとCarveし続けると → 毎フレーム再計算でパフォーマンスが落ちる
-
静止したときだけ行う → 必要最低限の計算に抑えられる
なので動く障害物(扉など)には基本ON推奨です。
ここまでの設定でこのオブジェクトは障害物として認識され、ベイクをやり直さなくてもリアルタイムで回避されるようになります。
(↑これはイメージ図です。フェンスの位置が変われば障害物の位置が代わり、従業員が移動できる領域が変わります。)
次に、Projectウィンドウから
「Assets/AssetHunts!/GameDev Starter Kit – Farming/Asset/Farm Fence」にある「Farm_Fence_C_01」を、Fence_Groupにドラッグ&ドロップしましょう。
その後、Ctrl + Dを6回押して複製し、フェンスが合計7個になるように配置します。

配置したフェンスをすべて選択し、InspectorからTransformを以下のように設定していきましょう。

これでフェンスの配置は完了です。

ショップから拡張できるようにフェンスを制御するスクリプトを作成しよう
ここで作る処理は、以下の流れで実装していきます。
- ショップで購入されたときに呼び出される関数を用意する。
- その関数が呼ばれたら、フェンスのX座標を減らして後ろへ移動させる。
- フェンスが拡張の限界位置まで到達したら、フェンスを削除する。

Projectウィンドウで Assets を開き、Scripts フォルダを開きます。
フォルダ内で右クリックして Create > MonoBehaviour Script を選択し、名前を「FenceMover」にしましょう。

作成できたら、そのスクリプトをダブルクリックで開き、以下の内容を書き込んでください。
FenceMover.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 |
using UnityEngine; using System.Collections.Generic; public class FenceMover : MonoBehaviour { [Header("移動設定")] [SerializeField] private float moveX = -2.0f; [Header("判定するX座標(この値になったら発動)")] [SerializeField] private float targetX = 10f; [Header("誤差許容値")] [SerializeField] private float tolerance = 0.1f; [Header("X到達時に非アクティブにするオブジェクト")] [SerializeField] private List<GameObject> disableObjects = new List<GameObject>(); private bool hasTriggered = false; public void MoveFence() { Vector3 pos = transform.position; pos.x += moveX; transform.position = pos; // 移動直後に判定 CheckAndDisable(); } private void CheckAndDisable() { if (hasTriggered) return; if (Mathf.Abs(transform.position.x - targetX) <= tolerance) { foreach (var obj in disableObjects) { if (obj != null) obj.SetActive(false); } hasTriggered = true; } } } |
スクリプトの解説
ショップで購入されたときに「MoveFence」が呼ばれ、フェンスを移動 → 指定位置に到達したらフェンスを非表示という流れになっています。
変数の役割
移動設定
|
1 2 |
[Header("移動設定")] [SerializeField] private float moveX = -2.0f; |
フェンスをどれだけX方向に動かすかを決めます。Unity画面でフェンスのX位置を試しに変更してみるとたしかにマイナス方向に動かせば使える畑のスペースが増えることが確認できます。
到達判定
|
1 2 3 4 5 |
[Header("判定するX座標(この値になったら発動)")] [SerializeField] private float targetX = 10f; [Header("誤差許容値")] [SerializeField] private float tolerance = 0.1f; |
targetX:この位置に来たら処理を発動
tolerance:誤差の許容範囲
tolerance は完全一致ではなく「だいたい同じ位置」で判定するために使います。
(ピッタリで判定にしてしまうとわずかな浮動小数点のズレで判定から外れてしまう)
最初にtargetXに与える数値については、FenceGroupのTransformのX座標を確認すると意図がわかります。

↑Transform Position X=28から2ずつマイナス方向に動かし、10になったら削除するわけですね。
非表示にするオブジェクト
|
1 2 |
[Header("X到達時に非アクティブにするオブジェクト")] [SerializeField] private List<GameObject> disableObjects = new List<GameObject>(); |
ここに設定したオブジェクトが、条件を満たしたときに非アクティブになります。
List 型なのでフェンス本体やコライダーなどをまとめて登録できます。
List 型について
ここで使用している「List型」は、複数の要素をまとめて管理できるデータ構造です。
配列と似ていますが、要素数をあとから自由に増減できる柔軟さが特徴です。
配列は最初にサイズを決める必要がありますが、Listは必要に応じて要素を追加・削除できるため、実際の開発では非常によく使われます。
今回のスクリプトでは List<GameObject> として使用しており、非表示にしたいオブジェクトを複数まとめて管理しています。
Inspectorから簡単に要素を追加できるため、フェンス本体やコライダーなどをまとめて登録しておくことができます。
Listの基本的な使い方をいくつか紹介します。
まず、要素の追加です。
|
1 2 |
List<GameObject> list = new List<GameObject>(); list.Add(gameObject); |
Listに新しい要素を追加できます。
次に、要素の削除です。
|
1 |
list.Remove(gameObject); |
指定したオブジェクトをListから削除できます。
要素数を取得する場合は、Countを使用します。
|
1 |
int count = list.Count; |
現在いくつの要素が入っているかを取得できます。
要素を1つずつ処理する方法として、foreachがあります。
|
1 2 3 4 |
foreach (var obj in list) { Debug.Log(obj.name); } |
Listの中身を順番に取り出して処理できます。
インデックスを使ってアクセスすることもできます。
|
1 |
list[0].SetActive(false); |
配列と同じように、番号を指定して要素にアクセスできます。
今回のように複数のオブジェクトをまとめて制御したい場面では、Listを使うことでコードがシンプルになり、管理もしやすくなります。
一度だけ実行するためのフラグ
|
1 |
private bool hasTriggered = false; |
条件を満たした時に何度も実行されないようにする制御用変数です。
MoveFence(メイン処理)
|
1 2 3 4 5 6 7 8 9 |
public void MoveFence() { Vector3 pos = transform.position; pos.x += moveX; transform.position = pos; // 移動直後に判定 CheckAndDisable(); } |
この関数がショップから呼ばれる想定です。
|
1 2 3 |
Vector3 pos = transform.position; pos.x += moveX; transform.position = pos; |
現在位置を取得して、X座標だけを変更して再設定しています。これによりフェンスが後ろへ移動します。その後すぐに
|
1 2 |
// 移動直後に判定 CheckAndDisable(); |
で「最後まで到達したかどうか」をチェックします。
CheckAndDisable(判定処理)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private void CheckAndDisable() { if (hasTriggered) return; if (Mathf.Abs(transform.position.x - targetX) <= tolerance) { foreach (var obj in disableObjects) { if (obj != null) obj.SetActive(false); } hasTriggered = true; } } |
こちらでX到達判定をしています。
|
1 |
if (hasTriggered) return; |
すでに実行済みなら何もしません。
|
1 |
if (Mathf.Abs(transform.position.x - targetX) <= tolerance) |
現在のX座標がtargetXに近いかどうかを判定しています。
|
1 2 3 4 |
foreach (var obj in disableObjects) { if (obj != null) obj.SetActive(false); } |
条件を満たしたとき登録されているオブジェクトをすべて非表示にします。
|
1 |
hasTriggered = true; |
一度実行したら、二度と発動しないようにフラグを立てます。
作成したスクリプトを設定しよう
まず「FenceMover.cs」が正しく保存されていることを確認しましょう。
確認できたら、Project ウィンドウから FenceMover.cs を探し、それを Hierarchy にある「Fence_Group」オブジェクトへドラッグ&ドロップしてアタッチします。

次に、HierarchyでFence_Groupを選択し、Disable Objectsにフェンスを登録しましょう。

次に、ショップに畑拡張用のボタンを追加していきます。
Hierarchyから「ShopTerminal_PC > Canvas > Display > Seed_Button」を選択し、Ctrl + Dで複製しましょう。

複製した「Seed_Button (1)」を選択し、Inspectorから名前を「FieldExpansion_Button」に変更して、以下のように設定していきましょう。

つぎにFieldExpansion_Buttonの子オブジェクトにあるText (TMP)を選択してテキストを以下のように変更しましょう。
|
1 2 |
畑を拡張 150 |


次に、畑を拡張するためのShopItemDataを登録します。
ProjectウィンドウでItemsフォルダを開き、右クリックから「Create > Shop > Item」を選択して、新しく作成したアセットの名前を「FieldExpansion」に変更しましょう。

作成したFieldExpansionを選択し、Inspectorから以下のように設定していきましょう。

また、一番下にある項目から、FenceMoverのMoveFenceを呼び出すように設定しましょう。

次に、ShopTerminalに登録していきます。
ShopTerminal_PCを選択し、ShopTerminalコンポーネントのItemsに作成したFieldExpansionを追加しましょう。

最後に「FieldExpansion_Button」を選択し、On Clickを以下のように設定していきましょう。

ここまで作成できたら、一度再生しましょう。
再生中にShopTerminal_PCを選択し、ShopTerminalコンポーネントのWalletを1500に設定します。


ここまでできたら、ショップから実際に購入して動作を確認してみましょう。

購入時にフェンスが後ろへ移動していれば成功です。さらに、最後まで拡張した際にフェンスが消え、追加で購入できなくなっていれば、正しく実装できています。
商品棚を購入で増やせるようにしよう
たとえば、畑を拡張してたくさんの作物を収穫できるようになったとしても、最初からすべての商品棚を使えてしまうと、ゲームとして簡単になりすぎてしまいます。
そこで、商品棚も購入によって少しずつ増やせるようにし、ゲームバランスを調整していきましょう。
今回も、前回作成したFenceMoverスクリプトと似た考え方で実装していきます。
仕組みはとてもシンプルで、関数が呼び出されたときに、次の商品棚をアクティブにして表示するだけです。

Projectウィンドウで Assets を開き、Scripts フォルダを開きます。
フォルダ内で右クリックして Create > MonoBehaviour Script を選択し、名前を「ProductShelfActivator」にしましょう。

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

今回は第1章から第9章までで作成してきた要素を組み合わせて様々な機能を実装しました。
これまで作ってきた機能を活用してどんどん施設拡大のアイテムをショップに増やしていきましたね。
次回は従業員を雇うシステムを作成し、従業員の色分けやショップの演出面などを強化します。
さらにゲーム性を高め、最後に遊びやすくしてゲーム完成としていきましょう。
あと一息です!
次の記事:
現場レベルのゲーム制作が、すべてここで学べます。







コメント