##EasyReadMore##

2009年12月12日 星期六

[教程] [轉貼]AI基礎教程--建立一個基礎的對戰AI







[教程] [轉貼]AI基礎教程--建立一個基礎的對戰AI

文章在:http://bbs.wow8.org/viewthread.php?tid=18007&extra=page%3D1%26amp%3Bfilter%3Dtype%26amp%3Btypeid%3D52

這個要比剛才發的那個基礎一些...所以大家好好看咯

原帖在此http://claymore.blogdriver.com/claymore/172076.html,此是翻譯了老外的,更原始的找不到了。
AI基礎教程--建立一個基礎的對戰AI

這個教程將會一步一步的教你如何去編寫一個基礎的.ai型的對戰AI腳本。如果你以為這個教程可以讓你做出比AMAI更強大的AI的話,那麼你得重新考慮下是不是還要看這個了。本教程僅僅能教會你基礎的JASS,內置本地函數以及你要成為一個對戰AI腳本程序員而必須知道的一些概念。

------------------------------------------
目前的內容:
1.Hello World!
2.採集資源
3.訓練更多的農民
4.建造農場來支持更多的農民
5.一些JASS理論:主函數,變量和類型
6.建造一個兵營並且訓練士兵
7.更多的JASS理論:流控制,條件和循環
8.函數:目前為止我們所用到的
9.何時該建造何物
10.建立和使用函數

即將更新的內容:
1.本地函數總瀏覽
2.語法檢查器
3.MF(Creeping)
4.你們所希望增加的內容或許也會貼出來
-----------------------------
已經發現和修正的BUG:
變量後綴:local已經在聲明中添加進去了。


==============================================================
Hello World!

讓我們從編寫和運行一個簡單的Hello World腳本開始吧!


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello World!" ) // 按F12看看呢?
endfunction


將以上代碼粘貼到一個名為human.ai的文件中去(建立一個文本文件然後改名為human.ai)
運行地圖編輯器
打開你最愛的對戰地圖,將它以另外一個名字保存(最好放在另外一個文件夾裡面)
按F12(或者點Modules(模塊)菜單,然後選擇Import manager(輸入管理器))
在Import manager(輸入管理器)中選擇File ->Import file (文件 -> 輸入文件)
選擇剛剛建立好的human.ai.
鼠標右擊已經導入的文件,選擇Modify file properties(修改文件屬性)
將文件位置設置為\Scripts\human.ai
保存地圖(順便把地圖關上)
運行魔獸爭霸III,選擇單人遊戲->自定義遊戲->你剛剛保存的地圖。
將電腦的種族設置為人類
將地圖設置為完全可見
遊戲開始後在屏幕上尋找Hello World!字樣(它在屏幕上一閃而過)

如果你認為它消失的太快了或者你不確定是否看到了那麼試試下面的代碼:


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call Sleep( 2 ) // <--- 用來確認遊戲已經準備好記錄這條問候了。
call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, "Hello World!" )
endfunction


如果你離開了你的電腦超過了1個小時而沒看到這段話的話請按F12 =)

==============================================================
採集資源

地球人都知道在魔獸裡採集資源是非常重要的...那麼我們來看看一個採集資源的AI腳本該如何編寫吧


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call Sleep( 1.0 ) // 休眠1秒
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

loop // 開始永恆循環
call ClearHarvestAI() // 首先重置採集管理器並且
call HarvestGold( 0, 4 ) // 告訴電腦用4個農民採集金礦
call HarvestWood( 0, 1 ) // 和1個農民採集木頭
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )
call Sleep( 1.0 ) //有時候如果不進行休眠引擎就會自動將這個進程關掉
endloop

call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction


用試驗Hello World!腳本的方法試一下上面這段代碼。只需要把以前導入的文件刪掉換上新的就行了。你也可以在Import Manager(輸入管理器)中右擊以前導入的文件然後選擇replace(替換)。

不要忘了在高級地圖設置中將地圖設置為始終可見,不然你在開始就看不到電腦的行動了。

為什麼我們總是要重置和再分配採集管理器呢?試想,如果不這麼做,當一個農民在采木頭的時候被殺死會怎麼樣?很簡單,永遠不會有另外一個農民來接替他的工作。

