天天看點

遊戲AI入門系列之可預測AI

本文為作者原創或翻譯,轉載請注明,不得移除此聲明,不得用于商業用途。

作者:[email protected] 首發連結: http://blog.csdn.net/rellikt/archive/2010/09/05/5864844.aspx 說到遊戲AI,總是讓人感到那麼的神秘與興奮。那些編寫良好的AI,往往可以通過不作弊或者少量作弊就能戰勝大多數的玩家,在棋牌類遊戲,解密類遊戲中,AI甚至已經達到了不可戰勝的地位。在圖形學已經日趨成熟的今天,各種遊戲的視覺效果往往已經讓人心滿意足,接下來能決定遊戲成敗的往往就遊戲AI的好壞了。今天就讓我們來了解一下遊戲AI吧。 by rellikt

AI簡介

AI全稱Artificial Intelligence(人工智能),大多數情況下它指代一段寫在計算機中的程式,這段代碼使計算機的工作看上去能像人腦一樣思考。你不需要把電腦AI想得有多神秘,在我編寫這篇部落格的時候,php編輯器對我做出的文法修改和格式調整就是AI的一種。可以說在現在生活中AI是無孔不入的。 人工智能顧名思義,當然是一種模拟人的思維,最終目标是和人一樣能思考的東西。其實我們現在已經有很多類似的拟人機器人的程式在遊戲中存在和應用。他們甚至能學習,探索,進化,生育。這是不是聽上去很不可思議?事實上AI中很多算法,例如神經元網絡算法,遺傳算法,模糊邏輯,機器學習等就是為了模拟這些行為而生的。我這系列的部落格就将為你慢慢解說上述AI算法。 言歸正傳,我們要設計一個遊戲中的AI系統,我這裡想提醒你最重要的一點就是拟人。幾乎每一個好玩的遊戲AI最大的特點就是模拟玩家,如果你的遊戲AI看上去有點笨,那就是你還沒把遊戲AI擺到玩家的角度去進行思考,或者思考不足。如果你的遊戲AI看上去太機靈了,那一定是你動用了一些隻有電腦才能動用的作弊手段。而這任何一種傾向都不是遊戲AI成功的标志,事實上現在大多數遊戲都在試圖用作弊來彌補算法上的不足,雖然這種做法在一開始會很有效,但是從長期來看,對于遊戲性來說并不是好事。我一直在等待一款能夠震撼現代遊戲業界的,以AI取勝的遊戲出現,我相信如果真有這麼一款遊戲出現,它的銷量一定不會比Quake或者Unreal差。 by rellikt

可預測AI

我首先想告訴大家,我們在遊戲中最長用到的AI并不是那些神秘兮兮的進階算法。就拿我做過的一個RPG遊戲來說,圖中小怪最多用的AI算法隻是随機做一定範圍的無規則運動,然後在玩家靠近的情況下,靠近玩家并對玩家進行攻擊,這種算法在低級怪特别多的圖裡面是常用的。我們不可能為了低級怪寫進階AI,遊戲系統不需要有那麼智能的低級怪,同時大量的進階AI占用的系統資源也是我們不希望的。 那這最簡單的AI我們是怎麼寫的呢?最簡單的我們先要一個基礎的運動。運動就必須要定義速度,定義速度我們可以這麼寫: monster_speedX += velocity_x; monster_speedY += velocity_y; 這樣一個簡單的運動就定義好了。 by rellikt 然後是随機運動,我們可以在遊戲主循環中這麼寫: //get a random value for speed. randVelocityX = rand() % 10; randVelocityY = rand() % 10; //add time to accumulator, if we accumulate for more than 5s then we clean up the accumulator //and change our velocity. accumulatorTime += deltaTime; if(accumulatorTime > 5) { monster_speedX += randVelocityX; monster_speedY += randVelocityY; accumulatorTime = 0; } 用上面這段代碼我們就可以實作一個簡單的每5s換一個方向的随機運動的AI,當然具體每個值的範圍還需要我們再仔細确認一下。 最後讓我們來實作追蹤的算法,說到追蹤,我們現在有必要做一些簡單的定義: class Actor { // more definition float positionX; float positionY; float velocityX:  float velocityY; // more definition..... } 我們一般會給2D世界中的物體這些最基本的屬性定義。這裡的位置屬性positionX和positionY往往會在一個範圍中,比如方型地圖就是MIN_X, MAX_X, MIN_Y, MIN_Y。而速度屬性就會有 VelocityX * VelocityX + VelocityY * VelocityY < MAX_SPEED*MAX_SPEED的限制,注意盡量避免用sqrt這個會嚴重影響效率的運算。這裡有時候在大量運算的時候為了效率考慮甚至直接使用-MAX_SPEEDX < VelocityX < MAX_SPEEDX和-MAX_SPEEDY < VelocityY < MAX_SPEEDY作為限制條件。這這個限制條件下,追蹤的算法可以簡單的寫為: by rellikt if(player.positionX < monster.positionX) { monster.velocityX = -MAX_SPEEDX; } else if(player.posionX > monster.positionX) { monster.velocityX = MAX_SPEEDX; } else monster.velocityX = 0; if(player.positionX < monster.positionX) { monster.velocityX = -MAX_SPEEDX; } else if(player.posionX > monster.positionX) { monster.velocityX = MAX_SPEEDX; } else monster.velocityX = 0; 寫到這裡我們一個最簡單的追蹤算法就算完成了。但是為了讓我們的遊戲看起來更加真實,更加有趣,不妨在多加點拟人的東西。

