Unity自學之旅05
- Unity學習之旅⑤
- 📝 AI基礎與敵人行為
- 🥊 AI導航
- 理論知識(基礎)
- 開始實踐
- 🎃 敵人游戲機制
- 追蹤玩家
- 攻擊玩家
- 子彈碰撞
- 完善游戲失敗條件
- 🤗 總結歸納
Unity學習之旅⑤
📝 AI基礎與敵人行為
🥊 AI導航
理論知識(基礎)
一、導航系統的基本組件
- NavMesh(導航網格):
- NavMesh 是 Unity 中 AI 導航的基礎,它是一個表示可行走區域的網格。它將場景中的可行走表面(如地面、樓梯等)進行三角剖分,形成一個平面的網格,用于代理(Agent)導航。
- 生成 NavMesh 的步驟:
- 首先,需要將場景中的幾何體標記為靜態(Static),以便 Unity 可以將它們納入 NavMesh 的計算。
- 然后,打開 Navigation 窗口(Window -> AI -> Navigation),在 Bake 選項卡中調整設置,例如 Agent Radius(代理半徑)、Agent Height(代理高度)等,這些設置決定了代理在場景中占用的空間大小和形狀。
- 點擊 Bake 按鈕,Unity 會生成 NavMesh,顯示為場景中的藍色網格。
- NavMeshAgent(導航網格代理):
- 這是附加在游戲對象上的組件,用于讓對象在 NavMesh 上移動。
- 主要屬性:
- Speed:代理的移動速度。
- Acceleration:代理的加速度。
- Stopping Distance:距離目標多遠時停止移動。
- Radius:代理的半徑,用于避障和計算可行走區域。
- Height:代理的高度,決定了代理可以通過的最小空間高度。
- NavMeshObstacle(導航網格障礙物):
- 該組件用于表示場景中的動態障礙物。當一個對象附加了
NavMeshObstacle
時,它會影響NavMeshAgent
的導航路徑。 - 可以設置為
Carve
模式,這會在 NavMesh 上動態地修改 NavMesh,使代理能夠繞開障礙物。
- 該組件用于表示場景中的動態障礙物。當一個對象附加了
二、導航行為和邏輯
- 路徑規劃:
NavMeshAgent
會自動根據 NavMesh 為代理規劃從當前位置到目標位置的最短路徑。它會考慮障礙物和可行走區域。- 可以使用
agent.remainingDistance
來檢查代理離目標的剩余距離,使用agent.pathStatus
來查看路徑的狀態,如是否完成、正在計算或被阻塞。
- 避障:
- Unity 的導航系統會自動處理代理之間和代理與障礙物之間的避障。通過設置
NavMeshAgent
的屬性,如Radius
和Avoidance Priority
,可以調整避障行為。 - 代理之間會根據彼此的
Avoidance Priority
進行避障,較低優先級的代理會主動避讓較高優先級的代理。
- Unity 的導航系統會自動處理代理之間和代理與障礙物之間的避障。通過設置
- 動態導航:
- 當場景中的障礙物或目標位置發生變化時,
NavMeshAgent
會自動重新計算路徑。 - 例如,如果要在游戲運行時改變目標位置,可以調用
agent.SetDestination(newDestination);
,其中newDestination
是一個Vector3
類型的新位置。
- 當場景中的障礙物或目標位置發生變化時,
開始實踐
window→package manager 選擇 Unity Registry 然后搜索 AI Navigation
window→ai→navigation(obsolete) ,這里是因為我所使用的Unity版本那個AI Navigation static 已經棄用了,我更改了一種使用方式。
選擇Environment,然后為點擊Inspector中的Static,接著將Environment的對象及其子對象都設置為Navigation Static.
點擊Bake→bake
為Enemy添加NavMeshAgent,讓敵人可以在NavMesh上面行走。
繪制幾個路徑點,用于敵人的自動巡邏。
引用巡邏點,編輯EnemyBehavior腳本
// 巡邏點的transformpublic Transform PatrolRoute;// 四個巡邏點的transform集合public List<Transform> Locations;void Start(){// 腳本運行時,初始化巡邏路徑InitializePatrolRoute();}void InitializePatrolRoute(){foreach (Transform t in PatrolRoute){Locations.Add(t);}}
讓Enemy跟著巡邏點動起來,在此編輯腳本
// ...
// 默認第一個索引為0,即先走第一個巡邏點
private int _locationIndex = 0;// NavMeshAgent的引用
private NavMeshAgent _agent;void Start()
{// 獲取Enemy對象上的NavMeshAgent組件的引用_agent = GetComponent<NavMeshAgent>(); // 腳本運行時,初始化巡邏路徑InitializePatrolRoute(); MoveToNextPatrolLocation();
}// 移動到下一個巡邏點
void MoveToNextPatrolLocation()
{_agent.destination = Locations[_locationIndex].position;
}
//....
現在只會移動到第一個巡邏點,然后我們的目的是,在每個巡邏點,循環往復的進行移動,這個時候就需要在Update()中搞點事情了。
void Update()
{// 檢測當前對象與目標位置距離if(_agent.remainingDistance < 0.2f && !_agent.pathPending){MoveToNextPatrolLocation();}
}// 移動到下一個巡邏點void MoveToNextPatrolLocation(){if(Locations.Count == 0) return;_agent.destination = Locations[_locationIndex].position;// 索引循環往復_locationIndex = (_locationIndex + 1) % Locations.Count; }
🎃 敵人游戲機制
敵人按照巡邏路線進行巡邏,當與玩家碰撞后,減少玩家生命值,然后敵人回到初始位置,玩家也可以攻擊敵人,減少其生命值。
追蹤玩家
當玩家進入敵人警戒范圍時,敵人會直接奔向玩家。
// 玩家的Transform引用public Transform Player;void Start(){// ...// 獲取Player對象的引用Player = GameObject.Find("Player").transform;}// ...// 碰撞觸發器void OnTriggerEnter(Collider other){if(other.name == "Player"){// 玩家進入到警戒范圍后,敵人奔向玩家_agent.destination = Player.position;Debug.Log("玩家進入警戒范圍");} }
攻擊玩家
攻擊玩家的邏輯很簡單,當玩家與敵人發生碰撞時,玩家減少生命值,并且在GUI上顯示
修改玩家的Playerbehavior腳本(敵人腳本也可以,因為碰撞是相互的)
// gameManager的引用,因為HP,收集道具數量都在該處存放
private GameBehavior _gameManager;void Start(){// 將當前player的剛體組件獲取并設置_rb = GetComponent<Rigidbody>();// 獲取當前角色的膠囊碰撞體_col = GetComponent<CapsuleCollider>();_gameManager = GameObject.Find("Game_Manager").GetComponent<GameBehavior>();}// 發生碰撞時void OnCollisionEnter(Collision collision){// 如果與敵人碰撞則HP-1if (collision.gameObject.name == "Enemy"){_gameManager.HP -= 1;}}
這里的話需要注意一個問題,就是敵人身上的碰撞體為碰撞觸發器,勾選了Is Trigger,然后直接通過Player的OnCollisionEnter()并不會觸發,需要給敵人再加入一個碰撞體就可以了。
子彈碰撞
為Player添加反擊手段,邏輯就是敵人有三滴血,當子彈射擊三次并且擊中敵人后,敵人自動銷毀。
編輯敵人Enemy的游戲腳本
// 敵人的HP=3
private int _lives = 3;
public int EnemyLives
{get { return _lives; }set{_lives = value;if (_lives <= 0){Destroy(this.gameObject);Debug.Log("敵人死亡");}}
}void OnCollisionEnter(Collision collision)
{// 當子彈碰撞到敵人時if(collision.gameObject.name == "Bullet(Clone)"){EnemyLives -= 1;Debug.Log("遭受子彈射擊");}
}
完善游戲失敗條件
邏輯是,生命值為0時,游戲暫停,出現一個按鈕說:你輸了。
先繪制一個Button按鈕,和之前的一樣,默認禁用即可,當生命值為0時,在腳本內調用。
編輯GameManager的腳本
public Button LossButton;
private int _playerHp = 10;
public int HP
{get { return _playerHp; }set{//...if (_playerHp <= 0){ProgressText.text = "You want another life with that?";LossButton.gameObject.SetActive(true);Time.timeScale = 0f;}else{ProgressText.text = "Ouch... that's got hurt.";}Debug.LogFormat("HP:{0}", _playerHp);}
}
🤗 總結歸納
本文主要介紹了在 Unity 中實現 AI 導航以及構建敵人游戲機制的方法。包括導航系統的基本組件、導航行為和邏輯,以及敵人的巡邏、追蹤玩家、攻擊玩家、被子彈擊中和完善游戲失敗條件等功能。
重要亮點
- 導航系統基本組件:Unity 中 AI 導航的基礎是 NavMesh,它將場景中的可行走表面進行三角剖分形成網格。NavMeshAgent 是附加在游戲對象上用于在 NavMesh 上移動的組件,NavMeshObstacle 用于表示動態障礙物。
- 導航行為和邏輯:NavMeshAgent 能自動規劃從當前位置到目標位置的最短路徑,處理避障和動態導航。例如,當場景中的障礙物或目標位置變化時,會自動重新計算路徑。
- 敵人巡邏機制:通過為敵人添加 NavMeshAgent,設置巡邏點,編寫腳本實現敵人在巡邏點之間循環往復移動。
- 追蹤玩家:當玩家進入敵人警戒范圍時,敵人會奔向玩家。通過在敵人腳本中檢測玩家進入碰撞觸發器,設置敵人的目標位置為玩家位置。
- 攻擊玩家:當玩家與敵人發生碰撞時,玩家減少生命值。在玩家腳本和敵人腳本中分別處理碰撞事件。
- 完善游戲失敗條件:當玩家生命值為 0 時,游戲暫停,出現 “你輸了” 按鈕。在游戲管理器腳本中根據玩家生命值控制按鈕的顯示和游戲時間的暫停。