==============================================================
訓練更多的農民

用5個農民不知道要花多長時間才能採集夠建立一支軍隊的資源,所以我們需要訓練更多的農民來保證資源供給。


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call Sleep( 1.0 ) // 休眠1秒
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

loop // 開始永恆循環
call ClearHarvestAI() // 首先重置採集管理器並且
call HarvestGold( 0, 4 ) // 告訴電腦用4個農民採集金礦
call HarvestWood( 0, GetUnitCountDone('hpea')-4 ) // 和1個農民採集木頭
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

call SetProduce(1, 'hpea', 0) // 試著訓練一個農民

call Sleep( 3.0 ) // 有時候如果不進行休眠引擎就會自動將這個進程關掉
endloop

call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

調用GetUnitCountDone('hpea')可以清點這個玩家擁有多少的人類農民。這用來計算有多少農民沒有在采金子從而讓他們去採集木頭。看看電腦是怎麼樣建造成堆的農民的...= )

啟動地圖的另一個更加方便我們觀察所創建的AI玩家行為方法:建立一個局域網遊戲,在地圖高級設置中選擇觀察者模式,將你自己設置成觀察者,這樣你在遊戲中就可以點擊電腦的單位或者建築來觀察他採集了多少資源。

感到厭煩了嗎?它是否只在不停的造農民?不?它是需要更多的人口麼?那麼讓我們來教他怎麼多造點農場吧! =P

==============================================================
建造農場來支持更多的農民

讓我們建造更多的農場,訓練更多的農民,採集更多的資源吧!= )
假設我們在還剩餘3個額外人口的時候就建造一個新的農場。


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call Sleep( 1.0 ) // 休眠1秒
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

loop
call ClearHarvestAI()
call HarvestGold( 0, 4 )
call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

// 如果我們正在訓練一個農民那麼下面的SetProduce就會失效
call SetProduce( 1, 'hpea', 0 ) // 訓練人類農民

// 我們的基地可以提供12個人並且每個基地分配6個人
// 比較目前人口上限和單位所佔人口數量來決定
// 是否需要建造一個新的人類農場

if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
call SetProduce( 1, 'hhou', 0 ) // 建造一個人類農場
endif

call Sleep( 3.0 )
endloop

call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

這樣我們就有許多的工人和許多的資源啦!但是我們是不是該把這些資源花掉呢?我們是不是要建造更多的房子來會幫助我們訓練軍隊呢?請注意下面關於建築的部分啦~

==============================================================
一些JASS理論:主函數,變量和類型

呵呵。抱歉了,我們在看建造建築前要打斷一下了~我覺得在其之前有必要想想JASS了.不過如果你對JASS已經很熟悉了就可以跳過這一段啦。好了~準備好來學習JASS吧!

主函數main


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
// 許多的代碼
endfunction


所有在AI腳本中的動作都會在遊戲引擎調用main函數的時候開始。
main函數必須像上面的那樣,不然腳本就無法執行。


變量
通常它被用來記錄一些東東非常好使。在JASS中你必須用變量來保存值。不同類型的變量可以用來保存不同類型的值。


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
local integer number // 告訴(聲明)引擎我們要儲存一個數字(聲明)
local integer anotherNumber // 並且(聲明)另外一個數字
local float decimalNumber // 還有(聲明)一個十進制數
local string someText // 以及(聲明)一些文本

set number = 42
set anotherNumber = 6 * 9
set decimalNumber = 3.14
set someText = "Is this to easy for you? ;-)"
endfunction

實現同樣功能的另外一種方法是:


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
local integer number = 42 // 聲明的同時賦值給它,一行用起來比兩行清楚多了~
local integer anotherNumber = 6 * 9
local float decimalNumber = 3.14
local string someText = "Is this to easy for you? ;-)"
endfunction

另外需要注意的是在函數開始的時候我們必須聲明(告訴引擎)所有我們將在這個函數里使用的變量。下面的例子是不合法的。


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
local integer number = 42 // 聲明的同時賦值給它,一行用起來比兩行清楚多了~
local integer anotherNumber = 6 * 9
local float decimalNumber = 3.14
set anotherNumber = number // 不合法
local string someText = "Is this to easy for you? ;-)"
set anotherNumber = number // 不過在這個地方倒是合法的
endfunction