拟人化的可預測AI

首先我們知道人的極限速度是恒定的,也就是說如果定義速度向量(x,y)那麼這個向量的模是有最大值的,這也就是我前面提到的那個最初的速度限定表達式的來源。我們一般可以使用下面的算法來實作這點: vector2f direction = vector2f(player.positionX - monster.positionX,  player.positionY - monster.positionY); vector2f normalizedDirection = vector2f.normalize(direction); vector2f monsterSpeed = speedMax * normalizedDirection; 上面的vector2f是一些算法庫中常見的資料結構,它會為我們完成一些我們需要的向量運算。這裡我不想再展開讨論關于線性代數的一些内容。 另外一點就是對于突然的變速或者變向人是會有反應時間的,并不能認為人能瞬間反應,這裡可以引入反應時間reactionTime。這樣做的話可以使玩家能夠有可能通過一下變速操作躲開monster 這裡實作的時候可以把前面算出的monsterSpeed壓入一個隊列中,然後通過合适的隊列長度來映射reactionTime,實作需要的反應時間。 另外将變速等AI指令封裝成一個個消息讓AI agent來處理也是一個常見的AI程式設計模型。關于消息處理等程式設計模型,有興趣的讀者可以去參閱一些MFC的代碼。by rellikt 最後在靠近獵物的時候,我們可以使monster因為興奮做出一些偏移或者減速,使玩家有更多的操作空間,這也是很有趣的。事實上我們如果對這個概念進行一下拓展,很容易就會想到給AI設定一些State來實作行為邏輯的控制。這就會牽涉到我們将要在下期部落格中談到的AI狀态機的建立了。 下面是從一段小程式中摘抄來的一段簡單的AI追蹤的代碼,有興趣的讀者可以做一下參考。在寫一些腳本AI的時候也許我們需要的就是這些了。 // compute vector toward player float vx = player_x - mines[index].varsI[INDEX_WORLD_X]; float vy = player_y - mines[index].varsI[INDEX_WORLD_Y]; // normalize vector (sorta :) float length = Fast_Distance_2D(vx,vy); // only track if reasonable close if (length < MIN_MINE_TRACKING_DIST)   {   vx=MINE_TRACKING_RATE*vx/length;    vy=MINE_TRACKING_RATE*vy/length;   // add velocity vector to current velocity   mines[index].xv+=vx;   mines[index].yv+=vy;   // add a little noise   if ((rand()%10)==1)   {   vx = RAND_RANGE(-1,1);   vy = RAND_RANGE(-1,1);   mines[index].xv+=vx; mines[index].yv+=vy;   }// end if   // test velocity vector of mines   length = Fast_Distance_2D(mines[index].xv, mines[index].yv);   // test for velocity overflow and slow   if (length > MAX_MINE_VELOCITY)   {   // slow down   mines[index].xv*=0.75;   mines[index].yv*=0.75;   } // end if   } // end if else   {   // add a random velocity component   if ((rand()%30)==1)   {   vx = RAND_RANGE(-2,2);   vy = RAND_RANGE(-2,2);   // add velocity vector to current velocity   mines[index].xv+=vx;   mines[index].yv+=vy;   // test velocity vector of mines   length = Fast_Distance_2D(mines[index].xv, mines[index].yv);  // test for velocity overflow and slow    if (length > MAX_MINE_VELOCITY) { // slow down mines[index].xv*=0.75; mines[index].yv*=0.75; } // end if   } // end if } // end else 追蹤問題是一個很複雜的AI問題,關于這方面的算法有很多,如果你想讓自己的AI agent能在複雜的地形上進行追蹤,那麼可能你會 需要pathfinding的算法。如果你想讓自己AI agent能有加速度,減速度,力矩旋轉,碰撞檢測等更真實的實體效果,你可能會需要一個好用的實體庫。這些部分已經超出了可預測AI的範疇,這裡就不做太多拓展了。上面那個例子對于我們現在的可預測AI來已經差不多夠用了 , 在後面的篇幅中也許我會提到更多的關于追蹤的AI算法。 我這裡最想提醒大家注意的一點其實還是拟人化的AI。往往就是那 些拟人化的AI細節決定了一個遊戲AI的好壞。

結論

以上就是可預測AI的一個基本概念。對我們程式來說這部分的工作更注重的細節,有很多細節都是通過認真觀察生活,觀察其他成名遊戲得來的。事實上可預測AI的制作,在遊戲制作領域往往是更偏重Design的活。作為一個AI程式員我們會把更多的精力放在模組化和一些複雜而且有效的算法上面。 下一章我想會介紹一些最基本的AI狀态機的建立和決策樹方面的知識。好吧,今天就先說到這裡。

繼續閱讀