進階概念

延遲

電腦執行程式碼是不需要時間的,以下的實驗可以證實這個想法:

Delay Test
 Events
  Player - Player 1 (Red) types a chat message containing -test as An exact match
 Conditions
 Actions
  Countdown Timer - Start TestTimer as a One-shot timer that will expire in 30.00 seconds
  Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
  Game - Display to (All players) the text: (String((Elapsed time for (Last started timer))))
  Animation - Play (Last created unit)'s stand animation
  Game - Display to (All players) the text: (String((Elapsed time for (Last started timer))))
  Cinematic - Ping minimap for (All players) at (Center of (Playable map area)) for 1.00 seconds
  Game - Display to (All players) the text: (String((Elapsed time for (Last started timer))))
  Player - Add 1000 to Player 1 (Red) Current gold
  Game - Display to (All players) the text: (String((Elapsed time for (Last started timer))))

輸入"-test"之後, 我們會發現它印出4個"0"。可見 二句敍述之間沒有任何的時間延遲。(實際上當然還是有一小小小段的執行時間,不過電腦整個處理器的運作方式會被設計成好像不用時間,以便簡化程式的運作,並減少混亂的產生。所以大家就當作是不用時間吧。)

不過還是有破壞這個原則的東西,W3的GUI Trigger提供了4個延遲函數--Wait(TriggerSleepAction)、Wait game-time(PolledWait)、Wait for Condition、Wait for Sound(WaitForSoundBJ)。其中Wait是較基本的函數,實際運作原理不詳(去問b社吧-_-")。

Wait會等待一段秒數,但是那個秒數不是遊戲秒,而是和真實時間的「秒」一致。在最快的遊戲速度下,真實的秒比遊戲秒還要慢一些,而遊戲的速度如果調慢,真實秒則可能會變得比遊戲秒快。為了解決這個問題,b社用區域變數和計時器,創造了Wait game-time。它可以相當精確地等待一段遊戲秒,但同時也會對系統資源造成較大的負擔。筆者建議,Wait game-time當然好用,不過只要能夠用Wait寫,還是用它比較省資源。最後兩個函數Wait for Condition和Wait for Sound恰如其義,就是等待直到一個條件符合和等待直到一段音效結束。

4個時間延遲函數都是有延遲的,最多可多延遲0.2xx遊戲秒左右。即使我寫Wait 0.0 seconds,電腦還是會等待0.0x~0.2x遊戲秒左右(但Wait 0.0 game-time seconds不會做任何等待)。如果我要等待的時間很短,例如0.3秒以下,那麼不建議用Wait,因為0.2x秒的誤差在這種情況下是很嚴重的!

觸發間的互動

最基本的觸發是一發生什麼事情就做什麼,跟以前發生過什麼事情無關,所以如果你的觸發彼此間是獨立無關聯的,那就不需要理會這個問題。 但有些觸發必須要某些條件成立才能正常作用,而條件的完成又得依靠其他觸發的執行結果,此時就要注意這個問題。

簡單的方法是在你的觸發中加入條件來檢查,這用在觸發結構簡單的地圖很不錯,但每個觸發的執行結果不盡相同,要加入的條件種類就五花八門了,甚至有些很難用條件來控制,所以下面介紹幾個指令。

在一個觸發中加入一個行動,然後你可以在「Trigger(觸發)」類中找到以下幾個指令幫助你控制觸發的執行:
*Trigger - Turn on
*Trigger - Turn off
*Trigger - Run (checking conditions)
*Trigger - Run (ignoring conditions)

前兩個一看就知道作用是相對的,這兩個指令是控制觸發的開啟和關閉。

當你正在編輯某個觸發時,可以在觸發編輯器按鈕列的下方找到「啟用(Enabled)」和「初始值開啟(Initially On)」這兩個核取方塊。前者是設定此觸發是否准許被執行,後者決定在遊戲剛開始時是否開啟這個觸發,和第一個選項不同的是可以在遊戲中使用觸發來切換開關,而第一個選項選了後在遊戲中無法更改。

一定有人會問:「如果把一個觸發停用,它就永遠不會執行。那我寫那個觸發做有什麼意義?」。基本上這個功能是為了測試方便,有時你可能為了測試而必須暫時讓一個觸發不作用,如果你刪了它,後來要用的時候就得重寫;如果你只是把它停用,日後要用的時候,只要再啟用它即可。

被設定成關閉的觸發在遊戲中不會執行,所以你可以把一些觸發先設為關閉,然後在其他觸發中把他開啟,之後這個觸發就會作用。同樣的,你也可以把已經開啟的觸發關閉,端看你的需要。

後兩個是執行觸發,不經由事件啟動。第三個是會檢查條件,若不符合就不會執行。第四個則是忽略條件,強制執行觸發。

前兩個指令是會保持觸發的開關,後兩個則是只執行一次,要如何使用就全看你的需求了。

執行順序

假設我隨便寫一段觸發:

Set x = 100
Player - Set Player 1 (Red) Current gold to x 

如果有別的觸發也用到x,有沒有可能在Set x = 100之後到Set Player 1 (Red) Current gold to x之前的那段時間內,改變x的值,使得執行Set Player 1 (Red) Current gold to x時,x不再是100?

基本上是不可能的,因為Set x = 100到Set Player 1 (Red) Current gold to x之間的時間是0,所以別的觸發無法介入。

然而,如果我寫的是:

Set x = 100
Wait 5.00 game-time seconds
Player - Set Player 1 (Red) Current gold to x

那麼等待後的x就未必還是100囉。

不過如果我的動作引發別的觸發,那就另當別論了。如下例,想想看電腦的執行順序,以及執行結果?(反白檢視答案)

Trigger 1
 Events
  Time - Elapsed game time is 5.00 seconds ----(1)
 Conditions
 Actions
  Player - Set Player 1 (Red) Current gold to 0 ----(2)
  Set x = 100 ----(3)
  Player - Set Player 1 (Red) Current gold to x ----(4)
  Player - Set Player 2 (Blue) Current gold to x ----(7)

Trigger 2
 Events
  Player - Player 1 (Red)'s Current gold becomes Greater than or equal to 100.00 ----(5)
 Conditions
 Actions
  Set x = 300 ----(6)

-->所以最後玩者1黃金100,玩者2黃金300

Trigger - Run

這個函數允許一個觸發呼叫另外一個觸發,它又分成Checking Conditions(檢查條件)和Ignoring Conditions(忽略條件)兩種,前者會檢查被呼叫的觸發的條件,全部符合才執行它;後者則否。這是一個很好用的觸發,其中最常見的功能是可以把類似的動作放在一條觸發裡,然後用其它觸發呼叫它,如此便能減少版面,而且如果日後要做調整,只要調整一遍就夠了。例如:

Create Hero
 Events
  Time - Elapsed game time is 10.00 seconds
 Conditions
 Actions
  Unit - Create 1 Archmage for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (ignoring conditions)
  Unit - Create 1 Paladin for Player 2 (Blue) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (ignoring conditions)
  Unit - Create 1 Mountain King for Player 3 (Teal) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (ignoring conditions)
  Unit - Create 1 Blood Mage for Player 4 (Purple) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (ignoring conditions)

Create Hero Sub
 Events
 Conditions
 Actions
  Unit - Set life of (Last created unit) to 50.00%
  Unit - Set mana of (Last created unit) to 50.00%
  Unit - Add Blink to (Last created unit)
  Camera - Pan camera for (Owner of (Last created unit)) to (Center of (Playable map area)) over 1.00 seconds
  Cinematic - Ping minimap for (Player group((Owner of (Last created unit)))) at (Center of (Playable map area)) for 1.00 seconds

像這樣就能用Trigger - Run Create Hero Sub <gen> (ignoring conditions)取代原來的4列敍述,而且日後要修改的話,只要修改Create Hero Sub中的內容,而不用在Create Hero那個觸發改好幾遍。

checking conditions也類似:

Create Hero
 Events
  Time - Elapsed game time is 10.00 seconds
 Conditions
 Actions
  Unit - Create 1 Archmage for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (checking conditions)
  Unit - Create 1 Paladin for Player 2 (Blue) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (checking conditions)
  Unit - Create 1 Mountain King for Player 3 (Teal) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (checking conditions)
  Unit - Create 1 Blood Mage for Player 4 (Purple) at (Center of (Playable map area)) facing Default building facing degrees
  Trigger - Run Create Hero Sub <gen> (checking conditions)

Create Hero Sub
 Events
 Conditions
  (Percentage life of (Last created unit)) Greater than 50.00
 Actions
  Unit - Set life of (Last created unit) to 50.00%
  Unit - Set mana of (Last created unit) to 50.00%
  Unit - Add Blink to (Last created unit)
  Camera - Pan camera for (Owner of (Last created unit)) to (Center of (Playable map area)) over 1.00 seconds
  Cinematic - Ping minimap for (Player group((Owner of (Last created unit)))) at (Center of (Playable map area)) for 1.00 seconds

由於checking conditions的函數比ignoring conditions函數的程式碼多(即使被呼叫的觸發根本沒有設條件),所以沒有必要的話(觸發沒有條件),盡量少用checking conditions,畢竟對沒有條件的觸發再做一次檢查是浪費資源的行為。

當然這也會有一些影響執行順序之處,不過不會太複雜。想想看,以下範例電腦的執行順序為?(反白檢視答案)

Test 1
 Events
  Unit - A unit Dies ----(1)
 Conditions
 Actions
  Set target = (Killing unit) ----(2)
  Trigger - Run Test 2 <gen> (ignoring conditions) ----(3)
  Set target = (Random unit from (Units within 512.00 of (Position of (Killing unit)))) ----(7)
  Trigger - Run Test 2 <gen> (ignoring conditions) ----(8)

Test 2
 Events
 Conditions
 Actions
  Animation - Play target's death animation ----(4) ----(9)
  Unit - Set mana of target to 0.00 ----(5) ----(10)
  Unit - Remove Positive buffs from target ----(6) ----(11)

如果是這樣呢?(反白檢視執行順序)

Test 1
 Events
  Unit - A unit Dies ----(1)
 Conditions
 Actions
  Set target = (Killing unit) ----(2)
  Trigger - Run Test 2 <gen> (ignoring conditions) ----(3)
  Set target = (Random unit from (Units within 512.00 of (Position of (Killing unit)))) ----(6)
  Trigger - Run Test 2 <gen> (ignoring conditions) ----(7)

Test 2
 Events
 Conditions
 Actions
  Animation - Play target's death animation ----(4) ----(8)
  Wait 2.00 game-time seconds ----(5) ----(9)
  Unit - Set mana of target to 0.00 ----(10.a) ----(10.b) (兩者先後不定,看哪個Wait延遲較少)

-->如果沒別的觸發干擾,執行到10.a和10.b的時候,target值都是後來指定的那個 部隊,所以最後只有一個部隊的法力會歸零。

呼叫觸發,可以產生另外一個獨立的執行體系,如上例可讓兩個Wait同步等待,而如果是這樣,就會先等完一個再等一個:

Test 1
 Events
  Unit - A unit Dies ----(1)
 Conditions
 Actions
  Set target = (Killing unit) ----(2)
  Trigger - Run Test 2 <gen> (ignoring conditions) ----(3)
  Animation - Play target's death animation ----(4)
  Wait 2.00 game-time seconds ----(5)
  Unit - Set mana of target to 0.00 ----(6)
  Set target = (Random unit from (Units within 512.00 of (Position of (Killing unit)))) ----(7)
  Animation - Play target's death animation ----(8)
  Wait 2.00 game-time seconds ----(9)
  Unit - Set mana of target to 0.00 ----(10)

雖然前者的同步寫法有造成變數錯亂的問題,但是在JASS技術中,可以用區域變數解決此問題(請參見JASS教學)。因此開新觸發成了很好的同步設定工具。

Trigger - Add New Event

這個函數也是很好用的東西,它允許我在遊戲中把新的事件加入到觸發中,而且可以選擇變數,例如:

Init
 Events
  Map initialization
 Conditions
 Actions
  Unit Group - Pick every unit in (Units owned by Player 1 (Red) of type Siege Engine) and do (Actions)
   Loop - Actions
    Trigger - Add to Kill <gen> the event (Unit - A unit comes within 128.00 of (Picked unit))

Kill
 Events
 Conditions
 Actions
  Unit - Explode (Triggering unit)

不知道讀者是否能夠理解。這是一個類似電流急急棒的觸發,[Init]觸發會在一開始執行,並且把玩者一的所有坦克加入到[Kill]觸發的事件裡。所以,任何 部隊碰到前面設定過的玩者一的坦克就會炸開(當然,玩者一在遊戲中如果再生坦克,由於它沒有被加到觸發裡,部隊碰到它就不會爆炸了)。

中斷For迴圈

如果For迴圈裡面有Skip Remaining Actions,由於迴圈還是在原來的Action下面,因此會跳出整個Action: 中斷For迴圈

中斷Pick迴圈

如果Pick裡面有Skip Remaining Actions,由於Pick所執行的是另外一個獨立的函式,因此它只跳出Pick-Action函式: 中斷Pick迴圈

For迴圈+延遲

由於迴圈索引Integer A(bj_forLoopAIndex;另外還有一個bj_forLoopAIndexEnd) 、Integer B是全域變數,很容易彼此相互干擾,造成錯誤。相信很多人寫過類似以下例子的東西:

Loop Delay
 Events
  Player - Player 1 (Red) types a chat message containing -test as An exact match
 Conditions
 Actions
  For each (Integer A) from 1 to 10, do (Actions)
   Loop - Actions
    Wait 1.00 seconds
    Game - Display to (All players) the text: (String((Integer A)))

如果Player1在2秒和5.1秒時分別輸入一次-test,大部分的人會認為電腦這樣印:  

時間(S) 2 3.x 4.x 5.x 6.x 6.y 7.x 7.y 8.x 8.y 9.x 9.y 10.x 10.y 11.x 11.y 12.x 12.y
文字 1 2 3 4 1 5 2 6 3 7 4 8 5 9 6 10 7

很可惜的,結果不是這樣,實際的執行狀況如下:

時間(S) 2 3.x 4.x 5.x 6.x 6.y 7.x 7.y 8.x 8.y 9.x 9.y 10.x 10.y 11.x
文字 1 2 3 1 2 3 4 5 6 7 8 9 10 11

詳細執行過程如下:

時間(S) 第一次輸入 第二次輸入 變數
0  bj_forLoopAIndex = 0
bj_forLoopAIndexEnd = 0
2 輸入-test
設定bj_forLoopAIndex為1
設定bj_forLoopAIndexEnd為10
進入迴圈
等待一秒
bj_forLoopAIndex = 1
bj_forLoopAIndexEnd = 0
3.x 顯示出"1"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 2
bj_forLoopAIndexEnd = 0
4.x 顯示出"2"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 3
bj_forLoopAIndexEnd = 0
5.x 顯示出"3"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 4
bj_forLoopAIndexEnd = 10
5.1   輸入-test
設定bj_forLoopAIndex為1
設定bj_forLoopAIndexEnd為10

進入迴圈
等待一秒
bj_forLoopAIndex = 1
bj_forLoopAIndexEnd = 10
6.x 顯示出"1"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 2
bj_forLoopAIndexEnd = 10
6..y   顯示出"2"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 3
bj_forLoopAIndexEnd = 10
7.x 顯示出"3"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 4
bj_forLoopAIndexEnd = 10
7.y   顯示出"4"
定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 5
bj_forLoopAIndexEnd = 10
8.x 顯示出"5"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 6
bj_forLoopAIndexEnd = 10
8.y   顯示出"6"
設定bj_forLoopAIndex加1 等待一秒
bj_forLoopAIndex = 7
bj_forLoopAIndexEnd = 10
9.x 顯示出"7"
設定bj_forLoopAIndex加1 等待一秒
bj_forLoopAIndex = 8
bj_forLoopAIndexEnd = 10
9.y 顯示出"8"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 9
bj_forLoopAIndexEnd = 10
10.x 顯示出"9"
設定bj_forLoopAIndex加1
等待一秒
bj_forLoopAIndex = 10
bj_forLoopAIndexEnd = 10
10.y 顯示出"10"
設定bj_forLoopAIndex加1
由於bj_forLoopAIndex比bj_forLoopAIndexEnd大,跳出迴圈
結束事件
bj_forLoopAIndex = 11
bj_forLoopAIndexEnd = 10
11.x 顯示出"11"
設定bj_forLoopAIndex加1
由於bj_forLoopAIndex比bj_forLoopAIndexEnd大,跳出迴圈
結束事件
bj_forLoopAIndex = 12
bj_forLoopAIndexEnd = 10

可以發現在第二個迴圈就開始發生共用變數的錯亂情形了。 那麼有沒有解決方法呢?唯一的解決方法就是使用區域變數做為迴圈的索引,這部分請參見JASS入門教學

Pick迴圈+延遲

Pick迴圈下的Wait都沒有作用,如下例:( 反白檢視執行順序)

Pick Delay Test
 Events
  Time - Elapsed game time is 5.00 seconds ----(1)
 Conditions
 Actions
  Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions) ----(2)
   Loop - Actions (假設選到3個部隊)
    Unit - Set life of (Picked unit) to 100.00% ----(3) ----(5) ----(7)
    Wait 10.00 game-time seconds ----(4) ----(6) ----(8)
    Unit - Set mana of (Picked unit) to 0.00

大家一定認為選取的部隊會先生命值全滿,然後10秒鐘之後法力歸零對吧?不過實際結果是:只有生命值全滿,後面的動作全部停掉了。

如果真的想在Pick之下順利執行等待指令,其中最簡便的解決辦法就是放到另外一個子觸發,如下:( 反白檢視執行順序)

Pick Delay Test
 Events
  Time - Elapsed game time is 5.00 seconds ----(1)
 Conditions
 Actions
  Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions) ----(2)
   Loop - Actions (假設選到2個部隊)
    Trigger - Run Pick Delay Sub <gen> (ignoring conditions) ----(3) ----(6)

Pick Delay Sub
 Events
 Conditions
 Actions
  Unit - Set life of (Picked unit) to 100.00% ----(4) ----(7)
  Wait 10.00 game-time seconds ----(5) ----(8)
  Unit - Set mana of (Picked unit) to 0.00 ----(9) ----(10)

-->結果會在執行後所有部隊的生命值全滿,過10.x秒鐘後均法力歸零(不是一個10.x秒,一個20.x秒喔)。

綜合教學/觸發器使用教學/4.進階概念.txt · 上一次變更: 2007年11月12日 9:28 pm 來自 wasabi
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0