轉至http://gamerboom.com/archives/91424程序員
做爲人類,咱們彷佛總傾向於使用本身所熟悉的解決方法。咱們老是會按照咱們所知道的方式去作某些事,而不是按照作這些事的「最佳」方式。由於老是帶着這種想法,因此咱們很容易使用一些過期的技術,並使用那些同時代人所不理解,或者並非那麼有效的方式去執行特定功能。因此我但願經過本文以及以後的文章向廣大讀者們介紹更多可以在編程中帶給大家幫助的解決方法。今天我要分享的即是行動列表!編程
行動列表是全部遊戲開發者都必須清楚的簡單但卻強大的AI。儘管不能與巨大的AI網絡相匹敵,可是它們容許相對複雜的突發行爲,而且執行起來也很簡單。無論你是剛剛接觸AI編程仍是着眼於擴展工具包的資深開發者,本文都將向你詳細介紹行動列表並提供一些有效的例子幫助你更好地執行解決方法。讓咱們開始吧。網絡
從前併發
幾年前我開始開發《King Randall’s Party》,在遊戲中玩家將建造城堡併爲了保衛它與嘗試着摧毀它的King戰鬥。我須要創造一個可以瞄準玩家城堡並計劃如何去摧毀它以達到本身目的的機智的AI。這對我來講是個巨大的挑戰,由於目標太大我很難同時顧及全面。因此就像任何其餘優秀的程序員那樣,我將其分解成一個比較容易管理的問題集。這時候我所面臨的第一個挑戰即是:我須要創造一個擁有特定行爲集的單位。app
而快速的網絡搜索讓個人精神差點崩潰。只要搜索「遊戲AI」便會出現有關策劃者,有限狀態機,指導行爲,嚮導等等結果。我根本不知道該從哪裏開始,因此我只能作任何理性的人會作的事。對的,我詢問了咱們家的狗。固然了,對於我來講這並非什麼新鮮事了。無論何時當我遭遇技術問題時我都會去詢問咱們家的狗。這時候你可能會想:「Jesse,你太瘋狂了吧。狗怎麼可能瞭解計算機?!」好吧,讓咱們先不要管這個。也許這裏包含或並未包含某種非法技術或者出如今大學審計課堂中的內容。less
無論怎樣我會說:「好吧Frankie,我知道對於AI咱們有許多事要作,可是我並不清楚該從哪裏開始。我該如何爲個人遊戲單位創造一個AI框架?」Frankie的表情讓我以爲本身就是個愚蠢的人,儘管個人高中老師Francis先生已經告訴我不要提出任何愚蠢的問題。我很是確定Frankie並未想到Francis先生。她反問我:「Jesse,一般你是如何開始你的一天的?」ide
我會把我一天須要作的全部事列下來而後根據這些任務的重要性以及所需時間對其進行排序。我這麼回答Frankie而後她說道:「這即是行動列表。」她告訴我行動列表是關於你的遊戲單位在特定時間內運行的任務或行爲列表。這是一種有限狀態系統形式,而且可以被描述爲是帶有一個分支的簡單行爲樹。如下即是關於它們是如何運行的。工具
行動列表如何運行oop
首先你將寫下你但願你的AI所擁有的全部行爲。
而後你須要對它們進行前後排序。從最低到最高。
如今咱們須要迭代該列表去檢查每一個行動是否可以有效執行。而後咱們將檢查行動的粘附性能,若是某個項目會阻礙以後的行動,咱們便會退出該列表。以後咱們便會清楚爲何這種阻礙如此重要了。
在咱們的例子中,咱們的第一個行動「攻擊玩家」將只在AI靠近玩家的時候執行。讓咱們假設它並未靠近玩家,因此它將檢查是否能在當前位置上創造一個階梯等,直至它找到一個可以執行的行動條款,如打破門。而後它將執行破門代碼。
「阻礙」即是在這裏開始發揮做用。若是破門立刻發生,它便不會阻礙到任何以後的行動,而以後的列表內容即可以繼續執行。但一般狀況都不會如此—-行動老是佔據一個以上的幀數。因此在這種狀況下破門行動項目將調用unit.Attack(door),即將單位的當前狀態從等待改爲破門,而在門被打破以前將恢復到true的狀態。
一個簡單的有限狀態機
這聽起來好像是可行的。Frankie提供了一個很好的建議,行動列表彷佛也很是適合個人項目。但在此以前我卻從未聽過它們,因此我對此仍是充滿疑問—-我聽到的大多數關於AI的內容都必須使用基於轉變的有限狀態機,即相似於在MUnity3D用於創造動畫所使用的工具。你將定義一些狀態並識別它們什麼時候以及如何彼此轉變。Frankie向我我解釋了當你在創造一個基於轉變的有限狀態機時,你須要定義你想要擁有的全部狀態,而後你也要定義這些個體狀態間的全部轉變。這真的會讓內容很快變得複雜。若是你擁有一個「傷害」的狀態,你便須要識別其它可以轉變成這個狀態的狀態。如走路變成傷害,跳躍變成傷害,蹲伏變成傷害,攻擊變成傷害等等。這是頗有用的方法,但卻很快便會變得複雜。若是你的AI需求很是簡單的話,這可能會是一筆潛在的沒必要要開支。
關於基於轉變的狀態機的另外一個難點即是調試。當你設置了一箇中止點並着眼於AI當前的狀態,這時候若是沒有額外的調試代碼,你便不可能瞭解AI是如何進入當前的狀態,它以前的狀態是什麼以及怎樣的轉變將它帶到如今的狀態。
行動列表的缺陷
當我進一步深刻行動列表時,我意識到它們很是適合個人執行內容,但同時我也發現它們存在一些缺陷。最大的缺陷便源自其最大的優勢—-簡單性。由於這是一個有序列表,因此我不可能擁有任何複雜的優先順序結構。因此若是我但願「攻擊玩家」排在「破門」前面,但卻在「移向目標」後面,但同時我又但願「移向目標」出如今「破門」後面,我便很難使用行動列表作到這點,而用有限狀態機的話又會很是繁瑣。
總而言之,行動列表對於簡單的AI系統真的頗有幫助,可是若是你想建立的是較爲複雜的AI,那麼執行行動列表便會較困難。也就是說你能夠經過某些方法去擴展行動列表概念而將它們變得更有幫助。
擴展這一律唸的方法
個人一些AI單位須要完成多種任務—-它們可能須要同時移動並發動攻擊。我能夠創造多個行動列表(遊戲邦注:一個處理移動,一個處理攻擊),但這麼作也存在問題,即若是有些移動類型會妨礙攻擊,而有些攻擊又須要單位站在原地的話該怎麼辦?這時候行動線路(Action Lanes)便會發揮做用。
行動線路是對於「阻礙」概念的擴展。基於行動線路,行動項目便可以識別阻礙執行的特定行動項目類型並容許其它項目順暢地執行任務。讓咱們進一步展現這一執行。
行動線路只是決定行動的一種附加方法。每一個行動項目都屬於一條或多條線路,當阻礙性能變成true時,它將添加所屬的線路,由於每一個行動都擁有一條或多條線路,因此它們只是阻礙其它屬於這些線路的行動。舉個例子來講吧,由於單位在創造階梯時必須保持不動且不能發動攻擊,因此攻擊玩家眷於行動線路,移向目標屬於移動線路,創造截圖同時屬於這兩種線路。而後咱們將排列這些項目的順序,若是它們同時執行便會阻礙到後續的行動。
執行案例
如今咱們已經瞭解了這一理論,因此咱們能夠嘗試一次真正的執行過程。首先讓咱們設置行動列表和行動項目。對於行動項目我但願可以分離執行,因此讓咱們創造一個接口。
如下即是BreakDoor行動項目的IActionItem執行:
對於行動列表自己我可使用一個簡單的列表。而後咱們將使用IActionItems去加載它。
以後咱們將設置一個方法並在每幀中迭代列表。須要記得咱們一樣也須要處理阻礙。
若是你想要使用行動線路的話事情會變得更復雜。這時候咱們將把行動線路定義爲位字段而後修改IActionItem界面。
而後咱們將爲這些線路修改迭代程序。若是行動項目的線路遭遇阻礙,它們便會被略過,但檢查的時候還是正常的。若是全部線路都遭到阻礙,咱們便會改變這一循環。
結論
能夠看出須要理解的內容有不少。Frankie讓我總結從中學到的內容,因而我通過綜合思考得出了一些關鍵要點。
行動列表比小型基於轉變的狀態系統更容易建立與維護。
它們創造了一個可識別的優先系統。
存在一些擴展方法去處理擴展功能。
在編程中咱們很難找到最佳解決方法。因此咱們必須確保咱們的工具箱中擁有各類編程工具,如此咱們即可以及時選擇一個可以有效解決問題的合適工具。最終證實行動列表很是適合《King Randall’s Party》。而它是否也一樣適合大家的項目呢?
(本文爲遊戲邦/gamerboom.com編譯,拒絕任何不保留版權的轉發,如需轉載請聯繫:遊戲邦)
Action Lists: Simple, Flexible, Extendable AI
By Jesse Crafts-Finch
As humans, we like to implement solutions which are familiar to us. We get caught up doing things the way we know how to do them, rather than the 「best」 way to do them. It’s easy to get caught up in thinking like this and as a result we end up using outdated technologies and implement features in ways that our modern contemporaries don’t understand, or are simply less effective or efficient. My purpose with this and future papers will be to expose readers to a broad spectrum of solutions that will hopefully help them in their own coding. Today I’ll be covering Action Lists!
Action Lists are a simple yet powerful type of AI that all game developers should know about. While ultimately not scalable for large AI networks, they allow for relatively complex emergent behavior and are easy to implement. Whether you are just getting into AI programming or are an experienced industry veteran looking to expand your toolkit, this presentation will introduce you to Action Lists and provide concrete examples to help you implement your own solutions. Let’s begin the story.
Once Upon A Time…
Several years ago I was starting the development of King Randall’s Party, a game in which the player builds a castle and tries to defend it against the King who is trying to knock it down. I needed to create a reasonably smart AI that could look at the player’s castle and figure out how to circumvent or destroy it in order to reach its objective – the gold pile the player is defending. This was a big challenge, almost too big to consider all at once. So like any good programmer I broke it down into a more manageable problem set. The first challenges: I needed to get the units to have a specific set of behaviors that they would act according to.
A quick internet search caused my mind to implode, of course. Searching for 「Game AI」 brought up results on planners, finite state machines, steering behaviors, flocking, A*, pathfinding, etc. I had no clue where to start, so I did what any reasonable, rational person would do. I asked my dog. This isn’t anything new, of course. Whenever I have a tech problem, I ask my dog. Now I know what you’re thinking 「Jesse, that’s dumb, and you’re crazy… What do dogs know about computers?」 Well, let’s just gloss over that. It may or may not have involved some illegal technology and or college class auditing.
Anyway, I said 「Ok Frankie – there is so much going on with AI, I really don’t have a clue where to start. How should I go about creating an AI framework for my game units?」 Frankie gave me this pretty withering look that basically informed me that I was an idiot and pretty dumb, despite what my high school teacher Mr. Francis may have told me about there never being any stupid questions. I’m pretty sure Frankie wouldn’t have had nice thoughts about Mr. Francis either. 「Jesse」, she asked, 「how do you typically start your day?」
Well, I write everything that I need to do that day down in a list and then prioritize it according to how important the task is and how soon it needs to be done. I explained this to Frankie and she responded 「And that, is an Action list.」 She told me that an Action List is a list of tasks or behaviors that your game units work their way through one at a time. It is a form of finite state system, and could be described as a simple behavior tree with a single branch level. Here is how they work. According to my dog.
How Action Lists Work
First you write down all the behaviors you want your AI to have.
Then you order them according to priority. Lowest to highest.
Now we iterate over the list checking each action to see if it can execute and if it can, executing it. We then check the actions Blocking property, and if the item is Blocking further actions we exit the list. We will get into why Blocking is important later.
In our example here, our first action Attack Player will only execute if the AI is close to the player. Let’s say it is not, so it checks if it can build a ladder at this location (and should it), and so on until it finds an action item it can execute such as Break Door. It then executes the Break Door code.
Here is where Blocking comes into play. If Break Door occurs instantly, then it will not block any further actions and the rest of the list can execute. This is typically not the case – actions usually take up more than one frame. So in this case the Break Door Action Item calls unit.Attack(door), which will change the unit’s CurrentState from Waiting to BreakDoor and will return true until the door is broken.
A Simple Finite State Machine
Well ok. That sounds workable. Frankie had made a good suggestion and Action Lists seem like a great fit for my project. But I’d never heard of them before so I had my doubts – most of what I hear floating around about AI has to do with transition-based finite state machines, similar to what Unity3D uses for animations in Mechanim. You define a bunch of states, and identify when and how they transition between each other. In exchange for some belly scratches, Frankie explained to me when you make a transition based finite state machine, you need to define all the states you want to have, and then also define all of the transitions to and from each of those individual states. This can get complicated really, really fast. If you have a Hurt state, you need to identify every other state that can transition to this state, and when. Walking to hurt; jumping to hurt, crouching to hurt, attacking to hurt. This can be useful, but can also get complicated really fast. If your AI requirements are fairly simple, that’s a lot of potentially unnecessary overhead.
Another difficulty with transition-based state machines is that it is difficult to debug. If you set a break point and look at what the AI’s current state is, it is impossible without additional debug code to know how the AI got into its current state, what the previous states were and what transition was used to get to the current state.
Drawbacks of Action Lists
As I dug into action lists some more, I realized that they were perfect for my implementation, but I also realized they had some drawbacks. The biggest flaw is simply the result of its greatest strength – its simplicity. Because it is a single ordered list, I couldn’t have any sort of complex hierarchy of priorities. So if I wanted Attack Player to be a higher priority than Break Door, but lower than Move To Objective, while also having Move To Objective being lower priority than Break Door… that’s not a simple problem to solve with action lists, but trivial with finite state machines.
In summary, action lists are really useful for reasonably simple AI systems, but the more complex the AI you want to model, the more difficult it will be to implement action lists. That being said, there are a few ways you can extend the concept of Action Lists to make them more powerful.
Ways to Extend This Concept
So some of my AI units are multitaskers – they can move and attack at the same time. I could create multiple action lists – one to handle movement and one to handle attacking, but that is can be problematic – what if some types of movement preclude attacking, and some attacks require the unit to stand still? This is where Action Lanes come in.
Action Lanes are an extension to the concept of Blocking. With Action Lanes, Action Items can now identify specific types of Action Items that it blocks from executing while allowing others to execute without problem. Let’s show this in action.
An Action Lane is just an additional way to determine what action. Each Action Item belongs to one or more lanes, and when its Blocking property returns true, it will add the lanes it belongs to Each action has a lane or multiple lanes they are identified with, and they will only block other actions which belong to those lanes. As an example, Attack Player belongs in the Action Lane, Move to Goal belongs in the Movement lane, and Build Ladder belongs in both since the unit must stand still and cannot attack while building. Then we order these items, and if they execute they will block subsequent actions appropriately.
Example Implementation
Now that we’ve gone over the theory, it is useful to step through a practical implementation. First let’s setup our Action List and Action Items. For Action Items I like to decouple implementation, so let’s make an interface.
Here is an example implementation of the IActionItem for the BreakDoor Action Item.
For the Action List itself we can use a simple List. Then we load it up with the IActionItems.
After that, we setup a method that iterates over the list every frame. Remember we also have to handle blocking.
Things get a bit more complicated if you want to use action lanes. In that case we define Action Lanes as a bitfield and then modify the IActionItem interface.
Then we modify the iterator to take these lanes into account. Action Items will be skipped over if their lane is blocked, but will otherwise check as normal. If all lanes are blocked then we break out of the loop.
Conclusion
So that’s a lot to take in. While coding the other day Frankie asked me to summarize my learnings. Thinking about it, there were a few key takeaways for me.
Action lists are easier to setup and maintain then small transition-based state systems.
They model a recognizable priority system.
There are a few ways they can be extended to handle expanded functionality.
As with anything in coding, there is often no best solution. It is important to keep a large variety of coding tools in our toolbox so that we can pick the right one for the problem at hand. Action Lists turned out to be perfect for King Randall’s Party. Perhaps they will be the right solution for your project?(source:gamedev)