使用 XNA/AI/人工智慧進行遊戲開發
遊戲始終提供了一個開發人工智慧的環境。因此,在過去幾十年中,它成為了遊戲中最重要的組成部分之一。如今,擁有複雜 AI 的遊戲處於技術的前沿。您將在許多遊戲中需要一些更簡單的演算法,也需要一些更復雜的演算法。它在許多情況下使用,例如
- 控制 NPC
- 尋路
- 動態遊戲難度平衡
- 戰鬥
早在 20 世紀 70 年代遊戲開發的初期,程式設計師和開發者就遇到了遊戲人工智慧領域。但在那個時候,AI 很簡單,可以說很謙虛,這種狀態一直持續到幾年前才改變。
- "AI 已從遊戲界的紅頭髮繼女悄悄轉變為該行業的閃耀明星" - 史蒂夫·拉賓,AI 遊戲程式設計智慧,2002 年,第 3 頁
第一款以單人模式為特色的遊戲,因此 AI 在其早期階段就像雅達利遊戲“Qwak!”一樣,並沒有像我們今天描述的那樣擁有 AI。敵人的移動主要是預定義的,並以模式形式儲存。只有隨著硬體的改進,例如微處理器的加入,才允許進行更多的計算,才能實現更多隨機元素。由此產生的遊戲包括太空侵略者、銀河戰士和吃豆人。這些遊戲呈現了例如不斷提高的難度等級、複雜多樣化的敵人移動、取決於玩家輸入的事件,甚至每個敵人的不同個性。隨著 20 世紀 90 年代新遊戲型別的出現,新的 AI 工具被開發出來並使用。其中包括有限狀態機。在較新的遊戲中,AI 成為遊戲的核心內容。AI 的改進不僅取決於給定的硬體元件。事實上,這絕對是一個非常重要的方面。有一些問題,如果沒有足夠的處理器資源,就無法解決。但應該補充的是,在遊戲開發的初期,程式設計師並沒有把 AI 太當回事。AI 通常是在完成所有高優先順序任務後才完成的。[1]
- 如今,AI 已經爬上了遊戲梯子的頂端,成為最高優先順序任務。
遊戲中存在無數需要人工智慧的場景。那麼,您如何解決追逐、群體行為或尋路問題呢?聰明的程式設計師和開發者想出了一些巧妙的技術和演算法,為您的遊戲增添了一絲智慧。以下將解釋一些有趣的演算法和 AI 場景。我不會聲稱這是一個完整的列表。但我會向您展示您將要面對的基本場景。[2]
無論您擁有什麼樣的遊戲,都有可能遇到追逐和躲避的情況。
描述追逐和躲避的最簡單方法是,首先找出捕食者和獵物之間的距離,然後在追逐的情況下減小距離,在躲避的情況下增加距離。
| 追逐 | 躲避 |
|---|---|
if (predatorPos > preyPos)
predatorPos --;
else if (predatorPos < preyPos)
predatorPos ++;
|
if (preyPos> predatorPos )
preyPos++;
else if (preyX < predatorPos )
preyPos-- ? > ;
|
它確實有效,但顯然這不是一種非常自然的方法。
讓捕食者沿著直線朝向獵物移動,這更符合現實。

查詢直線路徑的演算法稍微複雜一些。您需要找到一條直線且最短的路徑,避免捕食者和獵物之間不必要的步驟。有很多有用的演算法可以幫助我們解決這個問題。想想那些為畫素環境構建的直線繪製演算法。這些演算法用於查詢從起點到目的地的直線路徑。但我們的演算法還需要滿足另一個標準,那就是找到最短路徑。所以現在該向 Bresenham 求助了。Bresenham 的演算法可以提供我們想要的東西。您可以在這裡找到它。
對於捕食者來說,更有效的方法是在獵物軌跡的某個點攔截獵物。

