文档库 最新最全的文档下载
当前位置:文档库 › jass使用教程

jass使用教程

前言 jass语言概述
0. 教程对象
本教程适用于没有接触过计算机语言的人。由于jass非常简单,所以对学过一种中高级语言的人完全没有难度,而本教程对j语言的各部分都介绍的比较详细,所以没有必要学。如果只会用行号的basic(不包括vb、vbscript、ASP)的话,那还是看看这篇教程吧(不过可以略过很多部分)。
如果是完全没有接触过编程的话,建议第一遍通读,大体掌握jass语言的结构,然后再由基础到高级一步步的提高。
基础篇是对于语言的基本结构的介绍,首先要掌握好里面的关键部分,打好这个基础后,可以跟随高级篇里面的范例一步步的学jass的应用。
1. jass的特点
jass语言是一个语法非常简单的语言,语法结构上比较接近basic,同时也引用了许多c的东西。
2. 如何学习和使用jass
在trigger editor窗口中,我们可以很容易的把一个t转成j,方法是Edit(alt+e)->Conver to Custom Text(x),然后我们就可以在已有的j的基础上进行编辑。
由于we里的文本编辑器功能不够丰富,另外,we的容错机制比较差(j的错误太多容易导致直接关闭),所以这里推荐两个专门用于jass编辑的工具:jass editor(汉化版)和jass workshop(by soarchin)。两个工具各有各的特点,后面会在涉及到的部分提到。
另外还需要得到cj和bj两个接口函数库,这个在je和jw中均有提供
3. jass和trigger的关系
在地图编辑过程中,绝大多数用j能够完成的东西也能够用t来完成。事实上,所有的t最后都会转化成j,trigger只是面向we的独特体系。具体的说,war3提供的API有common.j和blizzad.j两个文件,其中c.j是纯粹的接口,而blz.j是对cj里面接口函数的封装,主要面向trigger editor,t生成的j代码中,绝大多数函数都是bj里的。
4. 为什么要学习j
a.用heavylock等工具压缩后,trigger和j面向we的部分被删除了,只留下script.j里面干净的j部分,为了研究别人的map,就要能读懂别人的代码;
b.t虽然能完成几乎所有的功能,但是对于内存释放和另外一些功能实现的能力太差;
c.虽然t看似是一个语法结构完整的可读性比较高的语句,但是因为常常语句太长导致可读性大大下降,而j可以更方便的体现逻辑性,事实上j的可读性更强;
d.用j可以写出比t效率高很多的代码,当然这是在对计算机工作原理比较了解的前提下。


=================================================
第一章 jass基础
0. 本章概述
本章介绍jass语言的最基本内容,包括注释、基本数据类型、变量、数组、基本运算符、运算的优先级、常量、基本运算法则
首先在这里说明一点,jass是区分大小写的,也就是说,jass不会认为“a”和“A”是同一种东西
--------

---------------------------------
1. 注释(comment)
注释符号://
不论是we的语法检查器还是war3,“//”之后该行的内容将被忽略
-----------------------------------------
2. 数据类型:
jass虽然不大,但是提供的功能足够丰富,共提供了6种基本数据类型:
1) integer : 32位有符号整数型(4byte) 取值范围:[-2147483648, 2147483647] (2^31+符号位)
2) real : 实数型(单精度浮点型,4byte) 取值范围:[1.5*10^(-45), 3.4*10^38]
3) boolean : 布尔型(1byte) 记录 true(真)或 false(假)
4) string : 字符串型(不定长) 用来记录字符
5) handle : 数据指针型 是jass语法的一个基础类型,相当于passcal中的pointer类型,由它可以派生出其他数据类型的指针。用来指向内存中的一个数据地址
6) code : 函数指针型 用于指向内存中的函数地址
* 其他数据类型: 1.16有81种,1.17又新增5种,都是由继承handle而来,在common.j最开始的部分,
声明形式如下:
type ****** extends handle //此处即使不是handle,也是其他由handle继承而来的类型