我們在聲明一個變量前總是要加上local字樣。Local意味著這個變量只在這個函數中有效。這個並不是必須的,稍後我們會看到如何使用全局變量。

現在深呼吸,然後跟我說:「變量是我們的朋友~ = D 」。

好了~~這個理論部分比我預想的要長呢。或許我們應該接著造建築了~或許我們會建造一個兵營,或者訓練一個士兵?我們自己的士兵(或者說是AI的=))

==============================================================
建造一個兵營並且訓練士兵

現在我們就開始建造一支無所不摧的軍隊的第一步吧!扼,先從訓練步兵開始吧 =P


+ Shingo Jass Highlighter 0.41
function main takes nothing returns nothing
call Sleep( 1.0 )
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

loop
call ClearHarvestAI()
call HarvestGold( 0, 4 )
call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

call SetProduce( 1, 'hpea', 0 ) // 訓練人族農民

// 判斷何時建造農場

if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
call SetProduce( 1, 'hhou', 0 )

endif

if ( GetUnitCount( 'hbar' ) <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a barrack" )
call SetProduce( 1, 'hbar', 0 ) // 在0號基地中建立一個人類兵營
else
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to train a footman" )
call SetProduce( 1, 'hfoo', 0 ) // 在0號基地中訓練步兵
endif

call Sleep( 3.0 )
endloop

call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction


或許你已經注意到了在建造農場和訓練單位間停頓了一小會。你知道是為什麼嗎?是我們沒有足夠多的農場嗎?或許我們正被計算目前正在使用多少人口困擾。或者你會在我公佈答案前自己試著修復這個問題.(提示:在條件(conditon)中有個地方錯了)

在我們擴展代碼前,像是建造不同的建築,訓練不同的兵種或者是讓士兵去戰鬥。不過那對這個基礎教程來說偏難了。呵呵。

在指出為什麼它會比較難之前我們又需要學習更多的JASS了~現在到了學習使用更多更加有用的函數的時候了~現在甚至是我們學習所有這些函數的來源和工作原理的時候了!不要走開哦!

==============================================================
更多的JASS理論:流控制,條件和循環

流控制是關於如何實現在一個程序中使用不同的方法的內容。JASS中用if語句和循環結構來支持它。讓我們看看吧。

if

通常我們會遇到在不同的情況下執行不同的操作的問題。最基礎的方法就是當某事為真的時候我們就做什麼,否則我們就做另外的什麼事(或者什麼事也不做)


QUOTE:
if condition then
doSomething
doSomethingMore
endif


+ Shingo Jass Highlighter 0.41
if GetUnitCount( 'hpea' ) <>
call setProduce( 1, 'hpea', 0) // 在我們的起始基地中建造一個農民
endif


嘿!我們如果我們農民足夠的時候該做什麼呢?


QUOTE:
if condition then
doSomething
else
doSomethingElse
endif


+ Shingo Jass Highlighter 0.41
if GetUnitCount( 'hpea' ) <>
call setProduce( 1, 'hpea', 0) // 在我們的起始基地中建造一個農民
else
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
endif

這只在只有2種情況可供選擇的時候有效,我們如何在多種分支中選擇呢?


QUOTE:
if condition then
doSomething
elseif condition2
doSomethingElse
else
doSomeOtherThing
endif


+ Shingo Jass Highlighter 0.41
if GetUnitCount( 'hpea' ) <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
call setProduce( 1, 'hpea', 0) // 在我們的起始基地中建造一個農民
elseif GetUnitCount( 'hpea' ) <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
else
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif


添加更多的elseif語句你就可以寫一個在很多的動作間選擇的程序了。你需要注意的僅僅是適當的放置條件。下面這個就沒有按正確的順序放。


+ Shingo Jass Highlighter 0.41
if GetUnitCount( 'hpea' ) <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
elseif GetUnitCount( 'hpea' ) <>
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
call setProduce( 1, 'hpea', 0)
else
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif

如果我們有一個農民,這段代碼會說我們不需要更多的農民。這是因為條件是從上而下依次檢查的。

循環
循環就是不斷的重複做某事。如果你在其他的編程中用過循環結構,在使用JASS的循環時你可能會感到不適應。不過有一種編寫在JASS中的while循環和for循環的方法。

讓我們來看一個將永遠執行下去的循環(或者執行到遊戲引擎關閉掉它。)


QUOTE:
loop
doSomethingForever
endloop


+ Shingo Jass Highlighter 0.41
loop
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user forever")
call Sleep( 1.0 ) // 不用休眠的話就無法永遠的執行
endloop

永遠執行的循環有它們自己的用處,但是他們並不很靈活。所以讓我們來看如何跳出循環。


QUOTE:
loop
doSomething
exitwhen condition
doSomethingMore
endloop


+ Shingo Jass Highlighter 0.41
local integer i = 1
loop
call Sleep( 1.0 )
exitwhen i > 10
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user 10 times")
set i = i + 1
endloop


for循環
我們在JASS中不能使用For循環,但是我們可以做一個像for循環的循環。


CODE:
for ( initStatement; condition; endOfIterationStatement ) {
stuff;
}
[Copy to clipboard]


在JASS中可以這樣做


QUOTE:
initStatement
loop
exitwhen not (condition)
stuff
endOfIterationStatement
endloop


現在讓我們來看看一個更加具體的例子。


CODE:
for (int i = 0; i <>
printf(\"We need to say it 10 times\");
}
[Copy to clipboard]


在JASS中變成


+ Shingo Jass Highlighter 0.41
local integer i = 0
loop
exitwhen not ( i <>= 10 時跳出
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need to say it 10 times")
set i = i + 1
endloop


while
或許你應該自己來研究如何在JASS中實現while循環。如果你不太習慣用while循環那麼就無視它們吧,反正JASS是沒有這種循環。

如果你想瞭解更多,去查詢TRIGGER和JASS的相關教程吧。

==============================================================
函數:目前為止我們所用到的

這部分列出了目前我們用到的所有的函數,看看它們是如何工作的,能幹什麼,以及為什麼我們只讓它們做什麼。希望你會喜歡這部分。
對於每一個我們用過的函數,我都會寫出來它們在資源文件中是如何聲明的,在引數中是怎麼解釋的,它能做什麼以及它返回什麼。


QUOTE:
native Sleep takes real seconds returns nothing
call Sleep( 3.5 ) // 讓腳本暫停3.5,秒
如果我們不讓腳本休眠,引擎會強制終止它


QUOTE:
constant native GetLocalPlayer takes nothing returns player
local Player p = GetLocalPlayer() //區別玩家的電腦
利用它可以用來指定顯示文本的對象。


QUOTE:
native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, \"Hello local player!\" )
我們需要告訴這個函數顯示給誰什麼內容。把那些0改成不同的內容我們就可以控制文字在屏幕上顯示的位置了。試驗的時候隨便寫吧,用0一般就足夠了。顯示的內容會在一小會後消失,這由文本的長度決定。


QUOTE:
native DisplayTimedTextToPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 60, \"Hello for 60 secs!\" )
同上,不過我們可以控制文本顯示的時間。


QUOTE:
native ClearHarvestAI takes nothing returns nothing
call ClearHarvestAI() // 命令採集AI停止採集
這個函數和下面的2個配合起來一起用可以用來控制用幾個農民采金子和幾個農民采木頭


QUOTE:
native HarvestGold takes integer town, integer peons returns nothing
call HarvestGold( 0, 5 ) // 讓主基地中的5個農民採集金子
如果這個函數被調用2次的時候,如果沒用上面的那個函數的話就會讓10個農民去採金子了。。。


QUOTE:
native HarvestWood takes integer town, integer peons returns nothing
call HarvestWood( 0, 10 )
同上,只是這個採集的是木頭


QUOTE:
native SetProduce takes integer qty, integer id, integer town returns boolean
local boolean success = SetProduce( 1, 'hpea', 0 ) // 在主基地訓練一個農民


QUOTE:
call SetProduce( 1, 'hpea', 0 )
我並不完全相信返回值,它讓我在很多時候產生疑惑


QUOTE:
native GetUnitCount takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) //清點AI玩家的農民數量
包含了正在訓練的農民


QUOTE:
native GetUnitCountDone takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // 清點AI玩家農民的數量
不包含正在訓練的農民

==============================================================
何時該建造何物

這部分我們將用我們自己的函數來進行建築,不過可能中間夾雜著不少的理論內容。所以我們必須考慮我們何時候該造何種建築或者訓練什麼單位。在我們只有兵營而沒有鐵匠鋪的時候顯然是不能訓練火槍手的。

讓AI忽視任何需求嘗試建造一個建築是最簡單的。如果我們把所有的建築順序寫在一個循環裡,它將會嘗試建造所有我們命令它建造的建築(如果我們沒有忘記科技樹的話)它在很多時候都用的不是很好,我們可能會建造許多同樣的建築,例如3個鐵匠鋪(真是瘋了。。。),那麼該如何解決呢?

如果我們回頭看一下第四部分,關於建造更多的農場來支持更多的農民,和第六部分,建造兵營並且訓練單位,我們不難發現,它們中用了一些if語句,用來決定該做什麼事的規則。試想,如果是我們正在玩遊戲,我們通常在腦中都會有一些,至少是一條建築的規則。所以,我們還等什麼呢?來試試吧!

規則 1: 當我們缺乏農民的時候應該訓練農民
規則 2: 當我們人口不足時應該建造農場
規則 3: 沒有兵營時應該造兵營
規則 4: 有兵營了就可以造步兵了
規則 5: 如果有兵營了就可以造一個鐵匠鋪了
規則 6: 如果我們沒有祭壇就造一個祭壇
規則 7: 如果我們有祭壇而沒有英雄就需要造一個英雄

讓我們看看這些規則,它們會讓我們建立一支由英雄帶隊的步兵和火槍手組成的部隊。想想如何在JASS中實現他們吧!(回顧一下第七節會很有幫助)

懶得自己試麼? =) 好吧,我帶你們一起搞吧。。。呵呵
讓我們看看如何將規則1應用到JASS中去,似乎用IF語句非常適合。


QUOTE:
if \"we have to few peasant\" then
\"train more peasants\"
endif


few具體是指多少?讓我們將規則1改為:

規則1B : 如果我們有少於10個農民那麼就開始訓練一個農民。

然後將它用JASS實現。


+ Shingo Jass Highlighter 0.41
if GetUnitCount('hpea') <>
call SetProduce( 1, 'hpea', 0 ) // 0是指我們最開始的基地
endif


好的~

那麼我們以此類推來改一下其他的規則吧~

規則 2b: 如果我們人口剩餘少於6個就造一個農場
規則 3b: 如果我們的兵營少於1個就建造一個兵營
規則 4b: 如果我們的兵營多於1個就訓練一個步兵
規則 5b: 如果我們的兵營多於1個而鐵匠鋪少於1個就早一個鐵匠鋪
規則 6b: 如果我們祭壇少於1個就早一個祭壇
規則 7b: 如果我們的祭壇多於0個而英雄少於1個就訓練一個英雄
規則 8b: 如果我們有少於一個的非JASS的規則那麼我們就將它改為一個符合JASS的規則 =)

