AIナビゲーション
プレイヤーの邪魔をする敵を追加します。
敵の作成
Cubeのゲームオブジェクトを追加し敵を作成します。
1. 空のゲームオブジェクトを作成
Hierarchyビューで「右クリック > Create Empty」の順番で選択し、名前を「Enemy」に変更します。
EnemyゲームオブジェクトのTransformコンポーネントをリセットします。

2. 敵用のゲームオブジェクトを作成
HierarchyビューでEnemyゲームオブジェクトを右クリックし、「3D Object > Cube」を選択し、名前を「EnemyBody」に変更します。
EnemyBodyのTransformコンポーネントのScaleを(X: 0.5, Y: 1, Z: 0.5)に設定します。
EnemyBodyのTransformコンポーネントのPositionを(X: 0, Y: 0.5, Z: 0)に設定します。

3. 適用のマテリアルを作成
ProjectビューのMaterialsフォルダを表示します。
Projectビューで「右クリック > Create > Material」の順番に選択しマテリアルを作成し、名前を「Enemy」に変更します。
Enemyマテリアルの色を赤に設定します。
SceneビューまたはHierarchyビューのEnemyBodyゲームオブジェクトにEnemyマテリアルをドラッグ&ドロップします。

NavMeshをベイクする
GroundゲームオブジェクトにNavMeshをベイクし、NavMeshサーフェスに含める設定をします。
1. GroundゲームオブジェクトにNavMeshをベイクする
HierarchyビューのGroundゲームオブジェクトを選択します。
Inspectorビューで「Add Component > NavMesh Surface」を選択しします。
NavMesh SurfaceコンポーネントのBakeをクリックします。

2. NavMeshサーフェスに含める内容を設定
GroundゲームオブジェクトのInspectorビューのNavMesh SurfaceコンポーネントのObject Collectionをクリックし設定を展開します。
Collect Objectsのドロップダウンメニューを開き、「Current Object Hierarchy」を選択しします。
選択したら再度、NavMesh SurfaceコンポーネントのBakeをクリックします。
PickUpゲームオブジェクト、Playerゲームオブジェクト、EnemyゲームオブジェクトはNavMeshでは無視されるようになりました。

敵にプレイヤーを追いかけさせる
新しいプログラミングを作成し、NavMeshエージェント(敵)がプレイヤーを追跡できるようにします。
1. EnemyゲームオブジェクトにNavMeshAgentを追加
HierarchyビューのEnemyゲームオブジェクトを選択します。
Inspectorビューで「Add Component > NavMesh Agent」を選択しします。
NavMesh AgentのSpeedを2.5に設定します。

2. EnemyMovementスクリプトの追加
EnemyゲームオブジェクトのInspectorビューで「Add Component > New script」を選択し、名前を「EnemyMovement」にします。
ProjectビューのAssetsフォルダからEnemyMovementスクリプトファイルをScriptsフォルダに移動させます。
EnemyMovementを開いてください。

3. NavMeshAgentコンポーネントを参照する変数の追加
UnityEngine.AIをインポートします。
EnemyMovement.cs
using UnityEngine;
+ using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
void Start()
{
}
void Update()
{
}
}
2つのグローバル変数を追加します。
EnemyMovement.cs
using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
+ public Transform player;
+ private NavMeshAgent _navMeshAgent;
+
void Start()
{
}
void Update()
{
}
}
Start関数でNavMesh Agentコンポーネントを取得し変数に割り当てます。
EnemyMovement.cs
using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
public Transform player;
private NavMeshAgent _navMeshAgent;
void Start()
{
+ _navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
}
}
4. Enemyゲームオブジェクトの目的地をPlayerゲームオブジェクトに設定
Update関数でEnemyゲームオブジェクトの目的地をPlayerゲームオブジェクトの位置に更新します。
EnemyMovement.cs
using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
public Transform player;
private NavMeshAgent _navMeshAgent;
void Start()
{
_navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
+ if (player != null)
+ {
+ _navMeshAgent.SetDestination(player.position);
+ }
}
}
5. player変数の割当
HierarchyビューでEnemyゲームオブジェクトを選択し、InspectorビューのEnemyMovementコンポーネントのPlayerスロットにHierarchyビューのPlayerゲームオブジェクトをドラッグ&ドロップします。
※この手順は必ず実行してください。ゲームが動作しなくなります。

6. ゲームテスト
Sceneを保存します。
ゲームを実行しEnemyゲームオブジェクトがPlayerを追いかけるようになっているかを確認してください。
静的障害物の作成
NavMeshサーフェスに障害物を組み込みます。
1. さまざまなゲームオブジェクトを作成
Hiearchyビューで「右クリック > 3D Object > Cube」を選択し、オブジェクトの位置、回転、大きさなどを変更してください。
上記手順を繰り返し、さまざまな形やサイズの障害物を作成します。
また、Enemyゲームオブジェクトの斜面登りをテストするので、少なくとも1つのオブジェクトをスロープのようなものにしてください。

2. 障害物をNavMeshサーフェスに含める
Hierarchyビューで、各障害物ゲームオブジェクトをGroundゲームオブジェクトにドラッグ&ドロップし、子ゲームオブジェクトにします。