-----------------------------------------
3. 变量:
知道了jass的基础数据类型,下面我们要了解如何使用这些数据。首先就是要对数据进行存取,基本的办法就是使用变量。什么是变量呢?就是war3在运行的时候在内存中申请一块数据空间,用来存取数据的。
1) 变量的类型:分全局变量和局域变量两种,在j中最常用的是局域变量
2) 变量的命名:
首先要给变量一个独特名字,作为这个变量的标志,以便在以后的使用过程中访问。
变量名称必须以英文字母开头,变量名中可以包括:大小写英文字母(A~Z, a~z)、数字(0~9)、下划线(_),其它符号会被认为非法(不要用中文命名变量名)。
we会自动给全局变量加上“udg_”前缀,在jass中使用全局变量的时候直接手动输入“udg_”前缀即可访问全局变量。





第二章 jass语法结构
0. 本章概述
本章介绍jass语言的基本语句,包括判断、循环、判断和循环的嵌套、数组-----------------------------------------1. jass的判断语句
与大多数的语言一样,jass使用了if作为判断语句。jass的if与passcal的判断语法相似(这里不得不b4 basic一下,赋值与判断相等居然都使用“=”,否则的话这里把basic也加进去了)。语法:
a)
if 判断式 then
...
<else
...>
endif 当 判断式 的值为 true 的时候,执行语句
若出现else,则为当 判断式 的值为 true 则执行到 else 之前的语句,否则的话,执行 else 到 endif 之间的语句
if 判断式 then
...
elseif 判断式 then
...
elseif 判断式 then
...
<else
...>
endif 实现多重判断,不做更多的解释了。
------------------------------

-----------2. jass的循环语句
一般的语言会提供for循环和loop循环两种循环,另外还有结构化语言中不提倡使用的goto这个转向语句。for循环实际上是一种最简单情况的loop循环,而靠goto控制的转向可以用loop来完成。遗憾的是jass没有提供另外两个循环中非常方便的关键字continue和break,不过靠if和exitwhen也可以完成的。语法:
loop
...
exitwhen 表达式
...
<exitwhen 表达式>
...
endloop 当表达式的值为true的时候,退出该循环。需要说明的是,可以有不止一个exitwhen,并且exitwhen可以在loop中的任意位置。在使用循环的时候,要注意避免死循环。所谓死循环,就是一直循环下去不会满足退出的条件,这在使用loop循环的过程中非常常见,例如: set i=1
loop
exitwhen i>5
... //中间忘记了set i=i+1
endloop war3有针对死循环的容错机制,当达到一定循环次数的时候会自动退出function,也就是说endloop后的语句不会被执行。
-----------------------------------------3. 判断与循环的嵌套
往往一个判断或者一次循环不能够满足我们想要做的事情,这个时候就涉及到了嵌套。嵌套不难理解,就是在一个判断或循环中,使用了另外一个判断或循环,如: loop
...
if ... then
loop
...
endloop
else
if ... then
...
endif
endif
...
loop
...
endloop
endloop 为什么要把嵌套单独拿出来讲一节,就是因为对初学编程的人来说,使用嵌套的时候常常会出现一些错误。一旦出现了嵌套,一定要先结束里层,然后再结束外层,如下面的语法是不正确的: loop
if ... then
...
endloop
...
endif 可能看到我前面写的东西,各位也比较喜欢空格了吧。在循环和判断的时候用空格来缩进,这样能够更直观的现实逻辑关系,使代码的可读性大大加强。但是缩进也可能导致一些主观的判断错误,例如: if ... then
...
if .... then
...
else
...
endif 在储存的时候这段代码会报错。问题比较容易,自己看吧(j需要endif的语法结构事实上能够比较好的避免大多数问题,像在c里面考试还会常常出这类的题hoho)。-----------------------------------------4. 数组的使用
有些时候,你会不会觉得一些重复的操作很烦呢?比如把一个单位身上的物品丢在一个地方,过一段时间之后再放回身上,例如: local unit u = GetTriggerUnit()
local item item1 = UnitItemInSlotBJ(u, 1)
local item item2 = UnitItemInSlotBJ(u, 2)
local item item3 = UnitItemInSlotBJ(u, 3)
local item item4 = UnitItemInSlotBJ(u, 4)
local item item5 = UnitItemInSlotBJ(u, 5)
local item item6 = UnitItemInSlotBJ(u, 6)
... 这种时候,我们可以用一个array(数组)来表示一组东西。1) 数组的声明:
数组也是变量,所以也有全局和局域之分。全局的数组