==============================================================
建立和使用函數

函數...他們到底好在哪裡,為什麼我們要用他們呢?厄...如果有另外一個人寫了大量的不錯的函數來給我們調用,我們就不用自己寫了。如果我們想自己寫幾個函數,我們就可以防止我們的代碼變成一團亂麻了。分割!征服!。把你的代碼分割成很多函數,然後征服每個函數吧!

讓我們來看一個簡單的不能再簡單的函數。


+ Shingo Jass Highlighter 0.41
function myFunc takes nothing returns nothing
endfunction


這個函數什麼也不能做。但是它包含了每個函數都必須具有的部分。它以function關鍵字開始,以endfunction結束。在function後是函數名:這裡是MyFunc。下面的是takes以及函數使用的所有參數。這個函數不使用任何參數。它也不返回任何東西。它教給我們什麼了?一個函數名為muFunc的函數,沒有任何的實參和形參並且沒有任何的返回值。

那我們現在來做一個可以實現一些功能,使用一些參數並且有返回值的函數吧。


+ Shingo Jass Highlighter 0.41
function add takes integer a, integer b returns //a+b的和
return a + b
endfunction


他的功能很明顯,不是嗎?而且它一點也不實用。我們可以直接用+直接計算而不是用函數。讓我們來做個更加有用的函數,用來讓所有的農民採集資源。