應根據捕食者和獵物的相對位置和速度選擇攔截點。為了預測該點,您必須考慮三個值。它們是位置、方向和速度。
- 相對速度(接近速度)
- 相對距離(接近範圍)
- 以等於接近速度的速度(接近時間)行駛相對距離所需的時間
- 獵物預測位置(目標點)
Vector FindInterceptingPoint(void)
{
Vector v, d;
Double t;
v = Prey.v - Predator.v; // closing velocity
d = Prey.pos - Predator.pos; // range to close
t = d.Magnitude() / v.Magnitude(); // time to close
return Prey.pos + (Prey.v * t); // target point
}
不同移動和機動模式是預定義的。使用這些複雜模式的電腦控制角色會給人一種智慧行為的錯覺。標準演算法使用編碼指令列表或陣列。這些指令告訴電腦控制角色在遊戲迴圈中的每一步如何移動。
以下模式借自 O'REILLY 的《遊戲開發者 AI》一書
Pattern[0].turnRight = 0;
Pattern[0].turnLeft = 0;
Pattern[0].stepForward = 2;
Pattern[0].stepBackward = 0;
Pattern[1].turnRight = 0;
Pattern[1].turnLeft = 0;
Pattern[1].stepForward = 2;
Pattern[1].stepBackward = 0;
Pattern[2].turnRight = 10;
Pattern[2].turnLeft = 0;
Pattern[2].stepForward = 0;
Pattern[2].stepBackward = 0;
Pattern[3].turnRight = 10;
Pattern[3].turnLeft = 0;
Pattern[3].stepForward = 0;
Pattern[3].stepBackward = 0;
Pattern[4].turnRight = 0;
Pattern[4].turnLeft = 0;
Pattern[4].stepForward = 2;
Pattern[4].stepBackward = 0;
Pattern[5].turnRight = 0;
Pattern[5].turnLeft = 0;
Pattern[5].stepForward = 2;
Pattern[5].stepBackward = 0;
Pattern[6].turnRight = 0;
Pattern[6].turnLeft = 10;
Pattern[6].stepForward = 0;
Pattern[6].stepBackward = 0;
.
.
.
該模式中編碼的指令是
- 向前移動 2 個距離單位
- 向前移動 2 個距離單位
- 向右轉 10 度
- 向右轉 10 度
- 向前移動 2 個距離單位
- 向前移動 2 個距離單位
- 向左轉 10 度
有時在遊戲中,讓非玩家角色以凝聚的群體移動更現實。讓我們考慮鳥類、綿羊和所有這些總是躲在羊群安全中的群居動物。或者那些大型電腦控制的人類、巨魔或獸人單位。叢集絕對是遊戲中常見的表演。看看我從 roxlu 借來的剪輯:http://vimeo.com/5352863。
有一些基本的叢集演算法來實現我們期望的行為。我們將看看 Craig Reynolds 演算法。這種實現是無領導的。所有鳥類(Craig Reynolds 創造的術語,指的是模擬的鳥群)個體都遵循群體本身。
該演算法遵循三個簡單的規則
- 分離
- 轉向以避免撞到鄰居
- 對齊
- 轉向以使其與鄰居的平均航向對齊
- 凝聚
- 轉向朝向鄰居的平均位置
您可以在此處找到對該演算法及其實現的解釋:http://oreilly.com/catalog/ai/chapter/ch04.pdf
有成千上萬個獨立的尋路問題。您找不到一種演算法作為所有這些問題的萬能藥方。即使 A* 演算法(實際上是許多問題的理想解決方案)也不適合所有情況。
追逐和躲避章節中的演算法進行了一些基本的尋路。我們澄清了視距演算法建立了一種更現實的移動方式。現在讓我們看一下避障
實現避障最簡單的方法如下
if Player In Line of Sight
{
Follow Straight Path to Player
}
else
{
Move in Random Direction
}
這裡的問題是由於它的簡單性,它只適用於少量障礙物。讓角色繞過障礙物會更有效:當角色與障礙物接壤時,它會在障礙物周圍追蹤。一旦目標進入角色的視線,它就會停止追蹤。
在這裡,玩家本身正在為非玩家角色定義路徑。這樣,電腦控制的玩家看起來非常聰明。玩家在其走的每一步都留下了路標。非玩家角色只會沿著這些足跡前進。
例如,您需要在賽車遊戲中使用路徑跟隨。沒有明確的目的地。只有一條預定義的道路必須遵循。
尋路是一項非常耗時的任務。特別是當您有一個具有大量障礙物的大環境時。路點減少了這個問題。主要思想是在遊戲環境中放置節點,然後使用它們進行廉價的尋路演算法。
A* 演算法為尋路問題提供了一種有效的解決方案。
我們查看了許多需要 AI 方法的遊戲玩法情況。但這些並非全部。如果您好奇,請檢視 O'REILLY 的《遊戲開發者 AI》一書
Pong 最初由 Atari 於 1972 年釋出。它包含一個黑色螢幕,螢幕兩側各有一個球拍,中間有一個球。球拍由玩家控制,玩家可以上下移動球拍來接住左右移動的球。如果一方玩家錯過球,對方將獲得一分。當一方玩家達到一定分數(例如 10 分)時,遊戲結束。[3]
Pong 中的 AI
[edit | edit source]如果你選擇與電腦對戰,實際上你是在與一個 AI 演算法對抗。你總是面臨著是否向上、向下移動球拍,或者保持靜止的決策。電腦也是如此。
第一種方法
[edit | edit source]解決這個問題的一個簡單方法是分析球的當前狀態(位置和運動方向),然後決定如何移動球拍。透過知道球的方向(以度數表示的角度),你可以確定球是在螢幕上向上還是向下移動。電腦現在可以計算球和球拍在 y 軸上的位置差,並根據這個資訊決定將球拍向下或向上移動,以減少這個差值,理想情況下直到差值變為 0。如果差值接近 0,球拍可以停止,等待球的運動需要採取其他動作。
int difference = ball.positionY – paddle.positionY;
if(difference >= 5 || difference <= -5)
{
if(difference > 0)
{
paddle.moveDown();
}
else
{
paddle.moveUp();
}
}
這種方法的優點是易於實現。它不需要太多計算量(效能良好),並且往往會犯一些錯誤,或者在某些情況下措手不及。因此,它看起來更像人類操作,並且是可以戰勝的。對於電腦 AI 來說,一種會讓它措手不及的情況是,當球拍向上跟隨球移動時,在球到達球拍邊緣之前,球撞到了頂壁並改變了方向向下移動。在這種情況下,球拍可能反應太慢(尤其是在更高水平時球的速度更快),無法對方向變化做出反應,從而失去球。
第二種方法
[edit | edit source]第二種方法稍微複雜一點,需要更多的計算。你只需要球的當前位置和當前運動方向——這些都是你可以從遊戲引擎中獲取的資訊。根據這些值,可以確定球的未來運動軌跡(包括所有牆壁碰撞和方向變化),以及球最終可能擊中 AI 玩家球拍的位置。如果你找到了這個點——而且這個點永遠不會改變,因為標準的 Pong 遊戲沒有突然發生事件來改變球的方向——你只需要直接將球拍移動到這個點上,就能接住球,並將球擊回對手的半場。這種方法也可以稍微修改,將球拍放置在擊球時能擊中球拍角落的位置,使其稍微旋轉一下。
即使它是解決這個問題的改進方案,也有一些缺點。它更難實現,可能需要更長時間,並且需要更多資源來計算球的最終位置(對於行動電話、掌上游戲機等慢速系統來說可能太慢),最重要的是(在我看來),你將有一個實際上不可戰勝的完美對手,因為它總是以最佳方式擊球,這對於真正的玩家來說可能很煩人。
更多示例
[edit | edit source]- Eliza
- 井字棋
困難
[edit | edit source]AI 必須在遊戲過程中即時計算,因此(良好)效能對於讓遊戲流暢執行至關重要。為了確保這一點,許多簡化、變通方法、演算法中的作弊行為會模擬玩家的理想行為。這樣既快又聰明。當效能是一個重要問題時,很明顯,對所有可能的決策進行暴力破解並不是處理遊戲 AI 中這種情況的最佳方法。
另一個關鍵事實是,即使電腦玩家能夠做到完美,也不應該完美地玩遊戲。作弊在這個話題中是一個很大的詞,因為電腦知道所有事實,並擁有關於遊戲世界的各種資訊,因此可以讓 AI 玩家瞭解一些在現實中不應該知道的事情。玩家必須認為自己是在與真正的對手對戰,而不是與電腦對戰,這就是為什麼首先 AI 必須是可以戰勝的(而不是無敵的),而且它必須表現得像人類一樣(犯錯、在某些情況下隨機行動等)。如果 AI 沒有以這種方式行事,遊戲很快就會對玩家變得非常無聊,或者如果玩家沒有機會獲勝就會令人沮喪。一些遊戲使用近似演算法而不是完美的解決方案,並在它們的演算法中實施“錯誤的”(或更差的)決策。
- 六條經驗法則和啟發式方法,足以提供良好的遊戲體驗
參考文獻
[edit | edit source]- ↑ Steve Rabin (2002). AI Game Programming Wisdom. Cengage Learning.
- ↑ David M. Bourg, Glenn Seemann (2004). AI for Game Developers. O'Reilly Media.
- ↑ http://en.wikipedia.org/wiki/Pong
作者
[edit | edit source]- iSteffi