一個算完整的敵人AI原型程式碼

參考『Unity遊戲開發實戰』練習寫的敵人AI,想做得功能多一些,不過行為過於複雜,就先大概寫這樣的結構了。
因為主要是想放在移動裝置上的橫向平台遊戲,沒要做太複雜的判斷跟行為,但又希望考慮到擴展性(沿用到別的PC遊戲作品),所以在敵人狀態上用距離、角度,來做警戒、攻擊、追擊的不同判斷。
找時間再延伸讓警戒到一定程度,繼續追擊到攻擊範圍......
應該有些細節還不是很成熟,但希望能幫上大家 :)


之前都是用updata撰寫,不過最後會一堆boolean很複雜、麻煩,這個方式還蠻不錯的,雖然會多好多自定義的fnuction,不知道這種方式是好是壞,希望有前輩能夠給些意見、想法。
另外附上我練習用的while迴圈,練習這個才搞懂為什麼這樣子寫......

#pragma strict
var while1Int:int; // While迴圈1計次
var while2Int:int; // While迴圈2計次
var while3Int:int; // While迴圈2計次
function Start () {
while(true){
print("進入While迴圈1");
yield testWhile1(); // 呼叫執行 testWhile1
print("進入While迴圈2");
yield testWhile2(); // 呼叫執行 testWhile2
print("進入While迴圈3");
yield testWhile3(); // 呼叫執行 testWhile3
}
}

function testWhile1(){
while(true){
print("進行While迴圈1");
while1Int++; // 累加While迴圈1計次
yield WaitForSeconds (3.0); // 延遲 3.0 秒
if (while1Int >= 3){ // 假使 迴圈計次 大於等於3 時 
while1Int = 0; // 歸零 迴圈1 計次
print("離開While迴圈1");
break; // 直接離開 迴圈1
}
}
}
function testWhile2(){
while(true){
print("進行While迴圈2");
while2Int++; // 累加While迴圈1計次
yield WaitForSeconds (3.0); // 延遲 3.0 秒
if (while2Int >= 3){ // 假使 迴圈計次 大於等於3 時
while2Int = 0; // 歸零 迴圈2 計次
print("離開While迴圈2");
break; // 直接離開 迴圈2
}
}
}
function testWhile3(){
while(true){
print("進行While迴圈3");
while3Int++; // 累加While迴圈3計次
yield WaitForSeconds (3.0); // 延遲 3.0 秒
if (while3Int >= 3){ // 假使 迴圈計次 大於等於3 時
while3Int = 0; // 歸零 迴圈3 計次
print("離開While迴圈3");
break; // 直接離開 迴圈3
}
}
}



以下是實際使用的AI跟元件結構
下圖是元件的結構,有兩個空物件當作兩個來回的巡邏點,還有玩家的方塊要自己拉進去。



#pragma strict
// 待機時間
var idleTime : float = 2.0;

// 巡邏參數
var patrolTarget : Transform; // 宣告 目前巡邏目標
var patrolTarget1 : Transform;   // 宣告 巡邏目標
var patrolTarget2 : Transform;   // 宣告 巡邏目標
var patrolSpeed : float = 2.5; // 巡邏速度

// 朝向目標參數
var targetPlayer : Transform;   // 注視目標
var lookAtPos : Vector3; // 注視座標

// 尋追敵參數
var seachAngle :float = 20.0; // 尋敵角度
var seachDis : float = 8.0;   // 尋敵距離
var followSpeed : float = 1.0; // 追敵旋轉速度
var followRotateSpeed : float = 3.5; // 追敵速度

// 攻擊參數
var attackRange : float = 2.0; // 攻擊範圍

// 警戒參數
var warnRange : float = 4.0; // 警戒範圍

// 宣告材質
var idleMaterial : Material;
var patrolMaterial : Material;
var followMaterial : Material;
var warnMaterial : Material;
var attackMaterial : Material;

function Start () {
// 重置巡邏目標
switchPatrolTaget();
print("迴圈前待機");
yield WaitForSeconds(idleTime);
while(true){
yield Idle(); // 呼叫執行 Idle
yield Attack(); // 呼叫執行 Attack
}
}

// 待機狀態
function Idle () {
while(true){
print("待機中");
// 如果 到達巡邏點 則 切換巡邏點 並 待機。 否則 巡邏移動
if (PatrolState()){
switchPatrolTaget();
renderer.material = idleMaterial;
yield WaitForSeconds(idleTime);
}else{
renderer.material = patrolMaterial;
Patrol();
yield;
}
// 如果敵人進入尋敵範圍及角度,跳出待機迴圈
if (SeachEnemy()){
print("SeachEnemy");
return;
yield;
}
yield;
}
}

// 切換巡邏點
function switchPatrolTaget(){
if (patrolTarget == patrolTarget1){
patrolTarget = patrolTarget2;
}else{
patrolTarget = patrolTarget1;
}
}

// 巡邏移動狀態
function PatrolState (){
if (transform.position == patrolTarget.position){
return true;
}else{
return false;
}
}

// 巡邏移動狀態
function Patrol (){
// 確認巡邏點角度
targetAngle(patrolTarget);
// 面對目前目標巡邏點
FaceToNowTarget();
// 向巡邏點移動
transform.position = Vector3.MoveTowards(transform.position,patrolTarget.position,Time.deltaTime * patrolSpeed);  
}

// 判斷敵人是否在視野範圍內
function SeachEnemy (){
// 距離小於 視野範圍 則 回傳 true。否則 回傳 false
if (seachDis > targetDistance(targetPlayer)  &&  Mathf.Abs(targetAngle(targetPlayer)) < seachAngle)
{
return true;
}else{
return false;
}
}

// 送入Transform參數 判斷與目標之間距離 並 回傳距離
function targetDistance(nowTaget:Transform){
var dis : float = Vector3.Distance(transform.position, nowTaget.transform.position);
return dis;
}

// 送入Transform參數 判斷與目標之間角度 並 回傳角度
function targetAngle (nowTaget:Transform){
// 宣告 注視目標座標 為 目標位置
  lookAtPos = nowTaget.position;
// 本身與目標之間相對位置
var relative : Vector3 = transform.InverseTransformPoint(lookAtPos);
// 計算兩者之間角度
var angle : float = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
lookAtPos.y = transform.position.y;
return angle;
}

// 跟隨敵人
function FollowEnemy (){
renderer.material = followMaterial;
FaceToPlayer();
transform.position = Vector3.MoveTowards(transform.position,targetPlayer.position,Time.deltaTime * followSpeed);
}

// 面向目標
function FaceToNowTarget (){
// 注視目標
transform.LookAt (lookAtPos);
}

// 面向玩家
function FaceToPlayer (){
var rotation = Quaternion.LookRotation(lookAtPos - transform.position);
    transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * followRotateSpeed);
}

// 攻擊敵人狀態
function Attack (){
while(true){
if (SeachEnemy()){
if (attackRange >= targetDistance(targetPlayer)){
AttackEnemy();
yield;
}else if(warnRange >= targetDistance(targetPlayer)){ 
Warn();
yield;
}else{
FollowEnemy();
yield;
}
}else{
return;
yield;
}
}
}

// 攻擊敵人
function AttackEnemy (){
renderer.material = attackMaterial;
FaceToNowTarget();
print("AttackPlayer");
}

// 警戒敵人
function Warn (){
renderer.material = warnMaterial;
FaceToNowTarget();
print("WarnPlayer");
}


留言

這個網誌中的熱門文章

參加畢業展之設計類展場小心得

Unity 判斷兩物之間距離、角度,並注視目標

Action Game Maker 1.03 正體中文漢化版