LV. 42
GP 1k

 ~輕鬆學JASS~ (實作篇)

樓主 Weber Weberkkk

===============

  ---(實作篇)---

===============


《7》引數與回傳

引數與回傳就跟數學的方程式一樣,像這條:
function Test1 takes integer A,integer B,integer C returns integer

  return A+B+C
endfunction

我如果打 set A = Test1(1,2,3)就會獲得6的回傳值

第二條:
function Test2 takes unit UnitA,unit UnitB,unit UnitC returns group
local group G = CreateGroup()
  call GroupAddUnit(G,UnitA)
  call GroupAddUnit(G,UnitB)
  call GroupAddUnit(G,UnitC)
  return G
endfunction

我如果打 set G = Test2(U1,U2,U3)就會獲得一個包含U1 U2 U3 三個部隊的群組

第三條:
function Test3 takes unit UnitA,unit UnitB,unit UnitC returns nothing
  call RemoveUnit(UnitA)
  call RemoveUnit(UnitB)
  call RemoveUnit(UnitC)
endfunction

我如果打 call Test3(U1,null,U3)會發生甚麼事呢?
這時候要擺脫數學的概念了,因為我們寫的是程式碼而不是數學
U1與U3經過Test3函數的處理之後,被我們刪除了
中間傳入一個null值,所以 call RemoveUnit(UnitB)這段不會發生任何事情

那如果是這樣呢?

function Test4A takes integer A,integer B returns integer

  return A+(B*2)
endfunction

function Test4B takes integer A,integer B returns integer

  loop
    set B = B-1
    exitwhen B == 0
    set A = Test4A(A,B)
  endloop

  return A
endfunction

我們打 set A = Test4B(3,3)會發生甚麼事?
我們會在Test4B獲得Test4A回傳的數值
第一次迴圈獲得A=7,然後獲得A=9,第三次則跳出迴圈

Test4B回傳的9是我們最後獲得的結果

所以再來一題吧:

function Test5A takes nothing returns nothing

    call RemoveUnit(GetEnumUnit())
endfunction

function Test5B takes group G returns nothing

    call ForGroup(G,function Test5A)
endfunction

ForGroup會挑取群組裡的每一個單位各自執行動作
是我們在GUI常常會用到的一個功能

而現在他們將執行的動作就是function Test5A
GetEnumUnit()也就是GUI常看到的(挑取的部隊)
所以當我們輸入call Test5B(G)的時候,這個群組會怎麼樣呢?
答案是,裡面的所有部隊將被刪除!
 

再舉個例子,這是阿扁害人TD新增科技的觸發

只要取得玩家、科技類型,就可以幫玩家新增相對應的科技
若不使用引數功能,要製作數十條相同的觸發才有辦法達到目的

tekes <玩家>要新增科技的玩家、<整數>科技的ID編號
returns <無>

function TechNLG takes player P ,integer T returns nothing

  if ( GetPlayerTechCountSimple( T, P) == 0 ) then

    call KillUnit( GetTriggerUnit() )
    call SetPlayerTechResearchedSwap( T, 1, P )
  else
    call DisplayTextToForce( GetForceOfPlayer(P), "You have already this technologie !!!" )
  endif
endfunction

這是呼叫用的function
只要更改每個點的科技類型引數,就可以輕鬆的完成整個新增科技的系統

function Fire takes nothing returns boolean
  call TechNLG(GetOwningPlayer(GetTriggerUnit()) ,'R001')
  return false
endfunction


不過這系統的做法實在點爛,連續抓了GetTriggerUnit()兩次
如何讓這系統變得更有效率呢? 留給大家思考看看!


最後順便一提,做為code的function不可導入引數,否則將導致錯誤
常見的code如Action、Condition、Filter、ForGroup、TimerStart

必須使用全域變數或return bug做為資料的傳遞媒介

 

《8》BJ函數研究

  blizzard.j函數可以將GUI指令轉為common.j指令,裡面包括了一些化繁為簡的功能,讓初學者可以更輕鬆的使用GUI調整參數,但裡面也有許多不必要的多層呼叫,所以在JASS裡面盡量直接使用common.j指令撰寫,會讓程式碼更有效率,完整的blizzard.j與common.j指令在NewGen(VJ)或JASS Craft都可以查詢,這邊只列出幾個比較實用且容易轉換的函數。


