无意发现这论坛,后来又无意发现以前不记得什么时候还注册过号,所以无意就把不久前写的教程拿过来了。球加分,球加精,球各种高亮。
模仿各位大神教程前必定废话连篇的定律,凑凑字数,省的这东西拿除去不到100字,那不是坑爹么。
不知为何,现在很多人都蛋疼的要研究jass这个老物。在无数人的骚扰以及高压下(囧是人都知道我其实很懒),无奈的填掉这个坑。其实jass这种东西,也就是那么回事,理解了基本结构差不多就算入门就可以简单的上手了。并不是什么高深到没有天理的家伙,当然这篇教程不是针对所谓的0基础入门,对于基础这种东西就不做解释了,看的懂就是看的懂,看不懂那也没办法。毕竟我又不是幼儿园老师,一定要教会你们1+1=2。。。
好吧,废话结束(写废话这种事情,还是不适合我啊,哎。) 备注:如有大神发现教程内出现错误,请指出。
=====================只是分割线而已=======================
第一章:概述
关于jass这种东西,在这里大致只讲解两个东西。变量和函数。变量的定义在这里就不多做解释了。大致的知道就可以了。
1函数
a . 函数结构
- function 函数名 takes 参数列表 returns 返回值类型
local 变量类型 变量名 (=初始值)
...........
执行代码
..........
return 返回值
endfunction
复制代码 注:
1:局部变量申明必须在所有执行代码的之前,也就是上面。
2:函数名必须唯一,不能出现相同名字的函数。
3:函数可以没有传递参数和返回值,这类在takes和returns之后写上nothing即可。即代表无传递值和返回值。
4:返回值必须唯一。
5:return必须写在函数末端,无返回值的函数就不需要写return了。
6:注意一下return和returns的区别,虽然只是多了1个s,但还差距是很大滴,前者是具体数据,后者是变量类型。
7:数组不能作为传递参数和返回值。
8:参数列表可以传递N个参数,中间用逗号隔开就可以了。
2变量
a . 全局变量申明:- globals
变量类型 变量名 ( = 初始值 ) //括号表示不赋值也可以
变量类型 array 数组名 //数组不可直接赋值,要在函数内逐个赋值
endglobals
复制代码 b . 局部变量申明:- function Test takes nothing returns nothing
local 变量类型 变量名 (=初始值)
local 变量类型 变量名 (=初始值)
...........
执行代码
..........
endfunction
复制代码 注:局部变量申明必须在所有的执行语句之前。
c. 全局变量与局域变量的区别:
全部任何函数都可以使用,局部只作用于所申明的函数。不同函数间的局域变量命名可以相同,不过最好不要与全部和参数名相同。当然,两种变量各有优缺点。全局变量占用内存大,执行速率快,局域则相反。触发内,比如最后创建的单位啊,触发单位啊,循环整数A什么的都是全局变量。变量编辑器里的变量也都是全局变量。
3示例- globals
integer i = 0 //正确,申明一个整数类型变量 i 初始值为0
real ang //正确,申明一个实数类型变量 ang 无赋值
unit array u //正确,申明一个单位类型变量数组u
integer array real = 0 //错误,数组不能直接赋值
endglobals
function Test_A takes nothing returns nothing
local integer i = 1 //申明一个整数局域变量i 赋值为1.
set i = i + 1
local real ang //错误局域变量申明应该在所有执行代码之前。
endfunction
function Test_B takes real a, real b returns nothing
//传递一个实数类型参数a,和一个实数类型参数b
call BJDebugMsg(R2S(a) + R2S(b)) //在游戏里显示a+b的值
endfunction
function Test_C takes nothing returns real //返回一个实数类型变量
return 1+2+3 //正确
endfunction
function Test_D takes nothing returns real,integer
//错误,一个函数只能返回一个数据类型
return ....
endfunction
function Test_E takes nothing returns boolean
//错误,有返回值类型结尾却没有具体的返回值
endfunction
function Test_F takes nothing returns nothing
local real a = 0
return real a
//错误,有具体返回值。却无返回值类型。
endfunction
function Test_G takes real ang returns real
return Cos(ang)
//正确,有返回值类型也有具体返回值
endfunction
function Test_H takes integer array i returns nothing
//错误,数组不能作为传递参数。
endfunction
复制代码 =====================只是分割线而已=======================
第二章:进阶研究
⑴ 函数的定义
我们可以把函数看成是能独立完成一件职能的机构,而函数起始的function和endfunction之间的部分就是这个函数职能或作用。当几个机构通过传递信息,回馈信息的方式结合在一起的时候就形成了一个完整的部门。从而完成我们指派的任务。就比如下面一个函数,我们想让他的职能是在游戏显示一行信息 “狼是个帅哥” (切,地球人都知道,这还用你说)。- function Test takes nothing returns nothing
call BJDebugMsg("狼是个帅哥") //在游戏显示,狼是个帅哥(唷,人家好害羞。)
endfunction
复制代码 简单剖析上面的函数,我们可以把各种代码什么的比喻成一个员工,而它就完成着自己的任务。当大家任务都完成无误的时候,这整个机构的任务就算是完成了。然后单个或多个机构结合在一起,唷 总裁,业绩提升了呢。 这是为什么呢,废话么。因为狼是帅哥捏。(额,稍微口胡了点而已。)
⑵ 函数的运行概念
函数是如何运行(调用)的呢?只需要 call 函数名(参数列表) 就可以了唷,很简单吧。参数列表这个前面已经解释过,这里就不多做解释了。需要注意的是。被调用的函数一定要在调用函数的前面,而且传递状态下的参数列表要和被调用参数的次参数列表的类型,次序一致。(有点象绕口令,我自己都有点晕。。)- function Test_A takes integer i returns nothing
call BJDebugMsg(I2S(i)) //在游戏显示,i的值
endfunction
function Test_B takes nothing returns nothing
call Test_A(174)
//正确,被调用的函数Test_A在调用函数Test_B的前面,函数Test_B将整数174传递到函数Test_A
call Test_C(174) //错误,被调用的函数Test_C在调用函数Test_B的后面。
endfunction
function Test_C takes integer i returns nothing
call BJDebugMsg(i)
endfunction
复制代码 那有的童鞋就要问了,那前面的函数就不可以调用后面的函数了么。其实是可以的,但是有诸多限制,反正也不常用。这里就不说了。有兴趣的可以自己去研究研究
⑶ 语法
a . 条件控制 (if条件)
①单层控制- if 条件 then //满足条件则做
.......
执行代码
.......
endif
复制代码 ②双层控制
- if 条件 then //满足条件则做
.......
执行代码
.......
else //不满足条件则做
.......
执行代码
.......
endif
复制代码 ③多层控制
- if 条件1 then //满足条件1则做
.......
执行代码
.......
elseif 条件2 then //满足条件2则做
.......
执行代码
.......
elseif 条件3 then //满足条件3则做
.........//满足条件N则做
endif
复制代码
b . 循环 (loop)- loop
exitwhen 条件 //满足条件退出循
.......
执行代码
.......
endloop
复制代码 示例:
- function Test_A takes nothing returns nothing
local integer i = 0 //声明一个整数类型局域变量i 赋值 0
local integer ii = 10 //生命一个整数类型局域变量 ii 复制 10
loop
exitwhen i > ii //条件当i 大于 ii的时候,推出循环
set i = i + 1 //设置i 等于 i 加 1
call BJDebugMsg(I2S(i)) //在游戏中,显示i的值
endloop
endfunction
复制代码
c . 各种符号
①逻辑关系符
a. and(且)
b. or(或)
c. not(非)
②比较和操作符
a. [] (数组)
b. []= (数组赋值)
c. = (赋值)
d. == (等于)
e. != (不等于)
f. > (大于)
g. < (小于)
h. >= (大于等于)
i. <= (小于等于)
d . 示例- function a takes integer i, integer ii returns nothing
if (i != 0 and ii == 0) then //当i 不等于 0 且 ii 等于 0 的时候显示条件符合
call BJDebugMsg("条件符合")
else
call BJDebugMsg("条件不符") //否则,显示条件不符
endif
endfunction
function b takes nothing returns nothing
local integer array i
set i[0] = 0
set i[1] = 10
loop
exitwhen i[0] > i[1] 当 i[0] 大于 i[1] 的时候退出循环
set i[0] = i[0] + 1
if i[0] = 5 then 当i[0]等于5的时候显示狼好帅。
call BJDebugMsg("狼好帅")
endif //endif嵌套在loop里面,所以应该先结束里层的if
endloop //然后在结束外层loop。
endfunction
复制代码
⑷缓存以及哈希表
a . 缓存
①原理
缓存很好理解,我们可以把缓存看做一个巨大的仓库,里面有很多的柜子,而柜子里又有很多的抽屉,不过,每一个抽屉只够能下5种类型的东西。且每一种类型的东西只能装一个,这就是缓存的原理了。
那么,在WE里房子可以看做是一个缓存,既然是一个很大的房子一个房子就足够装下很多的东西,所以一个地图一个缓存就够了。柜子,可以看成缓存的目录,柜子可以看成缓存的标签。缓存的目录和标签都是采用字符串的形式。
然后,我们可以想象一下。把一个套套(这种重要的东西,当然要存起来了。丢了怎么办。)存进仓库的A号柜子,B号抽屉里面那么 仓库 - A号柜子 - B号抽屉 - 套套a 那么同样的,想要拿出这个套套来使用,我们就必须要找到指定的路径才能找到这个套套。而且基于抽屉里东西的唯一性,那么再放一个套套进去就会覆盖掉前一个套套。那么 仓库 - A号柜子 - B号抽屉 里的套套,就会指向套套b 。当然,在其他的柜子或抽屉里放上套套将不会影响套套a。因为是其他的路径嘛。。
So,对于缓存也是一个道理。一个标签只能存5种类型的数据,整数,实数,布尔值,字符串和单位。每种数据只能存一个,否则后来的舒服会覆盖同种数据。
②使用
缓存在使用前必须初始化,方法是 set 缓存变量名 = InitGameCache("缓存名"),缓存名可以随便取。- GetStored数据类型(缓存,目录,标签) //取出数据
call Store数据类型(缓存,目录,标签,具体值) //存储数据
call FlushStored数据类型(缓存,目录,标签) //清空标签
call FlushStoredMission(缓存,目录) //清空目录
call FlushGameCache(缓存) //清空缓存
HaveStored数据类型(缓存,目录名,标签名) //检测缓存数据是否存在
复制代码 ③示例
假设缓存变量名为 udg_GC (别问我udg是什么,这就是所谓的基础了。)- function Test takes nothing returns nothing
call StoreInteger(udg_GC,"A","b",15) //将整数15存在了缓存GC的A目录b标签下
call StoreReal(udg_GC,"A","b",0.1) //将实数0.1存在了缓存GC的A目录b标签下
call StoreBoolean(udg_GC,"A","b",true) //将布尔值true存在缓存GC的A目录b标签下
endfunction
function Test_B takes nothing returns nothing
local integer i = GetStoredInteger(udg_GC,"A","b")
//取出缓存GC的A目录b标签的整数类型数据,并赋值给整数i
local real r = GetStoredReal(udg_GC,"A","b")
//取出缓存GC的A目录b标签的实数类型数据,并赋值给实数r
endfunction
复制代码 这时候有的童鞋们就要问了,只有5种太少了吧混蛋,那我要存别的类型怎么办呢。哇嘎嘎,童鞋们这时候伟大的Return bug就出场了。RB同志说:有我在,缓存就是个受。想怎么虐就怎么虐。不过在这里就不多做赘述了,大家可以找相关教程自己研究。。。
b . 1.24的哈希表
①原理
很简单,只不过是把缓存的标签和目录的形式改成了整数,同时1.24修正了Return bug (RB童鞋泪奔中。)同时可以直接储存一些句柄类数据。
②使用
哈希表在使用事需要声明一个哈希表类型变量才能使用,这个可以结合前面所将到的东西。- call Save数据类型(哈希表,目录,标签,具体值) //存
Load数据类型(哈希表,目录,标签,具体值) //取
call FlushChildHashtable(哈希表,目录) //清空目录
call HaveSaved数据类型(哈希表,目录,标签) //检测数据是否存在
复制代码 示例什么的和缓存其实差不多,也就不多说了。相信IQ超过80的人,应该都能理解的。
=====================只是分割线而已=======================
第三章:传递参数与返回值的简单解析。
本来写了这么多,是不想写这章的。可是大部分的在群里的提问都是传递参数是什么,返回值又是干什么怎么用的什么什么什么的。无奈之下开了这章,简单的解释一下传递参数和返回值的概念和运用。
⑴传递参数
a . 概述
传递参数也就是跟在"takes"后面的那一坨东西,它可以实现不同函数之间的数据传递使用,传递参数可以没有用"nothing"表示。可以是一个,也可以是N个参数同时传递。具体机构为:
- function Test takes (参数1) ,(参数2) ....(参数N) returns nothing
...........
执行代码
...........
endfunction
复制代码
在这里,不同的参数之间使用逗号隔开就可以。
b . 运用
- function Test takes nothing returns nothing
local real a = 1
call BJDebugMsg(R2S(a))
endfunction
复制代码
上面一段函数,相信看了前面的教程后,不难理解。在屏幕上显示1,那我们像让他显示2,3,4或者N呢,那怎么办,难道一个一个写,事实上完全不用这么麻烦。我们可以稍微改动一下这段函数,然后经过调用就可以任意的显示我们像要的数字。- function Test takes real a returns nothing
call BJDebugMsg(R2S(a))
endfunction
复制代码 一个小小的改动,只是多了一个传递参数而已。这时候,我们就可以通过 call 调用函数,来显示我们像要的数字了。比如我们像让他显示1999。只需要 call Test(1999)就可以了。当然,示例写的比较简单。这里就要通过自己的具体应用来把它发扬光大了。。。
⑵返回值
a . 概述
这个概念和传递参数的相反,传递参数是把数据传递给其他函数使用,而返回值却是从其他函数获取一个数据。需要注意的事项在前面已经写的很明白,这里就不在做解释了。。直接进入运用部分。
b . 运用
- function Con takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'A000' ) ) then
return false
endif
return true
endfunction
复制代码
上面的函数有什么用呢,它的意思是当释放的技能等于'A000'的时候(别问我'A000'是什么),将会返回ture值。那否则就反之了。其实这个函数可以精简很多,下面将提到。。
=====================只是分割线而已=======================
第四章:触发器和函数
一般的触发器是由3个函数组成的,事件函数(初始化函数),条件函数,动作函数,那么jass要完成一个技能啊系统啊什么的也是需要这3个步骤的。当然,也可以扩展出很多其他的自定义函数以及使用方法什么的。。。。那么废话不多说,马上就要拨开云雾见青天了。。我们在WE中新建一个触发器a。
[attach]122670[/attach]
然后我们把它转换成文本格式,也就是jass。。得到一堆代码。。- function Trig_a_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then
return false
endif
return true
endfunction
function Trig_a_Actions takes nothing returns nothing
call KillUnit( GetSpellTargetUnit() )
endfunction
//===========================================================================
function InitTrig_a takes nothing returns nothing
set gg_trg_a = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_a, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_a, Condition( function Trig_a_Conditions ) )
call TriggerAddAction( gg_trg_a, function Trig_a_Actions )
endfunction
复制代码 哇哦,看上去是不是有点小复杂呢。没关系,我们来一点点的剖析。
- function Trig_a_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then //释放的技能不等于'AHtb'返回false(假),否则返回true
return false
endif
return true
endfunction
复制代码
咦,看看注释,怎么觉得这功能很熟悉呢。好吧,如果你这么想的话,那么某狼会毫不犹豫的大吼一声:我XX你个XX,熟悉你妹啊,一眼就应该看出来这是条件函数啊混蛋。。。
- function Trig_a_Actions takes nothing returns nothing
call KillUnit( GetSpellTargetUnit() ) //杀死技能释放目标
endfunction
复制代码
咦,别妄想了。在想就成妄想症了。没错,这就是动作函数。。那么下面的就是重点了,重点啊重点。(口胡啊口胡)
- function InitTrig_a takes nothing returns nothing
set gg_trg_a = CreateTrigger( )
//设置触发器a为新建触发(这个前缀gg_trg_是触发器变量前缀注意其实触发器也是变量)
call TriggerRegisterAnyUnitEventBJ( gg_trg_a, EVENT_PLAYER_UNIT_SPELL_EFFECT )
//为触发器a添加事件,任意单位发动技能效果
call TriggerAddCondition( gg_trg_a, Condition( function Trig_a_Conditions ) )
//为触发器a添加条件, 函数为 Trig_a_Conditions
call TriggerAddAction( gg_trg_a, function Trig_a_Actions )
//为触发器a添加动作,动作函数为 Trig_a_Actions
endfunction
复制代码
噢,TXXD。原来是这样啊。没错,其实就是这么简单。所以说jass也不是什么很难的东西。。。囧了个囧。。我在说什么呢,自言自语么。。。
=====================只是分割线而已=======================
家庭作业
怎么,童鞋们,学完了么?接下来我们一起来写作业吧(囧orz。。。)好吧好吧,大家来写个击退函数吧。记得要支持多淫哦,写过的童鞋就在下面跟帖好了。
喵了个咪,终于写完了。球加精,球加分,球交作业。。
|