HierarchyビューでGroundゲームオブジェクトを選択し、InspectorビューのNavMesh SurfaceコンポーネントのBakeをクリックします。障害物を含めた構成で再度NavMeshを生成します。
3. エージェントの設定を変更
GroundゲームオブジェクトのNavMesh SurfaceコンポーネントのAgent Typeドロップダウンから「Open Agent Settings…」を選択します。
Step Heightの値を変更すると、エージェント(敵)がより高い平面に乗ることができます。
Max Slope(最大傾斜)を大きくすると、エージェント(敵)はより急な坂を登ることができます。

4. ゲームテスト
ゲームを実行して、さまざまなエージェントの設定や障害物の構成を試してください。
動的障害物の作成
軽量で移動可能なゲームオブジェクトを作成し、NavMeshの障害物となるようにします。
1. 軽くて移動可能な立方体を作成
Hierarchyビューで「右クリック > 3D Object > Cube」を選択し、名前を「DynamicBox」に変更します。
DynamicBoxの位置と大きさを自由に調整してください。
Projectビューで「右クリック > Create > Material」の順番に選択しマテリアルを作成し、名前を「DynamicObstacle」に変更します。
Enemyマテリアルの色は自由に設定してください。
SceneビューまたはHierarchyビューのDynamicBoxゲームオブジェクトにDynamicObstacleマテリアルをドラッグ&ドロップします。
HierarchyビューでDynamicBoxを選択します。
Inspectorビューで「Add Component > Rigidbody」を選択します。
RigidbodyコンポーネントのMassを0.1に変更します。これにより簡単に推し進めることができます。
ゲームを実行し、EnemyオブジェクトがDynamicBoxをすり抜けることを確認してください。

2. 立方体をNavMeshの障害物に設定
HierarchyビューでDynamicBoxゲームオブジェクトを選択してください。
Inspectorビューで「Add Component > NavMesh Obstacle」を選択してください。
NavMesh ObstacleコンポーネントのCarveを有効にします。

3. 立方体をプレハブ化
HierarchyビューのDynamicBoxゲームオブジェクトをProjectビューのPrefabsフォルダにドラッグ&ドロップします。これによりDynamicBoxゲームオブジェクトのプレハブが作成されます。
必要に応じてDynamicBoxプレハブを使用しSceneのプレイエリア全体に散在させます。
※必要に応じてDyanamicBoxをまとめる空のオブジェクトを作成してください。

4. ゲームテスト
ゲームを実行して、さまざまな障害物の構成を試してください。
勝敗の条件を設定
EnemyオブジェクトがPlayerオブジェクトに触れたらPlayerゲームオブジェクトを破壊して「You Lose!」と表示される敗北条件を追加します。
1. Playerゲームオブジェクトを破壊し「You Lose!」と表示させる条件を追加
PlayerController.csを開きます。
OnCollisionEnter関数を追加します。
PlayerController.cs
...(省略)
public class PlayerContrller : MonoBehaviour
{
...(省略)
void SetCountText()
{
...(省略)
}
+
+ void OnCollisionEnter(Collision collision)
+ {
+ if (collision.gameObject.CompareTag("Enemy"))
+ {
+ Destroy(gameObject);
+ winTextObject.gameObject.SetActive(true);
+ winTextObject.GetComponent<TextMeshProUGUI>().text = "You Lose!";
+ }
+ }
}
2. EnemyBodyゲームオブジェクトにEnemyタグを追加
HierarchyビューでEnemyBodyゲームオブジェクトを選択します。
Inspectorビューで「Tag」ドロップダウンメニューの「Add Tag…」を選択します。
追加(+)ボタンを選択し、新しいタグ「Enemy」を作成します。
※先程コードで記述したタグ名と大文字/小文字が一致しているか確認してください。
Hierarchyビューで再度EnemyBodyゲームオブジェクトを選択し、InspectorビューでEnemyタグを設定します。
※EnemyゲームオブジェクトではなくEnemyBodyゲームオブジェクトに適用することに注意してください。
ゲームを実行して、EnemyゲームオブジェクトがPlayerゲームオブジェクトに衝突(触れる)とPlayerゲームオブジェクトが破壊され「You Lose!」と表示されることを確認してください。
※必要に応じてEnemyゲームオブジェクトの位置をPlayerゲームオブジェクトから離してください。