只要在全局变量的设置中,把array勾上就可以了,实际上是在变量的声明过程中,在变量类型和变量名称之间加入array关键字: local item array items* 补充:在全局变量的声明中,还有个size选项,那个选项实际上是假的,只是你自己输入一个数字提示自己这个数组应该有多大而以。jass为了防止mapper不熟练使用数组产生的bug,采用了自动增加数组长度的方式。2) 数组的引用:
使用方括号“[]”作为数组的索引,如访问index=1的变量 set item[1] = CreateItem(...) 数组索引的最低值为0,最高值俺没测试过,不过估计没谁那么疯狂,不断的往数组里面添东西。
结合if判断和loop循环,数组可以减少我们大量的重复工作。------------

第三章 过程、函数,Trigger与指针
0. 本章概述
本章设计到jass语言中更复杂的一些东西,熟练使用好这些东西,你将体会到更多作图的乐趣。从本章开始,我会尽量结合自己在作图的一些经历,配合范例讲解j的实际应用
-----------------------------------------
1. function概述
结构化语言的特点就是,使用判断、循环和独立的过程来完成所有的功能。function是一段用来完成一个独立功能的代码,使用好j,就要学会用好function
1) function的构成
一段function,由声明、函数体两部分构成:
function 名称 takes [nothing | 参数列] returns [nothing | 返回类型] //声明
... // 函数体部分
<return [返回值]>
endfunction //标志一段function的结束
2) function的分类
a) 过程与函数
在passcal中,声明一段函数只用这个关键字,而声明过程则用另一个关键字;在c中,则直接使用变量类型来声明函数,可以把无返回类型的函数(void类型)理解成过程。我在这里沿用passcal的概念,将不返回值的函数定义为过程。
常函数与变函数
常函数返回一个固定的值,而变函数则根据参数的不同和函数体内的运算。常函数是在function的声明前加constant关键字:
constant function
c) API与自定义function
API(Application Programming Interface,应用程序接口)是指包含在common.j中的函数,该部分函数实际上是一些已经由系统封装好的函数,用户只要知道function名、功能、参数,就可以利用这部分function做到自己想要做的事情,通常API是实现用户无法通过自定义function来实现的功能,在cj中,
API的声明形式如下:
native CreateItem takes integer itemid, real x, real y returns item
自定义function是指用户在使用过程中,自己定义的一些过程或函数,如我们用到一个地图初始化的trigger中注册trigger的部分:
function InitTrig_MapInit takes nothing returns nothing
set gg_trg_MapInit = CreateTrigger( )
call TriggerAddAction( gg_trg_MapInit, function Trig_MapIni

t_Actions )
endfunction
3) function的参数
在function的声明中,takes与returns之间的部分为参数部分。
nothing关键字表示无参数。传递参数的时候,要写明传递的参数的类型和参数在函数过程中的名称,如传递几个参数,不同参数之间用逗号(,)隔开:
a. function **** takes integer i returns **** //传递一个integer型参数
b. function **** takes real x, real y returns **** //传递两个real型参数
b. function **** takes nothing returns **** //不传递参数
4) function的返回值类型
返回值类型是在声明中returns后面的部分。
如使用nothing关键字,则表示不返回任何数值,该function为一个过程。函数只能有一个返回类型,可使用任何在cj中声明的变量类型:
a. function **** takes **** returns integer //返回integer型
b. function **** takes **** returns nothing //过程

-----------------------------------------
2. function的使用
1) 过程体与函数体
过程体由变量声明、代码两部分组成。变量声明部分是对本地变量的声明,前面已经介绍过,用local关键字来声明
函数体除了过程体以外,还需要有返回函数值的语句,这是函数与过程的区别。
2) function的可见度
只有一个function在前面已经声明过,后面的代码才能使用这个function。一个function对整个map都是可见的。
3) 调用function,传递参数,在function中使用参数,取得函数返回值
我们定义了一个function,那么如何在后面的过程中使用呢?
对于过程和函数,都可以使用下面的方法来调用:
call 名称(<参数列>)
参数列中参数的数量必须与声明中的数量相同,并且类型一一对应,如有一个函数:
function xyz takes integer a, real b, string c returns integer
...
endfunction
我们在调用这个函数的时候,必须如下形式传递:
call xyz(1,0.5,"c")
传递的参数可以是数值(如1 0.5 "a"等)、常量(如bj_PI、UNIT_STATE_LIFE等)、变量。在function使用参数的时候,直接使用声明function时候的变量名称:
function *** takes integer i, integer j returns ***
local integer a = i + 1
set j = j + a
....
endfunction
需要说明的是,参数的传递都是按值传递的,假如有这样2个funciton:
function add1 takes integer i ret