直接丟CJ系列

function DestroyEffectBJ takes effect whichEffect returns nothing
  call DestroyEffect(whichEffect)
endfunction

function DestroyLightningBJ takes lightning whichBolt returns boolean
  return DestroyLightning(whichBolt)
endfunction

function DestroyTimerBJ takes timer whichTimer returns nothing
  call DestroyTimer(whichTimer)
endfunction

function IssueTargetOrderBJ takes unit whichUnit, string order, widget targetWidget returns boolean
  return IssueTargetOrder( whichUnit, order, targetWidget )
endfunction


--(其他略,實在太多了...)--


看到上面這串,如果你了解前幾篇所講的「引數」
你會發現這些BJ函數是多麼的累贅

它們對引數完全沒有做任何的處理
只是把引數再丟給common.j而已

下次使用這些函數的時候,直接把BJ刪掉,跳過無用的BJ函數吧!

  call DestroyEffectBJ(whichEffect)

  ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 

  call DestroyEffect(whichEffect)


調換位置系列

function SetUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit, integer level returns integer
  return SetUnitAbilityLevel(whichUnit, abilcode, level)
endfunction

function GetUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit returns integer
  return GetUnitAbilityLevel(whichUnit, abilcode)
endfunction


--(其他略,實在太多了...)--


看完有種 Orz 的感覺

不就是把引數抓來換位置,再丟給CJ處理嗎?
一樣是毫無意義的函數,下次看到就直接調換引數位置,呼叫CJ吧!


隱藏功能系列

function TriggerRegisterEnterRectSimple takes trigger trig, rect r returns event
  local region rectRegion = CreateRegion()
  call RegionAddRect(rectRegion, r)
  return TriggerRegisterEnterRegion(trig, rectRegion, null)
endfunction

function TriggerRegisterLeaveRectSimple takes trigger trig, rect r returns event
  local region rectRegion = CreateRegion()
  call RegionAddRect(rectRegion, r)
  return TriggerRegisterLeaveRegion(trig, rectRegion, null)
endfunction

function TriggerRegisterPlayerUnitEventSimple takes trigger trig, player whichPlayer, playerunitevent whichEvent returns event
  return TriggerRegisterPlayerUnitEvent(trig, whichPlayer, whichEvent, null)
endfunction

--(其他略,實在太多了...)--

其實這些事件都可以加上Filter做篩選動作

因為GUI由Condition負責篩選
所以Blizzard把Event的Filter功能全部隱藏起來,不讓GUI新手們使用

到了JASS因為把Action移到Condition
所以Condition自然要移到Filter裡面

在某些情況下你也可以直接把Action移到Filter
不過必須注意的是Filter只能抓GetFilterUnit()跟少量的資訊
使用上也有諸多限制,只能執行篩選的動作,無法完全取代Condition

 《9》BJ函數研究之二

以下是傳說中的ForGroupBJ函數

function ForGroupBJ takes group whichGroup, code callback returns nothing
  local boolean wantDestroy = bj_wantDestroyGroup
  set bj_wantDestroyGroup = false

  call ForGroup(whichGroup, callback)

  if (wantDestroy) then
    call DestroyGroup(whichGroup)
  endif
endfunction

ForGroupBJ與ForGroup只差在要不要依據bj_wantDestroyGroup把做為引數的群組刪除
但必須注意的是,它套用的布林bj_wantDestroyGroup是個bj定義的全域變數
還有很多函數會使用到它

也因此,使用bj_wantDestroyGroup可能造成許多問題
你可以這樣寫:

function Start takes nothing returns nothing
local group G = GetUnitsOfPlayerAndTypeId(Player(12), 'nkot')

  call ForGroupBJ(G ,function RefereeIndex)
  call DestroyGroup(G)
endfunction


不過,因為不需要使用ForGroupBJ刪除引數的功能
所以乾脆這樣寫,直接下達common.j指令再把群組刪除

function Start takes nothing returns nothing
local group G = GetUnitsOfPlayerAndTypeId(Player(12), 'nkot')

  call ForGroup(G ,function ABC)
  call DestroyGroup(G)
endfunction


以下是創造N個單位於某地點的BJ函數