3. Playerが勝利したらEnemyゲームオブジェクトを破壊
PlayerController.csを開きます。
全てのPickUpゲームオブジェクトを収集したら、Enemyゲームオブジェクトを破壊する処理を追加します。
PlayerController.cs
...(省略)
public class PlayerContrller : MonoBehaviour
{
...(省略)
void SetCountText()
{
countText.text = "Count: " + _count.ToString();
if (_count >= 12)
{
winTextObject.SetActive(true);
+ Destroy(GameObject.FindGameObjectWithTag("Enemy"));
}
}
...(省略)
}
4. ゲームテスト
ゲームを実行して勝利条件をテストしてくだい。PickUpゲームオブジェクトをすべて収集するとEnemyゲームオブジェクト(EnemyBodyゲームオブジェクト)が破壊されます。
最終スクリプト
PlayerController.cs
using UnityEngine;
// PlayerゲームオブジェクトにアタッチしたPlayerInputからの入力を受け付けるために必要な名前空間
// キーボード/ゲームパッド → Actions(ProjectビューにあるInputSystem_Actionsファイル) →
// PlayerゲームオブジェクトにアタッチしたPlyaerInput → このスクリプト
using UnityEngine.InputSystem;
using TMPro;
public class PlayerContrller : MonoBehaviour
{
/// <summary>
/// Playerの移動速度を保存する変数
/// </summary>
public float speed = 5f;
// private(プライベート)で定義する変数の名前は最初に_(アンダーバー)を付けます。
// private変数は他のスクリプトファイルから値を取得・変更させることができない変数です。
/// <summary>
/// PlayerにアタッチされたRigidbodyを保存する変数
/// </summary>
private Rigidbody _rb;
/// <summary>
/// X方向の移動量を保存する変数
/// </summary>
private float _movementX;
/// <summary>
/// Y方向の移動量を保存する変数
/// </summary>
private float _movementY;
/// <summary>
/// 収集されたPickUpゲームオブジェクトの数を保存する変数
/// </summary>
private int _count;
/// <summary>
/// 収集されたPickUpゲームオブジェクトの数を表示するUIテキストコンポーネント
/// </summary>
public TextMeshProUGUI countText;
/// <summary>
/// WinTextを表示するゲームオブジェクト
/// </summary>
public GameObject winTextObject;
void Start()
{
// PlayerにアタッチされたRigidbodyを取得し変数_rbに保存します。
_rb = GetComponent<Rigidbody>();
// ゲーム開始時に_countを0に初期化します。
_count = 0;
// カウント表示を更新します。
SetCountText();
// ゲーム開始時にWinTextを非アクティブに設定します。
winTextObject.SetActive(false);
}
/// <summary>
/// プレイヤーのキーボード(WASD)/ゲームパッド(Lスティック)の入力があった際に呼び出される関数です。
/// </summary>
/// <param name="movementValue">入力デバイスのX方向とY方向の値を持っています。</param>
void OnMove(InputValue movementValue)
{
// Vecotr2(2次元座標:X座標/Y座標)型の変数movementVecotrにmovementValueをVector2(2次元座標)に変換し保存します。
Vector2 movementVector = movementValue.Get<Vector2>();
// movementVectorのX座標・Y座標をそれぞれに変数に保存します。
_movementX = movementVector.x;
_movementY = movementVector.y;
}
void FixedUpdate()
{
// _movmentXをX軸の値に_movmentYをZ軸の値にした3次元座標に変換し変数momventに保存します。
Vector3 movement = new Vector3(x: _movementX, y: 0.0f, z: _movementY);
// RigidbodyのAddFrouce関数に3次元座標を渡し、指定した方向に力を加えます。
// movment(力)にspeed(移動速度)掛け合わせます。
_rb.AddForce(movement * speed);
}
void OnTriggerEnter(Collider other)
{
// プレイヤーが衝突したオブジェクトにPickUpタグが付いているか確認します。
if (other.gameObject.CompareTag("PickUp"))
{
// 衝突したオブジェクトを非アクティブ化します。(非表示になる)
other.gameObject.SetActive(false);
// _countの数を1増やします。
_count = _count + 1;
// カウント表示を更新します。
SetCountText();
}
}
/// <summary>
/// 収集されたPickUpゲームオブジェクトの表示数を更新する関数です。
/// </summary>
void SetCountText()
{
// 現在の数でCountTextのテキストを更新します。
countText.text = "Count: " + _count.ToString();
// カウント数が勝利条件に達したかを確認します。
if (_count >= 12)
{
// WinTextゲームオブジェクトを表示します。
winTextObject.SetActive(true);
// Enemyゲームオブジェクト(EnemyBodyゲームオブジェクト)を破壊します。
Destroy(GameObject.FindGameObjectWithTag("Enemy"));
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
// このゲームオブジェクト(Playerゲームオブジェクト)を破壊します。
Destroy(gameObject);
// winTextのテキストを「You Lose!」に書き換え表示します。
winTextObject.gameObject.SetActive(true);
winTextObject.GetComponent<TextMeshProUGUI>().text = "You Lose!";
}
}
}
EnemyMovement.cs
using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
/// <summary>
/// PlayerゲームオブジェクトのTransformコンポーネントを保持する変数
/// </summary>
public Transform player;
/// <summary>
/// NavMeshAgentコンポーネントを保持する変数
/// </summary>
private NavMeshAgent _navMeshAgent;
void Start()
{
// このオブジェクトにアタッチされたNavMeshAgentコンポーネントを取得して変数に割当する
_navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
// プレイヤーへの参照がある場合に処理をする
if (player != null)
{
// このオブジェクト(敵)の目的地をプレイヤーの現在位置に設定する
_navMeshAgent.SetDestination(player.position);
}
}
}