+ Shingo Jass Highlighter 0.41
function resourceManager takes goldPeasants returns nothing
call ClearHarvestAI()
call HarvestGold( 0, goldPeasants )
call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
endfunctions


這個函數使用了決定多少個農民採集金子(其餘的都去採木頭)的參數。它在另外一個函數(例如main)中調用方法如下:


+ Shingo Jass Highlighter 0.41
call resourceManager( 5 )


這樣就可以讓5個農民去採金子而其他的去採木頭了。在前面的程序中使用這個函數我們在main中可以省去很多行的代碼。在第二部分的程序可寫為:


+ Shingo Jass Highlighter 0.41
function resourceManager takes goldPeasants returns nothing
call ClearHarvestAI()
call HarvestGold( 0, goldPeasants )
call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
endfunctions

function main takes nothing returns nothing
call Sleep( 1.0 )
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

loop
call resourceManager( 4 )
call Sleep( 1.0 )
endloop

call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction


要注意被調用的極限函數是要寫在調用它的函數前的。不然魔獸III的程序就會這樣想:這是什麼怪函數?從來沒聽說過嘛!我最好關閉這個腳本,它看上去不完整嘛。
另外一個有用的極限函數是像這樣的:


+ Shingo Jass Highlighter 0.41
function print takes string message returns nothing
call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, message )
endfunction