补充资料:
1. 全局变量与局域变量的差别
2. 两个jass的示例
下面结合两个范例来消化前面学过的东西



范例 1


前面已经说过,function是线性运行的,而trigger是并行运行的,那么假如我在做一个既能的时候,想要生成一个范围内的效果。但是如果在程序中用PolledWait执行的时候会发现,实际上的时间没法和真实的技能效果时间对应上,那么怎么解决呢?
一般的方法是另外做一个trigger用来产生效果,在技能触发了t之后

,使用来显示效果的t生效,然后再在本段t中进行伤害,到了一定时间之后在将那个效果t禁止。
用j的话,我们可以利用trigger的工作原理,将两段代码合并在一起。
首先,我们需要一个使function立即执行并不等待function结束的功能,下面介绍这个功能的实现:
// 参数用来传递一个function
function RunFunctionAsTrigger takes code cFun returns nothing
//申请一个居于的trigger,因为我们只要求这个trigger运行一次,所以不需要全局变量
local trigger trg = CreateTrigger()

//把参数传递来的function作为新创建的trigger的action
call TriggerAddAction(trg, cFun)
//立即触发这个trigger,实际上就是让function作为另外一个进程运行,不需要等待执行结束
call TriggerExecute(trg)
//由于这个trigger已经在运行中,我们不再需要它了,所以马上毁掉这个trigger,并释放内存
call DestroyTrigger(trg)
set trg = null
endfunction
第二步,我们要想要在一定范围内持续出现一个效果,那么这个效果应该是每一定时间出现的,所以要创建第二个基础函数。另外考虑到,这个trigger持续一段时间后怎么结束呢?所以要让这个function有返回类型:
//传递一个function的参数,一个real型是说明多少秒之后执行,bool型说明是否只执行一次
function RunFunctionAsTriggerWithTimer takes code cFun, real timeout, boolean periodic
returns trigger
//与前面的过程一样,只对变动部分进行讲解
local trigger trg = CreateTrigger()

call TriggerAddAction(trg, cFun)
//给这个trigger注册一个事件,这个函数的意思就是用于多少秒的时候执行,是否只执行一次
call TriggerRegisterTimerEvent(trg,timeout,periodic)

//返回我们做好的这个trigger
return trg
endfunction
准备工作已经做好了,以后实现类似功能的时候,我们都可以调用这两个函数。使用j的好处就是,我们可以把大量重复的工作封装成function,用到这个功能的时候,只要调用这个function就可以了,而不用一遍一遍的复制,做重复的工作。
下面的是在一定范围内不断产生lich的frost nova的效果:
//用来产生效果,围绕一点(udg_fFSLocX,udg_fFSLocY)(两个都是全局变量),产生一个效果
function FSEffectWithTimer takes nothing returns nothing
//随机产生一个x和y,可以看成近似一个圆,因为出现在这个圆之外是个小概率事件
local real x = udg_fFSLocX+GetRandomReal(0,400)*SinBJ(GetRandomReal(-180,180))
local real y = udg_fFSLocY+GetRandomReal(0,400)*SinBJ(GetRandomReal(-180,180))
//产生效果
local effect eff = AddSpecialEffect("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl",x,y)

//等待2秒
call PolledWait(2)
//销毁效果,释放内存
call DestroyEffect(eff)
set eff = null
endfunction
//用于在技能中拿来RunFunctionAsTri

gger的
function FrostSpaceEffect takes nothing returns nothing
//把效果函数注册成每0.1秒运行一次,即每0.1秒产生一个效果
local trigger trg = RunFunctionAsTriggerWithTimer(function FSEffectWithTimer, 0.1, true)

//等待5秒
call PolledWait(5)
//摧毁效果trigger,释放内存
call DestroyTrigger(trg)
set trg = null
endfunction
好了,现在你可以做一个技能,然后把上述的jass放到里面,看看效果如何了:)




* 补充:在全局变量的声明中,还有个size选项,那个选项实际上是假的,只是你自己输入一个数字提示自己这个数组应该有多大而以。jass为了防止mapper不熟练使用数组产生的bug,采用了自动增加数组长度的方式。这个好像要分情况吧,我试过用timerdialog的数组,只有[0]和[1]是有效的其它都没效,这个很明显的size有关

相关文档