function CreateNUnitsAtLoc takes integer count, integer unitId, player whichPlayer, location loc, real face returns group
  call GroupClear(bj_lastCreatedGroup)
  loop
    set count = count - 1
    exitwhen count < 0
    call CreateUnitAtLocSaveLast(whichPlayer, unitId, loc, face)
    call GroupAddUnit(bj_lastCreatedGroup, bj_lastCreatedUnit)
  endloop
  return bj_lastCreatedGroup
endfunction


可以發現,它是以迴圈的方式不斷的重複創造部隊
然後再把創造好的部隊加入bj_lastCreatedGroup(最後創造的群組)

而繼續深入分析會發現

function CreateUnitAtLocSaveLast takes player id, integer unitid, location loc, real face returns unit
  if (unitid == 'ugol') then
    set bj_lastCreatedUnit = CreateBlightedGoldmine(id, GetLocationX(loc), GetLocationY(loc), face)
  else
    set bj_lastCreatedUnit = CreateUnitAtLoc(id, unitid, loc, face)
  endif

  return bj_lastCreatedUnit
endfunction

這函數會先判定要創造的單位是不是鬧鬼金礦(unitid == 'ugol')
然後再不斷的把單位紀錄成bj_lastCreatedUnit(最後創造的單位)

BJ的運作方式實在讓人傻眼,一大堆不必要的操作,為的是甚麼呢?
當然是為了方便新手使用(最後創造的單位)與(最後創造的群組)這些變數

但是,當我們不需要使用這些變數的時候
BJ的功能就成了拖慢執行效率的罪魁禍首

所以,該怎麼辦呢?

當我們要創造N個單位,並命令單位移動至某地點的時候
我們可以這樣寫


function WB_CreateNMove takes integer count ,player Pl ,integer ID, location locA ,location locB returns nothing
  loop
    set count = count - 1
    exitwhen count < 0
    call IssuePointOrderLoc(CreateUnitAtLoc(Pl,ID,locA,270), "move", locB )
  endloop
endfunction

或是這樣寫

function WB_CreateNOrder takes integer count ,player Pl ,integer ID, location locA ,location locB,string order returns nothing
  loop
    set count = count - 1
    exitwhen count < 0
    call IssuePointOrderLoc(CreateUnitAtLoc(Pl,ID,locA,270), order, locB )
  endloop
endfunction


再來看看BJ挑取範圍內部隊的函數

function GetUnitsInRangeOfLocAll takes real radius, location whichLocation returns group
  return GetUnitsInRangeOfLocMatching(radius, whichLocation, null)
endfunction


很顯然這又是一個毫無用途的函數
它呼叫了挑取範圍內部隊包含比對條件的函數,並且把條件設定為null


function GetUnitsInRangeOfLocMatching takes real radius, location whichLocation, boolexpr filter returns group
  local group g = CreateGroup()
  call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
  call DestroyBoolExpr(filter)
  return g
endfunction


其實負責挑取部隊的只有GroupEnumUnitsInRangeOfLoc這個CJ函數
只要給它群組、地點、範圍、Filter,它就會把符合條件的部隊加入群組

這時候可以來談談一開始提到的問題,為什麼GUI的挑取部隊會沒有效率呢?
當你要挑取範圍內的敵方部隊時,會創造一個filter的function在JASS腳本內

每次挑取部隊都會拿這個filter的function創造一個filter
然後再創造一個群組g,並且把群組與filter傳給GroupEnumUnitsInRangeOfLoc運作

這個BJ函數設計的還不錯,在使用完畢之後,會自動幫你把filter刪除
所以只要有做好基本的記憶體漏失處理,並不影響效能


不過,假如要用同一個條件挑取多次部隊呢?

這時候會變成以下情況:

創造Filter與群組 → 刪除 → 創造Filter與群組 → 刪除 → ....(迴圈)


我們可以這樣寫:

function WE_UnitInRange takes group g ,real radius, location whichLocation, boolexpr filter returns group
  call GroupClear(g)
  call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
  return g
endfunction

重複利用群組g與filter,避免創了又刪,刪了又創的狀況
在製做支援多重施展的挑取動作時,可以大幅提升挑取效率

BJ函數有著相當高的泛用性與相容性
卻犧牲了執行效率


純JASS的優勢就在於,可以創造自己需要的函數來運用
下面我們會看到更多例子
 


板務人員:

3963 筆精華,10/04 更新
一個月內新增 0
歡迎加入共同維護。


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】