==============================================================
一個戰鬥腳本

這部分可能出現的過早了。讓我們一起重新編寫一下第六部分中的代碼。建造兵營並且訓練一些士兵。

為了增加代碼的可讀性,我把它分成了用來控制資源,建造建築和訓練單位這幾個不同的管理器中了


+ Shingo Jass Highlighter 0.41
function resourceManager takes nothing returns nothing
local integer goldPeasants = 5
local integer peasantsDone = GetUnitCountDone('hpea')
if peasantsDone <>
set goldPeasants = peasantsDone - 1
endif

call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In manager")
call ClearHarvestAI()
call HarvestGold( 0, goldPeasants ) // then tell it to have

call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )

endfunction

function unitManager takes nothing returns nothing
if GetUnitCount('hpea') <>
call SetProduce(1, 'hpea', -1) //在任何可行的地方訓練農民
endif
call SetProduce(1, 'hfoo', -1) // 在任何可行的地方訓練地面部隊
endfunction

function buildingManager takes nothing returns nothing
local integer produced = 12 + 6 * GetUnitCount('hhou')
local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
local integer foodSpace = produced - used
local integer barrack = GetUnitCount('hbar')

if foodSpace <>
call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"Building house")
call SetProduce(1,'hhou',-1)
endif

if barrack <>
call SetProduce(1,'hbar',-1)
endif
endfunction

function main takes nothing returns nothing
call Sleep( 1.0 )
call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started")

loop
call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "in loop")
call resourceManager()
call unitManager()
call buildingManager()
call Sleep( 1.0 )
endloop

call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting")
endfunction


這個代碼將會訓練許多的僅僅在原地站著不動的士兵。但是需要注意的是...讓我們來創建一個讓AI控制步兵前往地圖上的某個地方的腳本吧!希望他們能找到敵人...我們很快就會考慮構建一個如何利用作弊來解決控制問題的AI...-_-


+ Shingo Jass Highlighter 0.41
globals //聲明全局變量
unit scoutUnit = null
real scoutX = 0
real scoutY = 0
real scoutHealth
endglobals
//================================================== =======
// resourceManager資源管理器
//
// 控制農民採集資源
//================================================== =======
function resourceManager takes nothing returns nothing
local integer goldPeasants = 5
local integer peasantsDone = GetUnitCountDone('hpea')
if peasantsDone <>
set goldPeasants = peasantsDone - 1
endif


call ClearHarvestAI()
call HarvestGold( 0, goldPeasants )

call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )

endfunction
//================================================== =======
// unitManager單位管理器
//
// 訓練單位
//================================================== =======
function unitManager takes nothing returns nothing
if GetUnitCount('hpea') <>
call SetProduce(1, 'hpea', -1)
endif
if GetUnitCount('hfoo') <>
call SetProduce(1, 'hfoo', -1)
endif
endfunction
//================================================== =======
// buildingManager建築管理器
//
// 建造建築
//================================================== =======
function buildingManager takes nothing returns nothing
local integer produced = 12 + 6 * GetUnitCount('hhou')
local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
local integer foodSpace = produced - used
local integer barrack = GetUnitCount('hbar')

