JASS理论
一、初識jass
1、概覽
關于jass,只要知道2個內容即可:變量和函數。
╔═══╦═════════════════╗
║ ?變量 ?║存儲數據,重點熟悉和掌握全局和局域變量 ? ? ║
╠═══╬═════════════════╣
║ ?函數 ?║執行功能,了解函數的結構、生成和運行等 ? ? ║
╚═══╩═════════════════╝
詳細聲明變量和函數的格式如下:
(1)
a、全局變量的聲明:
globals
? ? 變量類型 變量名 ( = 初始值 ) //括號表示不賦值亦可,下同
? ? 變量類型 array 數組名 //數組不可直接賦值,需在函數內逐個賦值
endglobals
b、函數和局域變量的聲明:
function 函數名 takes 參數列表 returns 返回值類型
? ? local 變量類型 變量名 ( = 初始值 )
? ? ......
? ? 執行語句
? ? ......
? ? return 返回值
endfunction
?
(2)、全局與局域變量的區別:
全局任何函數皆可用,局域只作用于所聲明的函數。不同函數的局域變量命名可相同,最好不要與全局和
參數名相同。全局占用內存大,執行速率快,全局則反之。觸發中,屬性變量諸如觸發單位、循環整數A、
最后創建的單位、新建的單位組等均是全局變量。同樣,變量編輯器(ctrl+B)中的變量亦是全局變量。
(3)、函數起步,掌握以下重點即可:
①、函數名唯一;
②、函數可以無參數和返回值,是為空函數;
③、參數列表為參數類型1 參數名1,參數類型2 參數名2,...,參數類型N 參數名N。逗號分隔即可;
④、返回值必唯一;
⑤、局域變量聲明必在所有執行語句之前;
⑥、return必須寫在函數末端,即endfunction上一行;
⑦、無返回值函數則無需寫return;
⑧、注意returns和return及其后攜帶數據的區別,前者后接變量類型,后者接具體的數據;
⑨、數組不能做參數和返回值。
?
例如
globals
? ? integer int = 3 //聲明一個整數型全局變量int并賦值為3
? ? real col //聲明一個實數型全局變量col未賦值
? ? string array str //聲明一個字符串數組str
? ? boolean array b = false //錯誤!數組不能直接賦值!
endglobals
local integer index = 19 //錯誤!局域變量聲明怎能脫離函數?
function A takes nothing returns nothing
? ? local integer a = 1 //聲明一個整數型局域變量a并賦值為1,下同
? ? local integer b = 2
? ? local integer c = a + b
? ? call BJDebugMsg(I2S(c)) //顯示c的值,該句(帶call)即為執行語句
endfunction
function B takes real r, integer e returns nothing?
? ? ?//接受一個實數型參數r和一個整數型參數e
? ? call BJDebugMsg(R2S(r)+I2S(e)) //顯示r的值
endfunction
function C takes nothing returns real?
//返回一個實數型變量
? ? return 2 * 3 * 15 //正確,必須擁有返回值
endfunction
function D takes nothing returns real, integer
//該函數錯誤!一個函數只能返回一個數據類型!
? ? return ...
endfunction
function E takes real r returns real
//該函數錯誤!有返回值類型,但函數結尾卻無具體返回值!
endfunction
function F takes real r returns real
//正確,有返回值類型也有具體返回值
? ? return r * 33?
endfunction
function G takes nothing returns nothing
? ? call BJDebugMsg(I2S(h))
? ? //錯誤!局部變量聲明必須在所有執行語句之前!
? ? local integer h = 5
endfunction
function H takes integer array num returns nothing
? ? //錯誤!數組不能作為函數的參數!
endfunction
jass深入部分
(1)、函數定義
很多初學jass的朋友一直沒有搞清楚該問題,很簡單,函數就是能獨立完成一件工作的機構。
如下例函數showtext,他能完成什么工作呢?或者說他有什么樣的職能?注意,凡是在函數
之間的部分(function~endfunction)就是這個函數的職能或作用。那么,該函數的只能就是
打印一行信息“我在學jass”。
?
下面是代碼
function showtext takes nothing returns nothing
call BJDebugMsg("我正在學jass") //顯示“我正在學jass”
endfunction
?
(2)、生成和運行
函數是如何運行(調用)的?很簡單,call 函數名(參數列表)即可,這里的參數列表的要求前面已經
說過。需要注意的是,call后面接的是一定函數,此類函數叫被調用函數。被調用函數一定要在調用
函數的前面,且傳遞狀態下的參數列表要和被調用函數的參數列表的類型、次序和數量相一致。
代碼
function A takes integer i returns nothing
? ? call BJDebugMsg(I2S(i)) //顯示31
endfunction
function B takes nothing returns nothing
call A(31) //正確,被調用函數A在函數B的前面。函數B將整數31傳遞給函數A
? ? call C(0.02) //錯誤!被調用函數C在函數B的后面!
endfunction
function C takes real rreturns nothing
call BJDebugMsg(R2S(r))
endfunction
?
問題:
可不可以讓一個函數調用其后面的函數?
解:可以的。使用ExecuteFunc("函數名")的檢索功能。
但要注意2重點:
①、函數檢索距離不宜過大,即2個函數之間的字節碼有一定限制;
②、檢索狀態下,被調用的函數不能擁有參數,但可以有返回值;
③、在遵循②前提下,被檢索的函數在檢索函數的前后均可。
代碼
function A takes nothing returns nothing
endfunction
function B takes nothing returns nothing
call ExecuteFunc("A") //正確,被檢索函數無參數,下同
? ? call ExecuteFunc("C")?
? ? call ExecuteFunc("D") //錯誤!被檢索函數D攜帶參數!
endfunction
function C takes nothingreturns integer
return 256
endfunction
function D takes integer ?i returns nothing
? ? call BJDebugMsg(I2S(i))
endfunction
?
語法
? ?循環
loop
? ? exitwhen 條件 //滿足條件退出循環
? ?執行語句
endloop
?
(2)、條件控制
a、單層控制:
if 條件 then //如果滿足條件1則做...
執行語句
endif
b、雙層控制:
if 條件1 then //如果滿足條件1則做...
執行語句
else 否則做...
? ?執行語句
endif
c、多層控制:
if 條件1 then //如果滿足條件1則做...
執行語句
elseif 條件2 then //如果滿足條件2則做...
? ?執行語句
elseif 條件3 then //如果滿足條件3則做...
? ?執行語句
elseif 條件4 then //如果滿足條件4則做...
? ? 執行語句
...... //如果滿足條件N則做...
endif
(3)、邏輯關系符:
僅3個,and(且)、or(或)、not(非)。
(4)、比較和操作符:
僅9個,[](數組)、[]=(數組賦值)、=(賦值)、
==(等于)、!=(不等于)、>(大于)、<(小于)、
>=(大于或等于)、<=(小于或等于)
(5)、實例。若混合使用,注意嵌套先結束里層再結束外層:
function A takes integer ?i, integer ?jreturns nothing
if (i==3 and j==5 ) then //如果滿足i=3且j=5則做顯示他們的和
? ? ? ?call BJDebugMsg(I2S(i+j))
? ?else
? ? ? ?call BJDebugMsg("條件不符!")//否則,不做顯示“條件不符”的警告!
? ?endif
endfunction
function B takes nothing returns nothing
local integer i = 1
? ?local integer array index ??
? ? loop
? ? ? ? ?exitwhen i > 7 //當i>7時退出循環
? ? ? ?set index = 25 * i
? ? ? ?set i = i + 1
? ? endloop
endfunction
function C takes nothingreturns integer
? ? local integer i = 1
? ? local integer array index ??
? ? loop
? ? ? ? exitwhen i > 7?
? ? ? ? set index = 25 * i
? ? ? ?if (i==2 or i==6 ) then
? ? ? ? ? ? ?call BJDebugMsg(I2S(i))
? ? ? ? endif //endif嵌套于循環內,因此應該先結束里層
? ? ? ? set i = i + 1
? ? endloop //再結束外層
endfunction
?
函數的數據傳遞
(1)、利用全局數組傳遞(這個不太熟自己查找)
(2)、在1.20的Gamechche(游戲緩存)+Return Bug(類型強制轉換)
我們知道,要實現一個技能多人運行,可以用全局數組。當然可以利用局域變量,因為局域變量
是互不干擾的、穩定的,且并行運行的。當然,我們可以通過傳遞參數傳遞局域變量的值,然而,
對一些不允許攜帶參數的函數,局域變量的數據無法在函數間傳遞,怎么辦?這個時候需要利用
到緩存。像這樣不允許參數的函數有哪些呢?歸納一下:如下幾種
1、jass計時器調用的函數;
2、隊列事件(單位組、玩家組、可破壞物組等)的過濾(條件)函數;
3、觸發的條件函數。
?
緩存舉例
緩存很好理解,就是一個倉庫,這個倉庫可以想象成很大的房子,里面有很多柜子,而每一個柜子
里面又有很多抽屜,然而,每一個抽屜只能裝指定的5種類型的東西,且每一種東西只能裝載一個。
這就是柜體原理,用不著多么抽象專業的術語,我想上面這個闡述已經讓你理解了什么是緩存。
那么,在實際的緩存中,房子可以看做一個緩存,當然,既然是很大的房子,一個地圖一個緩存足矣。
柜子可以看做緩存的目錄,抽屜可以看做緩存的標簽,由于緩存的目錄和標簽是采用字符串的形式,因
此,我們不妨存儲一個打火機1到房子X的A號柜子K號抽屜里:房子X → A號柜子 → K號抽屜 → 打
火機1同樣的,我們必須在存儲后指定的路徑下才能找到這個打火機1,基于抽屜存放東西的唯一性,“房
子X → A號柜子 → K號抽屜”即指代所存放的打火機1。由于抽屜一次一種東西只能放一樣,如果再放
一個打火機2進去的話,就會覆蓋掉之前的打火機1,此時,“房子X → A號柜子 → K號抽屜”指向打
火機2。當然,如果在其他抽屜比如L號抽屜方上打火機2,絲毫不影響打火機1的存放,因為是2個毫不相干
的路徑嘛。
所以,對于緩存,一個道理,一個標簽只能存5種類型的數據:整數、實數、布爾值、字符串和單位。每種數據
只能存一個,否則后進來的數據會覆蓋之前的屬于這個種類的數據。下面,看看緩存是如何實現的。注意,緩存
必須在使用前初始化,方法為set 緩存變量名 = InitGameCache("緩存名"),緩存名愿意取什么名看你自己心情。
?
緩存存取
取:call Store數據種類(緩存, 目錄名, 標簽名, 該類數據的某個值)
存:GetStored數據種類(緩存, 目錄名, 標簽名)
清空標簽:call FlushStored數據種類(緩存, 目錄名, 標簽名)
清空目錄:call FlushStoredMission(緩存, 目錄名)
清空整個緩存:call FlushGameCache(緩存)
檢測緩存數據是否存在:HaveStored數據種類(緩存, 目錄名, 標簽名)
?
假定緩存名為udg_GC(udg_是什么?有T基礎都多少知道):
call StoreInteger(udg_GC, "A", "b", 15) //將整數15存到了緩存GC的A目錄下的b標簽下
call StoreReal(udg_GC, "A", "b", 0.2) //將實數0.2存到了緩存GC的A目錄下的b標簽下
call StoreIBoolran(udg_GC, "A", "b", false) //將布爾值false存到了緩存GC的A目錄下的b標簽下
call StoreString(udg_GC, "A", "b", "TigerCN") //將字符串TigerCN存到了緩存GC的A目錄下的b標簽下
由于同一個路徑下每種數據可以存一個,所以不會沖突,但若是此時在這個數據下再存一個整數37,即
call StoreInteger(udg_GC, "A", "b", 37)
那么GetStoredInteger(udg_GC, "A", "b")所指向的就是后存進來的37了,但這不影響該路徑下其他類型的數據。
除非它們也想這樣改變。當然,為了保證整數15不被覆蓋,我們可以將37存到標簽c下,即:
call StoreInteger(udg_GC, "A", "c", 37)
利用循環+緩存,我們可以存取局域數組變量。
所以我們要存句柄類的變量諸如單位、特效、點、單位組、物品等均可以利用此形式,自己按照這個格式套就是了。
這樣我們就來存儲一個觸發單位:
function H2I takes handle hreturns integer
? ? return h?
? ?return 0?
endfunction
function I2U takes integer ireturns unit
? ? return i?
? ? return null?
endfunction
function run takes nothing returns nothing
? ? local unit u = I2U(GetStoredInteger(udg_GC, "Unit", "Caster"))
? ?//從緩存對應的路徑下讀取出存儲的單位的地址,通過I2U轉換成單位,再賦值給變量u
? ? call RemoveUnit(udg_GC, "Unit", "Caster", H2I(u))
? ?//刪除單位u
? ?call FlushStoredMisiion(udg_GC, "Unit")?
? ?//清空Unit目錄。注意,刪除整個緩存會刪除其他使用緩存的數據
? ?set u = null
endfunction
function test takes nothing returns integer
? ? local unit u = GetTriggerUnit() //聲明一個單位型局域變量u并賦值為觸發單位
? ?local timer t = CreateTimer() //聲明一個局域計時器t并賦值為新建的計時器
? ?call StoreInteger(udg_GC, "Unit", "Caster", H2I(u))
? ?//將u轉換成整數并存儲到Unit目錄的Caster標簽下
? ?call TimerStart(t, 5, false, function run)
? ?//開啟計時器t,一次性,持續5秒,作用與函數run
? ?set t = null
? ?//清空句柄變量
endfunction
(3)、1.24的Hashtablee(哈希表)
很簡單,只不過是把緩存的目錄和標簽字符串記錄的形式改成了整數,也修正了RB,同時可直接存儲一些
句柄類的數據。
取:call Save數據種類(哈希表, 目錄名, 標簽名, 該類數據的某個值)
存:Load數據種類(哈希表, 目錄名, 標簽名)
清空目錄:call FlushChildHashtable(哈希表, 目錄名)
清空整個緩存:call FlushParentHashtable(哈希表)
檢測緩存數據是否存在:HaveSaved數據種類(哈希表, 目錄名, 標簽名)
?
五、觸發器和函數
一般觸發器無非是由三個函數組成:事件函數(初始化函數)、條件函數、動作函數,所以無需多疑,jass要完
成一個技能或系統也需要經歷這3個步驟,當然,往往可以擴展出很多自定義的函數。總體思路不變。打雷了,
就寫到這吧!10分鐘讓你基本掌握jass。更多內容請到高級討論區去挖掘!如果有人問我最后創建的單位、殺死
單位、創建特效用jass怎么寫,我只能說,最直接最偷懶的方法就是用T寫再轉換成J就行。
總結
- 上一篇: 如何在html上显示时间设置,js实现在
- 下一篇: 计算机联锁设备的应用的摘要,《VPI型计