if foodSpace <>

call SetProduce(1,'hhou',-1)
endif

if barrack <>
call SetProduce(1,'hbar',-1)
endif
endfunction
//================================================== =======
// allocateScout指派一個偵察兵
//
// 選擇一個單位作為偵察兵,用來引導其他的部隊
//================================================== =======
function allocateScout takes nothing returns nothing
local group grp = CreateGroup()
call GroupEnumUnitsOfType(grp, "footman", null)
set scoutUnit = FirstOfGroup(grp)
call RemoveGuardPosition(scoutUnit)
set scoutX = GetUnitX(scoutUnit)
set scoutY = GetUnitY(scoutUnit)
set scoutHealth = GetUnitState(scoutUnit, UNIT_STATE_LIFE)
endfunction
//================================================== =======
// init初始化
//
// 初始化腳本
//================================================== =======
function init takes nothing returns nothing
endfunction
//================================================== =======
// walk移動
//
// 命令單位移動到前方某處
//================================================== =======
function walk takes unit u, real distance returns nothing
local real dir = GetUnitFacing(u) * 3.14 / 180
local real x = GetUnitX(u) + distance * Cos(dir)
local real y = GetUnitY(u) + distance * Sin(dir)
call IssuePointOrder( u, "move", x, y)
endfunction
//================================================== =======
// walk移動
//
// 命令單位移動到指定方向某處
//================================================== =======
function walkDir takes unit u, real distance, real direction returns nothing
local real dir = direction * 3.14 / 180
local real x = GetUnitX(u) + distance * Cos(dir)
local real y = GetUnitY(u) + distance * Sin(dir)
call IssuePointOrder( u, "move", x, y)
endfunction
//================================================== =======
// helpScout幫助偵察兵
//
// 命令單位跟隨偵察兵
//================================================== =======
function helpScout takes nothing returns nothing
local group grp = CreateGroup()
local unit u
call GroupEnumUnitsOfType(grp, "footman", null)
set u = FirstOfGroup(grp)
loop
exitwhen u == null
if u != scoutUnit then
call IssuePointOrder( u, "attack", scoutX, scoutY)
endif
call GroupRemoveUnit(grp, u)
set u = FirstOfGroup(grp)
endloop
call DestroyGroup(grp)
endfunction
//================================================== =======
// scout偵察兵
//
// 控制偵察兵前往地圖上的一個隨機點
//================================================== =======
function scout takes nothing returns nothing
local real R = 1024
local real x = GetUnitX(scoutUnit)
local real y = GetUnitY(scoutUnit)

if scoutX == x and scoutY == y then
call walkDir(scoutUnit, R, GetUnitFacing(scoutUnit) + GetRandomReal(90, 270))
else
set scoutX = x
set scoutY = y
call walk(scoutUnit, R)
endif
endfunction
//================================================== =======
// combatManager戰鬥管理器
//
// 控制軍隊跟隨偵察兵
//================================================== =======
function combatManager takes nothing returns nothing
// call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In combatManager")
if scoutUnit == null or not UnitAlive(scoutUnit) then
call allocateScout()
endif
if GetUnitCountDone('hfoo') > 4 and scoutUnit != null then
call scout()
call helpScout()
endif
endfunction
//================================================== =======
// main主函數
//
// 運行AI的主函數
//================================================== =======
function main takes nothing returns nothing
call Sleep( 1.0 )
call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started")
call init()

loop

call resourceManager()
call unitManager()
call buildingManager()
call combatManager()
call Sleep( 5.0 )
endloop

call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting")
endfunction


沒有留言:

張貼留言

※怎麼下載?Xuite硬碟MediaFireBadongoSendSpace
※文章可以轉貼嗎?可以,不過要註明出處、標示本站連結。
※載點可以轉貼嗎?不可以,不能盜連。
※建議瀏覽器:Chrome 或 Firefox 或 Opera 或 IE7以上
!請按+1來支持本站!