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

jass教程

本文针对Jass基础=0的各种不想学习Jass 的地图制作者而写, 看完本文后, 我保证jass对你来说仍然是一个未知的广阔世界,但是看完本文后,你一定能够熟练的使用timer和hashtable,配合简单的vjass来做你想要的大部分东西,我不能保证你对Jass了解多么深入,这不是我的目的,我可以保证你们一定能够通过本文,最快的学会Jass中最简易而实用的工具方便我们作图. 高深的Jass知识请去各大论坛查阅相关的Jass教程.

Hashtable 属于1.24新的工具,如果你使用1.24b以上版本制作地图,本文hashtable部分将对你有用。如果你使用1.20~1.23版本作图,那么本文你可以忽略第四章hashtable的部分,但是你需要寻找gamecache/ return bug的教程,因为gamecache/ return bug是适合于1.23以下版本作图的工具,而不兼容hashtable. 反之,hashtable是1.24以上的工具,不兼容gamecache/ return bug, 这就回答了为什么很多1.24的地图和1.20不能互相并存的原因了。当然,你可以使用YDWE开发者提供的更强大的YD功能, 具体去看相关帖子,我这里只说最普通情况下的作图方式而已.

由于我的水平实在有限,对j的了解也只是初涉皮毛, 不足之处还请见谅和指正
目录
引言 0楼
前奏:至上的拿来主义 0楼
第一章: 从Vjass的全局变量开始0楼
第二章: 了解一下function 0楼
第三章: Timer的使用 7楼点击查看
第四章: 1.24b以上的Hashtable哈希表的简易使用方法 8楼到9楼点击查看
其他: 让vjass的library为我们开路9楼点击查看


---------------------------------------------------------------------------------------------引言---------------------------------------------------------------------------------------------
想写一些关于Jass的感言,u9也好,地精研究院也好,Jass教程都无数多,虽然星际2近在咫尺,但是我依然坚信,只要星际2一天不出破解,一天不支持局域网,就只能是一个曲高和寡的游戏罢了,魔兽风采依旧魔兽风采仍在。

我和大部分作图的有着激情的作者一样,属于技术低下型的,但是这不妨碍我们作图,可是T不能满足某些需要,怎么办呢?Jass难学,这点造成了不少人不想开始学习Jass,也造成了不少人依旧使用悲剧的T在作图。Jass有何好处?Vjass能干嘛?这些很多T作者无法体验的好处,一直对于他们来说是个门槛,很难去跨越,我希望从我自己的一些心得,能够让一些作图的作者们了解并会使用我认为最有用,最能带来方便的东西,其他Jass领域,我们不需要知道也懒得知道.

本文针对的读者,我希望是对T基本比较了解的人,然后希望提高作图效率,并且不再被技术门槛所限制的作者们。

我是一个支持YDWE的人,所谓YDWE是一个Evergu

o Aeris等人开发的强大编辑器,我推荐它的最主要原因就是,他支持Vjass...当然我不是要告诉大家Vjass的详细使用方法,我们作为菜鸟,不需要了解Vjass的struct, textmacro....我们只需要了解其中方便我们作图的功能就好.

以下内容的前提是,大家知道,如何在编辑器里面写jass, 方法就是任意新建一个T, 转换T为J文本,删除里面所有文本, 干干净净的开始我们自己的实用Jass旅程
大家请注意,我个人作为长期接触WE的菜鸟,并不觉得不会Jass有什么困难,但是我希望大家了解一些Jass的最实用的东西,提高作图速度,加强作图效率,排除各种泄露,轻松作图. Jass是什么,是个巨人,我们只需要站在巨人的肩膀上,至于巨人长什么样子,构造是什么,我们无需了解,我会尽力让大家都能站到这个肩膀上,足矣

---------------------------------------------------------------前奏:至上的拿来主义------------------------------------------------------------------------------------------------------
我想在正式开始前,向大家推荐Hiveworkshop这个网站,拿来主义的特点就是拿别人的演示,别人的模型壮大自己的威风。当然,我们本着RP的原则,一切拿来主义要尊重原作者,并且在我们自己的地图中给予感谢.

拿来主义并不是说拿就拿的,老外的演示虽然都很细致,细致到讲解给你所有的移植步骤,但是基础的英语阅读大家不能视为障碍,多看看就习惯了。老外很多演示如今都是vjass, 我秉着一切最简单的原则,只要我们大致了解vjass实用的部分就可以了,我希望我的这个文章不仅能够让大家了解实用的jass并且使用简单的jass作图,也能够为大家扫清移植老外演示的一切阻碍.

----------------------------------------------------------------第一章: 从Vjass的全局变量开始------------------------------------------------------------------------------------------
我设定的入口比较奇特,不论你会不会jass, 我一开始就和你谈vjass的全局变量, 这个好东西,太方便了太方便了

大家都知道,在编辑器里面定义一个变量,就是X那个窗口定义出来的变量,叫做全局变量,之所谓全局,就是可以满世界到处通行可用的,那么这些全局变量系统都会自定义为udg_开头(user defined globals), 我们这里要接触一些jass, 如果大家使用T转J的话, 会看到自己定义的变量通通被加上了udg_前缀,很是不爽.....

Jass里面支持局域变量(只能在一个function(一个function称之为一个部分,比如T如果转J, 那么事件就是一个function, 条件和动作又各是一个function,以后再说)里面使用),局域变量虽然排泄方便但是局域变量十分的受限,所以我们仍然需要使用全局变

量。

于是vjass就可以让我们轻松定义全局变量,不再需要编辑器中X的那个窗口去定义全局变量,我们自己可以随处随时想定义什么全局变量就定义什么,而且不会被系统强行添加udg_前缀,具体方法如下

在任何一个T转J之后,删除所有文本,然后复制以下代码:
globals
unitAAA //定义了一个全局变量:单位AAA
realBBB//定义了一个全局变量:实数BBB
integerCCC//定义了一个全局变量:整数CCC
constant integerDDD=100//定义了一个全局常量:整数DDD
endglobals

以后在使用这些变量的时候,就直接写AAA, BBB, CCC, DDD 就好了。还有,每个变量类型是什么,我不多重复,大部分教程里面都有列表,大家可以去查,我这里说几个常用的
boolean 布尔值 destructable 可破坏物 dialog 对话 button 按钮 texttag 漂浮文字 integer 整数
item 物品 leaderboard 排行榜 player 玩家 force 玩家组 location 位置(点) real 实数
rect 地区 effect 特效 string 字符串 group 单位组 timer 计时器 unit 单位

constant加变量类型的写法属于定义一个常量,一般来说,如果我们写一个技能,技能持续的时间是个定值,那么我们就可以用constant前缀来定义这个变量,并且在定义它的时候就赋值。

globals一般写在最顶端,方便日后进行修改和调整,不然看到一大长串的jass代码,谁能不头疼呢......有了globals来定义全局变量,随心所欲方便查看。

另外, // 这个相当于在触发器里面添加的动作“添加注释”,可以写在任何地方,并且在//这一行,后面的内容将视为注释文本,不会对游戏造成任何影响,通常我们用来注释自己写的一些代码,以便查看和修改。

还有,养成好的习惯是很重要的,全局变量不能重复,不管是编辑器窗口定义的,还是globals定义的,比如:

我在编辑器窗口定义了一个变量叫做love, 这个变量实际上会被加入udg前缀就成为了udg_love, 如果我用globals定义一个变量也叫love, 这样不会和之前的冲突, 因为之前是udg_love, 但是如果我用globals定义了一个变量叫做udg_love, 这就不行了, 这个和之前的编辑器窗口定义的变量就冲突了

一般情况下,我们写技能的时候会经常用到全局变量,比如写一个冲锋的技能,不妨我们定义一个前缀叫做CF,那么所有的用于该技能的全局变量就可以写作比如:
CF_unit表示施放技能的单位
CF_target 表示施放技能的目标单位
CF_dam 表示技能造成的伤害
CF_time 表示技 能 持续的时间

这样写可以大大的避免其他技能定义的全局变量造成的冲突。

注意:Jass是区分大小写的,不管是你自己定义的变量,还是其他的,大小写一定要注意,否则不能被YDWE的Jasshelper的

语法检查通过

第一章最后的部分,我给一个我自己写的【动态漂浮文字】演示的开头的部分,大家可以参考:
globals
constant integer FT_min = 1//漂浮文字的初始大小
constant integer FT_normal = 10 //漂浮文字最大化后变回正常字体的大小
constant integer FT_critical = 16 //爆击时候漂浮文字最大化后变回正常字体的大小
constant integer FT_max_normal = 14//漂浮文字由小变大的最大字体
constant integer FT_max_critical = 20 //爆击时候漂浮文字由小变大的最大字体
constant real FT_p = 0.01 //漂浮文字变化大小的周期,建议设定为0.01秒
constant integer FT_z = 1 //漂浮文字每个周期内字体变大的程度,建议设定为1
hashtable FT_ht = InitHashtable()//初始化哈希表
endglobals

有些人可能会问,什么时候空格呢?这个遵循一个不成文的原则,每写一个“部分”,部分内的下面的行可以空4格,这样方便自己书写和查看,当然,空几格还是不空格对代码本身没有影响,只是我们自己的习惯罢了

----------------------------------------------------------------第二章: 了解一下function-----------------------------------------------------------------------------------------
看过很多教程,但是我个人觉得大部分都没有把重点放在解释function上,我个人从没学过编程,function的很多东西完全不清楚,以至于一开始对jass无限郁闷。所以这里,我将把function详解,使得像我这样的菜鸟们,可以轻松的接受并搞清楚.

先给一个截图,大家看看YDWE内的Jass是什么样子的, Jass语法部分的词语是会用各种颜色,或者加粗来显示的,玩家自定义的变量是用正常字体显示的,看着很舒服


我们来看一个例子
function AAA takes nothing returns nothing
...............省略
endfunction
我们一个个来说:
1.function AAA 是给这个function命名为AAA, 在jass中,每一个function是不能同名的,这个类似于全局变量,所以命名的时候要注意,加入前缀比较好。(vjass中的library可以实现function命名自由度更大些,有兴趣的去看Aeris的vjass教程)
2. takes nothing 这里takes 表示携带(初中英语),为什么takes nothing呢,这里只是一个例子,我们如果takes nothing ,就是说啥也没带。
2.1 那么,我们能不能takes 一些东西呢, 当然可以,这里要很多废话,请注意仔细看下去
takes 能take 什么:可以take任何变量类型,比如takes unit u, integer i, real r 等等等等,这里和我们一开始声明全局变量有些类似
take这些变量是干嘛用的: 这是function 的关键所在,我必须举例说明:
比如我要写一个冲锋的技能,我就写先一个function如下:
function chongfeng takes unit u returns nothing
endfunction
然后我们新建一个触

发器写个T如下
-事件任意单位开始技能的效果
-条件使用的技能 等于 冲锋
-动作自定义代码 call chongfeng(GetTriggerUnit())
我们解释一下function chongfeng这里我们就takes携带了一个unit叫做u, 这个unit 当然你爱叫啥都行,叫uu, vv, a, b, c是我们自己的爱好, 那么这个unit u 是干嘛的,到底指哪个unit 呢? 比喻一下, takes unit u 好比就是我们去买菜, 那我们要买什么菜呢?当然这是老妈的命令“你去买颗“白菜””。也就是说,takes unit u 表示我们明确了要买菜的任务,但是需要老妈告诉我们去买什么菜。 我上面的T的例子中,call chongfeng(GetTriggerUnit())的意思是:call 表示老妈发布命令, chongfeng是指我们之前function chongfeng的名字, 括号里面的 GetTriggerUnit(), 指的是触发单位,就是放技能的单位, 相当于我们要买的“菜”.
回顾一下,老妈在Trigger中call 我们function chonfeng 去买菜, 买的是GetTriggerUnit. 那么在WE中,系统就会认为我们把Trigger中的触发单位GetTriggerUnit传递到了另外一个function chongfeng中去了, 而chongfeng中takes unit u, 这里的u就指的是我们在Trigger中的触发单位GetTriggerUnit, 这个概念是很重要的一步,传递变量就是这么开始的。
注意,unit u 这个是个局部变量,何谓局部变量,在此例子中,就是一个字母u代表了一个单位,这个单位是Trigger中的施放技能单位(或者触发单位GetTriggerUnit),这个u只在我们的function chongfeng中是有效的,其他任何Trigger, function都无法使用此变量u, 而u是接受了Trigger中定义的触发单位GetTriggerUnit的变量传递,就指那个使用了冲锋技能的单位。局部变量虽然受限于当前function内部,但是局部变量是可以任意重复的,我们完全可以在其他function中定义一个局部变量也叫做u,但是两个u是不想干的,完全不冲突没有任何关系.
当然,function chongfeng不只需要unit u 来获得传递过来的技能施放单位,如果冲锋技能需要有一个目标单位呢?我们就把function chongfeng修改一下,添加unit p
function chongfeng takes unit u,unit p returns nothing
endfunction
然后我们在下面的Trigger中修改call的部分:
call chongfeng(GetTriggerUnit(), GetSpellTargetUnit() )
GetSpellTargetUnit就是著名的“技能施放目标单位”了,有人会问,我怎么知道?这个最简单方法就是随便开一个触发器,然后写个动作包含有“技能释放目标单位”的,然后T转J就好,你自己去找找对应的单词就知道了。Spell是技能,Target是目标,这些单词不认识的话请用金山词霸
有人会说,那系统如何分辨unit u, unit p哪个是施法单位,哪个是技能施放目标呢?这里,jass是按照顺序来辨认的, function chongfeng takes unit u, unit

p本身没有对u和p作出定义,但是u在第一个,p在第二个。所以我们在另外一个触发器中call chongfeng的时候,把GetTriggerUnit放在前面,把GetSpellTargetUnit放在后面中间用逗号隔开,就一一对应了u和p了。
还有,不管call也好, takes也好,如果涉及到多个变量,请按你需要的顺序把他们写好,用逗号隔开即可

*********************************** *********************************** ***********************************
这里我提一下正规的局部变量的声明方法,第一章我们知道了如何声明全局变量,局部变量的声明也是有自己的格式的,我继续使用function chongfeng的例子:
function chongfeng takes unit u returns nothing
local integer i//声明一个局部变量:整数i
local real r //声明一个局部变量:实数r
local integer x = 2 //声明一个局部变量并且赋值:整数x = 2
local real y = 3//声明一个局部变量并且赋值:整数y = 3
endfunction

注意:所有的局部变量的声明必须紧接着function的下面一行, 就是说局部变量都是在function内部一开始的时候声明的,你可以声明的时候就赋值,也可以不赋值,但是有些类型的变量在声明的时候必须赋值的,比如计时器timer, 声明的时候不能就是local timer t 而应该是local timer t = CreateTimer() 才可以.

另外,这里的unit u 相当于已经声明的局部变量,可以在function内部随意使用
*********************************** *********************************** ***********************************

2.2 这里还得注意一下,function chongfeng我们可以称为冲锋技能的主函数,而我们后来写的触发的事件-条件-动作我们可以称作次要的调用函数。一般写技能的时候,我们注意,调用函数必须写在主函数之后,什么才是写在主函数之后呢?就是先创建一个触发,转成J的格式,写我们的function, 在新建一个触发,写我们的事件-条件-动作来调用我们之前的function, 调用的方法就是call 函数名():
a.如果function chongfeng takes nothing returns nothing, 我们就call chongfeng(), 括号内啥也不写
b.如果function chongfeng takes unit u, unit p,integer i, real rreturns nothing, 我们就call chongfeng( unit, unit, integer, real), 括号内的变量顺序和function chongfeng takes变量的数序保持一致,括号内对应的变量大家可以根据需要填写,可以是GetTriggerUnit 触发单位, GetSpellTargetUnit 技能目标单位, 等等。
c.这样写的好处是什么? 方便实用易修改好移植,大多数演示就是如此写的,我们自己写技能的时候也可以使用先建立jass的主函数function, 写好之后,在随便新建一个其他的T来调用这个function, 并且传递我们需要的函数。Jass的好处众所周知,可以人性化的定义局域变量,局域

变量可以支持多人而不冲突。比如我call chongfeng( GetTriggerUnit() )这个写法,哪怕有12个玩家同时使用冲锋技能,我们function chongfeng takes unit u returns nothing 中takes unit u这个 unit u 对于每个玩家都不会有冲突,大家尽管放心使用.
d. 一定要遵循function 主函数在前,触发的调用函数在后的原则么?如果只是jass的话,确实我们必须这么做。但是有了vjass, 我们不必在意,vjass可以理解为jass的一个增强模式比jass多些新的东西而jass本身的东西没有改变,我们只要使用YDWE就可以随意使用vjass, 具体写法请参照本文的最后一部分其他: 让vjass的library为我们开路

3. 在大致了解了 takes nothing 还是takes 一些变量的使用方法之后,我们可以看看returns nothing是个啥玩意.
3.1 returns nothing是啥意思?直白的中文就是返回xxx, 这里指"什么值也不"返回
3.2 returns nothing可以return些啥呢?同理takes, 你想return啥变量类型就return啥
3.3 returns 用来干嘛的?如果继续买菜的例子,老妈让我们去买白菜,你可以回来说啥也没买着,因为白菜卖光了,这时就是returns nothing , 当然你也可以带着一个西红柿回来说,你实际上买了个西红柿, 当然你也可以说你买了25颗白菜,这个时候可以return这个25的整数.
3.4 事实上,我们可以这么想,继续冲锋的例子,在主函数function chongfeng中,我们returns nothing 表明我们不想要它返回什么,举例returns integer表示我们希望function chongfeng能够在一系列的执行之后搞出来一个整数来返回,那么返回到哪里去呢?如果大家记得我们后来新建一个触发“事件-条件-动作”来call chongfeng()的这个的话,我们就知道,在触发中,我们调用函数chongfeng, 我们可以在这里调用他,因为他takes unit u 了;同样我们可以从他那取得一个返回值integer, 在我们的触发器中继续使用。 注意,takes和returns 这两个是不相干的,根据我们的需要而定。
3.5 当然,returns在一般情况下都是returns nothing,也就是说,大家尽量掌握好takes的用法,returns 不必特别在意,当然returns的具体格式,我们还是来看看:
这里我举一个很无聊的例子,我们试图写一个function, 作用就是把其他函数传递过来的整数处理,处理的方法是变为原来的1/10,然后再返回这个值。function 很容易写,如下:
function QQ takes integer i returns real
local real a = I2R(i)/10
call DisplayTextToPlayer( Player(0), 0, 0, I2S(i))
call DisplayTextToPlayer( Player(0), 0, 0, R2S(a))
return a
endfunction
我们接下来另外开一个触发器, 写如下的东西
- 事件
玩家 - 玩家1(红色)输入 i= , 信息过滤方式 包含字符串
- 条件
((输入聊天信息的长度)等于)4
- 动作

定义代码: call DisplayTextToPlayer( Player(0), 0, 0, R2S( QQ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ) ) )
那么上述两个触发器,前者是J 后者是T, 我们一一来分析.
首先是function QQ takes integer i returns real 这个函数,我们暂且命名为QQ, 那么这个QQ他首先takes 了一个integer整数 变量名是 i , 也就是说我们需要在另外一个地方来call 这个function QQ并且传递一个整数变量. QQ除了takes integer i 还进行了returns real的一个效果,就是说,QQ函数可以制造出一个返回的实数变量让其他的地方使用。那么我们已经了解了takes integer i 这种传进来的传递效果,现在我们分析returns 这个返回的“传出去”的效果如何达到.
(1) local real a = I2R(i)/10 : 这个是我们function QQ中的第一行代码,显然我们定义了一个real实数的局部变量a, 并且我们规定 a 的表达式是 转化整数为实数(i), 并且除以10, 也就是说,我们设定的效果是a = i的10分之一。假设传递进来的integer i = 10, 那么a = 转化整数为实数(10)/ 10 = 10.00 /10 = 1.00

实数,整数,字符串变量之间的转化技巧
**********************************************************************************************************************************************************************
额外补充,大家初次看到I2R之类的可能很陌生,其实这些是很容易记住的缩写,分别表示转化,就像我们在T中遇到的一个整数,可以转化为实数,一个字符串可以转化为一个整数等等。 这里,把T转成J就会看到类似的东西
I2R 全称是 Integer to Real (2和to是英文的同音), 依次类推我们可以有
R2I (Real to Integer) 实数转化为整数
R2S (Real to String) 实数转化为字符串
I2S 整数到字符串, S2I 字符串到整数, S2R 字符串到实数

我们还需要了解整数和实数的转化问题,如果我们转化一个实数2.99为整数,得到的是2;如果我们转化整数2为实数, 得到的就是2.00.
在我们的例子中, 我们如果写 a = i/10 , 而不对 i 进行整数转化实数的话, 如果 i = 12, 我们得到的a = 1.00, 但是如果我们事先转化整数i 为实数的话,即a = I2R(i)/10, 在 i = 12的时候, 得到的a就是1.20了 这些细节需要注意再注意.

补充一些细节: 很多rpg中往往喜欢设定技能伤害值和人物属性挂钩,一般我们设定技能伤害值是一个实数,假设是real r,但是漂浮文字的时候,我们如果直接显示漂浮文字为R2S(r)的话,如果r = 25.91, 游戏中就会漂出来25.91这样很恶心的带有小数的数字,所以我们可以先把r转化成一个整数,再转化成漂浮文字,即是漂浮文字为 I2S ( R2I (r) ), 这样以来显示的漂浮文字就是29这样干净清爽的值了,虽然不很精确,但是一般来说这个不必过分计较
*******

***************************************************************************************************************************************************************
(2) call DisplayTextToPlayer( Player(0), 0, 0, I2S(i)) : 第二行代码我们写的是这个,其实这个是我用T转J之后复制过来修改的,这个的意思很明了,看多就认识了,Display 显示 Text 文本 To Player 给玩家, YDWE很贴心的加入了Jasshelper, 大家在写DisplayTextToPlayer( 之后,就会得到一些提示,告诉你这个括号里面需要有几个逗号隔开哪些类型的变量。 这里格式就是,先写玩家编号,在写屏幕x,y 值偏移的百分比,最后是写string字符串。我们给出的玩家是Player(0), 大家注意,Jass里面Player(0)是指玩家1, 这个有点扯淡,希望注意, 之后两个0 表示屏幕x,y 偏移是0, 就是没有偏移正常显示在游戏左下方, I2S(i)表示我们要显示的文本是整数i , 注意这里我们需要写的变量类型是字符串,如果我们想显示整数i, 就必须转换整数为字符串, 用到的是 I2S. 当然,如果我们要显示一个我们自己定义的汉字或者其它说明文字,我们替换I2S(i)为 "我们想要显示的文字" , 这里用双引号引用起来,内部添加我们需要的文字,界面会把这个部分刷成蓝色斜体方便我们确认这是文本, 比如 call DisplayTextToPlayer( Player(0), 0, 0, "我们的队伍像太阳" )
(3) call DisplayTextToPlayer( Player(0), 0, 0, R2S(a)) : 第三行和第二行是一样的,只是我们显示的是实数a, 所以用到R2S这个。

用显示文本来进行错误排查
****************************************************************************************************************************************
为什么要在这里写第二行和第三行?实际上这两行是以后大家写jass的时候经常用到的一种排查错误的方式,在正式的游戏中我们不会写这个,但是作图的时候,Jass难免会有写错,笔误之类的情况,即使jass语法检查通过, 也经常会发现我们写的jass没有达到我们希望的效果,错误排查可以通过文本显示一些变量, 看看这些变量是否都正确,也可以看看程序执行到哪一步了,方便我们继续排查错误直到正确为止。

这里我们写这两个,明显是希望看到游戏提示的文本a 是否真的是 i 的十分之一,当然实验证明,这里没有错误,检查通过。

其他时候我们可以直接用DebugMsg显示文本,原理一样,只是更加方便的一个显示文本的函数而已, 写法是 call BJDebugMsg( ) , 括号内写字符串变量或者"文本"就好了
*****************************************************************************************************************************************
(4) return a : 这个return 大家看到的是写在最后一行的,return 在触发器中的对应的T是

“跳过剩余动作”,不难理解,一个function 执行阿执行,看到了return, 就好像“你妈叫你回家吃饭”一样,function到此就不再执行了。 return 一般写在最后, 这里我们return a 对应的是function QQ takes integer i returns real 中的那个returns real, 我们这里相当于告诉程序,return的这个real 是我们的局部变量 实数a, 注意我们在function QQ那里写的是返回一个实数,所以在endfunction之前,我们的return的参数也必须是实数,不然就是错误的。
(5) 这个function QQ整体的想法基本就实现了,我们写这个函数,希望他能够处理一个传递过来的整数变量,然后把他变为原来的十分之一并且是一个实数,这个function最后会返回一个实数值。如果我们把这个function比作一个机器,是一个压缩机,我们把一个零件放进去(传递变量给function), 这个机器就会把我们放进去的零件变小为原来的十分之一,然后再还给我们使用(返回值returns),不论任何玩家,任何时候去放这个零件,压缩机都会照常执行.
(6) 我们再看看如何在另外一个地方来call这个函数,我把之前的那个T再贴一次 - 事件
玩家 - 玩家1(红色)输入 i= , 信息过滤方式 包含字符串
- 条件
((输入聊天信息的长度)等于)4
- 动作
自定义代码: call DisplayTextToPlayer( Player(0), 0, 0, R2S( QQ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ) ) )
这个里面,我们看到事件是玩家输入的聊天信息,这里我们设置为i= 部分匹配, 也就是说玩家只要输入的信息包含i=, 事件就成立; 条件我们设置为玩家输入的信息为4个长度,就是说i=28这样的输入信息是成立的。当然我们自己测试没必要那么严格,i=ab在这里也成立,只是我们自己测试的时候i=后面写上两位数的数字就行了。
我们看动作,关键在此,这个动作看似很长,实际这里我们把call, return和显示文本揉在一起了。我一步步解释 :
SubStringBJ(GetEventPlayerChatString(), 3, 4 这个的意思对应T中的动作,截取输入的聊天信息,我们截取 事件响应-玩家输入聊天信息 的 3~4位,就是说如果我们输入信息是i=28, 那么截取的信息就是 28. 这些代码怎么来的,自己写个T的对应动作,转成Jass然后copy过来就好了
注意,SubStringBJ截取的是字符串,也就是说,我们知道28在这里被截取了,但他是个字符串,我们需要把他转化成我们需要的整数,老生常谈,S2I 就可以用上了所以我们看到S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ,现在大家已经理解了这么一长串的意思了吧。
继续,我们已经得到了一个整数28,得到的方法是S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) , 还记得我们要call function QQ不? function QQ takes inte

ger i 这个东西的,也就是说,我们要call 它,需要call QQ(28) 或者写成通用代码就是call QQ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) 对吧。 但是我们注意了 function QQ 不仅takes integer i 还有 returns real 这个玩意儿, 那么我们获取这个return回来的real 如何做呢? 事实上,这里不需要去写call, 我们直接就可以获取到return 回来的值, 写法很简单,就是 QQ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ) , 这个其实就是一个real 实数变量了, 为什么呢, 实际上, 我们QQ (integer) 这样的写法相当于让function QQ 获取了传递过来的整数, function QQ 开始执行,最后返回了一个原来整数10分之一的实数a, 就是returns real, 而QQ(integer) 本身就是我之前的比喻, 我们把整数交给QQ, QQ处理之后还给我们一个实数,这个实数直接就是QQ ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ), 也就是说,我们的call 和return 让这一串代码就全部搞定了。
我们知道,QQ ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ) 这个是返回来的实数, 所以我们想把它显示在屏幕上,用的还是call DisplayTextToPlayer() 这个老掉牙的东西,因为 QQ ( S2I( SubStringBJ(GetEventPlayerChatString(), 3, 4) ) ) 这个是个实数, 我们必须把它转化成字符串的格式,怎么转,R2S 就完了。
大家可以把这个例子复制到你的WE中,自己试试,你输入i=任意两位数字,然后function QQ会提示给你你输入的数字,变为1/10后的数字, 而我们的那个T又会再提示一次变成1/10后的数字,这就是一个利用function 的takes 和returns的简单的变量传递的过程,是不是很爽?

为什么要传递变量?
*********************************************************
或许有人会问,为啥我们搞来搞去这些传递变量?问的好,大家如果写T写习惯了,难免会发现,自己从来不去考虑这个问题,因为一直用的是全局变量,还是udg_开头的在那个X窗口内添加的变量,全局变量当然不需要传递,因为全局嘛,全世界都能用,但是全局变量不能重名是不是, 定义起来要么是编辑器窗口的udg_, 要么是我们第一章看到的用globals来定义全局变量,我们作图的原则是一切从简,局部变量就有这个好处,简单,不怕重复。

举例,我们设计一个英雄的4个技能,如果没有jass的局部变量,我们每个技能都得去定义n多个全局变量来记录施法单位,技能目标,技能伤害值,然后在T的窗口里面一个个定义,然后动作的时候一个个的选来选去,尤其是层套层的那种T,选一次费事的要死。

相反,我们有了Jass,局部变量随便定义,我们可以先在function中写好一切,最后只需要外部的一个T来call function就好了,涉及的变量可以

用传递来实现T和J的一种连接。不仅方便,容易修改,容易移植,也看起来井井有条. 另外, Jass是可以随意复制粘贴,尤其是可以替换,大家用多了,会发现替换replace是个无比方便快速的好东西,同样情况下T的话,就得一个个去郁闷了。

实际上T也是function, 大家如果转化了T为J之后,事件-条件-动作每一个都对应一个function. 我们所说的变量传递call 的方法,实际上是function 和function 之间的局部变量传递,当然我们清楚,全局变量不需要传递。接下来我们稍微用我们已经了解的知识来看看最普通的T转J后的效果,作为回顾: - 事件
单位 - 任意单位死亡
- 条件
(触发单位)的类型等于 Footman
- 动作
不做任何动作
我们接下来使用T转J我们来看看都出现了什么,这个触发我们命名为Good

于是我们得到了以下的东西 function Trig_Good_Conditions takes nothing returns boolean
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'hfoo' ) ) then
return false
endif
return true
endfunction

function Trig_Good_Actions takes nothing returns nothing
call DoNothing()
endfunction

//===========================================================================
function InitTrig_Good takes nothing returns nothing
set gg_trg_Good = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( gg_trg_Good, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( gg_trg_Good, Condition( function Trig_Good_Conditions ) )
call TriggerAddAction( gg_trg_Good, function Trig_Good_Actions )
endfunction
以上就是我们得到的J文本,我们一起来看每个function对应的是什么意思:
-首先,我们看看这里面有三个function, 我们看到,第一个function 对应的是条件, 第二个对应的是动作, 第三个对应的是事件。我们首先不觉得任何奇怪,事件在最下面。这个遵循我们之前提到的顺序,如果你要call 一个function, 那么call 本身所在的function 应该写在 被call的function 之后。我们简要的说,事件是一个开关,用来call条件的function 和动作的function, 所以我们把条件和动作的function放在前面,而事件放在后面.

- 其次, 我们先看事件对应的function InitTrig_Good takes nothing returns nothing, 注意这个function 被系统命名为InitTrig_ 加上 Good, 我们知道我们的触发器叫做Good, 这里,InitTrig 按照意思表示初始化触发器,也就是说,由于我们任何新建的触发器都是初始打开的,所以这里命名规则是InitTrig也不奇怪. 当然这个地方名字都是随便改的。最后,这个function 没有take也没有return 我们不奇怪,因为他是call别人的,自己本身一身轻。
下面看看事件function内部的东西, 第一行,我们设置一个触发器类型的变量gg_trg_Good 初始化,这个gg_trg_Good算作一

个系统新建的全局变量,我们当然可以自己把第一行替换为局部变量 local trigger trg = CreateTrigger() , 这样我们在endfunction 之前写上一个 set trg = null 就可以完美排泄触发器类型的变量了。不过这里作为全局变量的触发器,要不停的使用的,不用去set = null也没关系。后面接下来的三行call Trigger 的就得熟悉英文猜意思了:
call TriggerRegisterAnyUnitEventBJ( gg_trg_Good, EVENT_PLAYER_UNIT_DEATH ):Trigger 触发 Register 注册 Any 任意Unit 单位Event 事件 BJ (这个是暴雪bj函数的后缀),也就是说,这句话是给触发器注册一个任意单位的事件,括号内的是我们的变量, gg_trg_good是第一句里面的全局变量,指代我们这个触发器, EVENT_PLAYER_UNIT_DEATH根据意思来,就是 事件_玩家_单位_死亡,在这样直白的翻译下,我想大家都看懂了吧。如果是其他的事件,对应的写法是不同的,我们不必记住,只要用的时候T转J就可以了。
call TriggerAddCondition( gg_trg_Good, Condition( function Trig_Good_Conditions ) ) : Trigger 触发 Add 添加 Condition 条件,明显这里是指要给我们的触发器添加条件,添加条件的触发器还是我们的gg_trg_Good在括号内有写, 而Condition( function Trig_Good_Conditions ) 这里就是添加条件condition的一个写法了,这个相当于call 一个function, 只是我们用此格式添加的条件就是这么写的,要写上function 加上触发器的名字,我们知道Trig_Good_Conditions是我们第一个function的名字,大家可以拉回去看看。注意,在这个格式的写法上,我们不能传递变量,所以第一个function 是takes nothing的。
call TriggerAddAction( gg_trg_Good, function Trig_Good_Actions ) : 这个明显就是加入Action动作的,后面的格式大家注意一下。

-接下来,我们来看条件的写法function Trig_Good_Conditions takes nothing returns boolean:首先我们知道,在上述的事件的function中,已经对条件进行了呼唤。那么条件的function 在呼唤的时候是没有takes的,但是作为条件,他有return的值是一个布尔值boolean,何谓布尔值,就是一个要么是真,要么是假的一个判定,具体我们看到
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'hfoo' ) ) then
return false
endif
return true
这里的写法,首先是条件if then endif.....这里提一句,在T中,我们向来是if....... then .........else ......... 不过在Jass中,我们要保持有始有终,如果我们写了 if 我们就要写endif, 如果我们写了function 我们就要写endfunction , 如果我们写了循环loop, 我们就要写endloop, 这种有始有终的规则应当是各种编程的规则吧。另外,jass中我们写if的时侯,如果没有else 的动作,我们可以不写else,直接写endif的。我看有写人在T里面写else 的动作是 " 什么也

不做" , 据说这种写法十分多次一举,所以我们在Jass中, 就自如许多。

我们再来看条件中的if 后面是什么, 首先是个not, 这个写法很诡异,不过所有T转J出来的条件都是这,not 是否定一个事实, 而后面的事实是 触发单位 的 单位类型ID 等于 hfoo, 什么是hfoo, 大家打开物体编辑器, 按ctrl + D 就能看到所有单位,物品,技能等的 ID 值, 我们在T中形象的知道,我们设定的条件是单位类型 = Footman 步兵, 在Jass里面,我们看到了hfoo, 在物体编辑器里面我们看到了hfoo就是步兵的ID. 大家注意一下,'hfoo' 的写法,是单引号。另外注意if 条件中我们 判定等于的时候 是两个等号,同理大于等于是写 >=, 小于等于是<=, 不等于我们写!= .

这里如何理解呢? not ( GetUnitTypeId(GetTriggerUnit()) == 'hfoo' ) 就相当于 GetUnitTypeId(GetTriggerUnit()) != 'hfoo' 的意思,就是说当触发单位的ID不等于步兵的时候,我们就返回一个false值,如果这个if不成立,我们就返回一个true值。当然,我们已经比较熟悉if的写法,我们完全可以自己写一个看起来舒服许多的。
if ( GetUnitTypeId(GetTriggerUnit()) == 'hfoo' ) then
return true
else
returnfalse
endif
如何, 这样写是不是舒服多了:如果单位类型符合就是真,否则就是假。
返回值是真是假,我们好像并没有体验到在事件的function中我们获取了返回值,事实上我们在用call TriggerAddCondition( gg_trg_Good, Condition( function Trig_Good_Conditions ) ) 这个玩意儿的时候,就已经在检测返回值了,如果是真,则动作运行,否则没有动作。而动作的function是function Trig_Good_Actions takes nothing returns nothing 这里我们没有任何新奇的敌方,动作具体行为我们在T中写的是“什么也不做”所以在Jass中转换为call DoNothing() 。

有人会问了,我们之前call其他function的时候,都是有传递变量的,而其他function也takes了一些变量用于接受传递,为什么这里没有?这是T的一个重要局限,我们写的T转J, 在条件和动作中是没有看到局域变量的使用和传递的,因为全部takes nothing,但是我们写T的时候,都有一个事件,比如单位事件,事实上,我们在条件中已经看到GetUnitTypeId(GetTriggerUnit()) 这个东西,我们的变量传递实际上是靠GetTriggerUnit()来完成的, 好比我们在T中会写 设置 AAA = 触发单位 一样, 然后我们接下来不停的使用AAA去代表这个单位,因为AAA是全局变量。

然而我们在Jass中,会经常在动作的function中写到 local unit AAA = GetTriggerUnit() , 这个是一个道理,只不过这里AAA是局部变量,只能局限在本function之内使用,一旦脱离本function,就失效了,如果要在其他function中使用

我们的AAA, 目前我们知道变量传递是用 call function() 的做法,但是对于有些情况我们不能使用call 的时候, 另外的function是takes nothing returns nothing的时候,该如何是好呢?答案就是“缓存”,或者1.24的新宠hashtable.

在本章完结之前,我们可以额外考虑一下,做一个技能该怎么做。大家看到很多很炫的技能,或者不炫耀而实用的技能,这些都是怎么做的?首先我们要知道,好的技能效果的模型不仅仅局限于魔兽内部,我们可以从外部导入,这就需要大家的审美观和素材网站了,我推荐地精研究院的模型区和Hiveworkshop的model区。 其次我们要知道,很多技能,比如圆形,正方形,螺旋型之类的,这些需要你补充你的数学知识,WE区有各种斑竹大人的教程和演示,大家不妨可以学习。最重要的还是自己的思路,做一个什么样子的技能,这个首先要思考通过什么原理去实现它,比如我们想做冲锋的技能,怎么冲呢?

设想一个单位施放了技能,技能等于冲锋,接下来我们记录下这个触发单位,顺便记录技能目标单位然后传递给我们的主要function chongfeng takes unit u, unit p returns nothing. 这是初始的皮毛部分,那么function chongfeng里面具体如何写呢?我提其中一种思路,我们可以让触发单位在一定的时间内不停的攻击目标单位,这样的话,触发单位就会向目标持续的奔去直到奔到他面前,然后等他到了目标面前,既然是冲锋,速度必定快,我们可以考虑加速冲过去,并且肯定得有晕眩的效果,我们设想此时我们添加一个马甲,向目标丢一个没有伤害的锤子。还有冲锋的过程中是否要无视其他单位的存在呢,当然要,我们就关闭触发单位的碰撞,等冲锋结束之后再开启。这些都是我们脑子中的一些想法,如何实现呢?如果用T去做,肯定累的半死外加各种动作不讨好。所以我们果断选择J来写,皮毛的写法本段初就提到了,那么之后呢,如何控制这一段冲锋的时间?要让单位不停的去攻击目标,势必每0.1秒发布一个动作比较合理, 就是说每0.1秒命令单位冲向目标,并且加快他的移动速度。但是function chongfeng中已经无法实现每0.1秒命令单位干嘛的动作了,我们势必新开一个function来做,这个function怎么能够得到之前的数据,怎么能够每0.1秒执行一次?这就是我们下面要提到的Timer计时器和Hashtable的储存功能了.
未完待续............



----------------------------------------------------------------第三章: Timer的使用------------------------------------------------------------------------------------------------

Timer 顾名思义,时间相关的东西,我们叫计时器,T高手们会说,这个玩意我经常用

阿,T里面不就有么。。。。没错,T里面是有计时器,但是T里面的计时器相比Jass里面的少了一个最重要的功能,就是召唤function.

如果我们转化T中的计时器的启动计时器的动作,我们定义一个全局变量叫做t, 转化为Jass于是:
call StartTimerBJ( udg_t, false, 30 ) //udg_t 是新建计时器的全局变量,false表示一次性计时,30表示时间长度
但是我们在Jass中,我们写的是:
local timert = CreateTimer()
call TimerStart( t, 30, false, function AAA) //t 是新建计时器的局部变量,30表示时间长度,false表示一次性计时,function AAA是召唤function AAA的执行
看到重要区别没,后者多了一个选择, 可以召唤function AAA, 但是T中的计时器是没有这个功能的,这个重要的区别将带来完全不一样的感受。

如果作为读者的你,还不是很了解计时器是干嘛的,那么我们就再来回顾一下, 不管是T还是Jass的计时器Timer, 效果无非两种:
1- 一次性计时,就是在若干时间后执行一些东西
2- 周期性计时,就是每隔若干时间来执行一些东西

而我们要知道Timer计时器是很准确的,准确到0.01秒,这个比T中的等待时间要准确的多,这个结论是很多前辈们亲身测试得到的结论,我们知道就好
Timer计时器,按照一些老外的说法,尽量少用0.01秒的周期性计时,如果能用0.03秒最好,这个结论我们也要了解一下,因为如果地图满世界的写0.01秒循环的计时器,据说不好

那么计时器的这个效果能用来干嘛?我们常用到周期性循环,回到我们的冲锋技能,我们需要让一个发动冲锋技能的单位在未来的一小段时间里,不停的重复的冲到目标单位去,也就是说我们需要用到循环计时的Timer, 这里我们讲Jass的Timer, 所以是call TimerStart( t, 0.1, true, function ...),就是让function ...每0.1秒执行一次。

那么按照顺序原则,我们首先要定义一个新的function chongfeng_zhouqi 来表示在周期时间内执行的动作,这个function 是被function chongfeng 来call TimerStart( t, 0.1, true, function chongfeng_zhouqi), 大家注意function chongfeng_zhouqi不是function chongfeng_zhouqi(), 所以我们不能通过直接添加变量的方法在括号内来传递变量,所以function chongfeng_zhouqi本身就得是takes nothing的,我们也不需要它返回什么值,所以就returns nothing,但是我们又不想用全局变量,因为我们目前满世界用的是局部变量,那么如何做呢?这里需要用到一个临时储存数据的东西,在1.24以上的版本中,我们要用到Hash来临时记录这些局部变量,我们将在下一章详细解释。

我们现在先把我们知道的写下来function chongfeng_zhouqi takes nothing returns nothing
endfunction

function chongfeng takes unit u, unitpreturns n

othing
call TimerStart( t, 0.1, true, function chongfeng_zhouqi)
endfunction
我们再写一个T来召唤function chongfeng如下:-事件任意单位开始技能的效果
-条件使用的技能 等于 冲锋
-动作自定义代码 call chongfeng( GetTriggerUnit(), GetSpellTargetUnit() )
那么本章到此没什么可讲的了,Timer本身没啥难度,但是我们需要配合Hashtable的临时储存系统来配合使用传递变量。在进行到下一章之前,我们其实遗留了一个问题

细心的人会发现,对于周期性的Timer来说,我们让function chongfeng_zhouqi 每0.1秒执行一次,但是问题出来了,我们并不需要单位一直冲锋,也就是说我们不需要function chongfeng_zhouqi 无休止的运行下去,我们总得想个办法让它在一段时间后停下来。熟悉T的人应该想到,我们只要设置一个全局变量整数,每运行一次就让他变大一点,直到他大到某个数字,就用条件判断来停止function chongfeng_zhouqi 的执行。

当然,在下一章我们学会了hashtable之后,我们可以用hashtable来设定一个局部变量的整数或者实数来控制Timer的循环次数。

最后,在开始Hashtable之前,对于各位读者,我们再次确认,你如果愿意或者正在使用1.24b以上的魔兽,那么请你继续看 Hashtable的内容,否则,如果你一直要坚持使用1.20e的话,那么请你去参考以前的其他教程,关于GameCache和Return bug的。

我还是推荐大家能够使用1.24b以上作图,
第一:我们有8M地图空间
第二:Hiveworkshop上最近一两年的所有演示都是1.24b以上的,并且老外的演示都很细心和方便使用,如果我们还是1.20e将无福享受这些好资源了
第三:与时俱进吧,说实话,Hashtable真的是无比简单无比方便,大家即使使用1.20e,我也推荐你看看hash,因为你会发现他的好


----------------------------------------------------------------第四章: 1.24b以上的Hashtable哈希表的简易使用方法----------------------------------------------------

本章是关于hashtable的,我个人其实是直接从hashtable学起的,对于return bug/gamecache 的使用方法也全然不知。但是,都是作为一个储存数据的媒介,我会把我所知道的都呈现出来。

最初我是在地精研究院寻找的hashtable教程的,总得来说,地精的一篇帖子写的非常好,生动的描绘了什么是hashtable,我们在知道hashtable的具体使用方法之前,也来遵循一个形象的原则,hashtable感觉上是什么样子。

hash一开始被翻译成哈希,延续到目前为止我们都称作哈希,table就是表格,所以我们叫他哈希表,不管是哈希,还是别的希,总之这是一个table.

地精研究院的帖子举的例子是图书馆的例子来形象的描述什么是hashtable,我这里就换一个例子

来告诉大家什么是hashtable..大型高级网吧的例子:
-相信大部分人都去过网吧,那么网吧的上机流程无非于此,我们首先找到一个网吧,走到前台,然后说我要上网
-然后,前台的人员会问你,你要去休闲区,还是竞技区,还是豪华包间区呢,你说我要区竞技区,我是高级玩家
-之后,我们领一张充值的上机卡,去你的竞技区寻找一个没有被使用的电脑
-最后我们凭此卡,登录这台电脑,然后开始做我们想做的事情
这里我们回顾了上机的例子,如果放到hashtable,如何来理解,下面我们来用上机的例子帮助大家理解hashtable的原理:

要理解原理我们先看看hashtable的格式是什么?

call SaveXXX( Hashtable, Parentkey, Childkey, 变量)

其中SaveXXX 可以写成SaveStr (保存string字符串), SaveInt(保存integer整数), SaveReal(), SaveUnitHandle(保存单位) 等等

那么我们来分解一下 SaveXXX后面括号内的东西。第一个因素是Hashtable的名称,一般hashtable我们会定义一个变量,来代表某一个哈希表,哈希表本身属于一个变量的类型,具体声明如下:globals
hashtableCF_hash = InitHashtable()
endglobals
那么在此CF_hash 就代表了我们定义的一个全局变量哈希表,以后使用的时候就写CF_hash 就可以了, 这里我必须强调一点,声明一个哈希表的变量,必须定义= InitHashtable(), 就和声明一个Timer 一样必须定义= CreateTimer(), 否则无法正常使用


****************************************************************************************************************************************************************
有人会问,那什么时候定义一个变量不需要定义值呢,这个大家可以参考T中的全局变量表, 比如


大家看到,有默认值的,和“没有”初始值的,是不需要我们声明的时候就定义的,但是计时器t 我们在定义的时候就得= CreateTimer()

一般来说,unit, string, real, integer, effect 这些初始声明的时候是不需要定义对应值的。

虽然hashtable也不需要,但是如果想使用它的话,就必须init 初始化他,所以我们是hashtableCF_hash = InitHashtable()
****************************************************************************************************************************************************************

下面到了关键部分,parentkey 和childkey.....

如果大家打开T的编辑器,会看到系统翻译为“主索引”和“子索引”,翻译的很好,但是我们不理解

英文直白的意思,就是家长和小孩,我们还是不理解 -好吧,我们回到网吧的例子,大家来比较一下, 假设你去的网吧叫做“阿拉希网络联盟”。网吧好比是hashtable, 而“阿拉希网络联盟”就是哈希表的名称
-这时候我们犹豫了,这

个网吧十分巨大,有分区上网,有的区叫做休闲区,就是一堆速度极慢的电脑;有的区叫做竞技区,里面一堆好电脑专门玩3D游戏的;有的区叫豪华区,那就是有沙发躺椅,绿色植物,美女相伴的上网区域,电脑都是30寸宽屏。 我们思索了一下,自己作为资深魔兽玩家,肯定要去竞技区,那么我们理解这每个区,就是parentkey, 而每个区的名字就是parentkey这个整数变量的名字。
-到了竞技区一看,哇噻,有1万台速度配置着16核cpu和双显卡的电脑,我们得找一台空机来上机对吧。我们理解这里每一台高配置电脑叫做childkey, 而每个电脑的编号比如我们上机的机子编号是"竞技9527", 那么这个"竞技9527"就是childkey的名称
如此,我们可以改写我们之前的hashtable
call SaveXXX( 阿拉希网络联盟, 竞技区, 竞技9527, 变量)

假设我们要在这个网吧的,竞技区的,竞技9527编号的电脑上存储一个整数变量int, 我们如下写
call SaveInteger( 阿拉希网络联盟, 竞技区, 竞技9527, int)

是不是很容易,很容易吧?当然,我们在jass里面拿中文做变量名称是悲剧的,所以我们换成字母比较妥善
call SaveInteger( CF_hash, JJ, JJ9527, int)

不过,我们习惯这么写
call SaveInteger( CF_hash, parentkey, 1, int)
parentkey我们在function前端作为local integer 变量声明,后面的1表示childkey的序号,我们一般直接写数字,表示我要在CF_hash哈希表的parentkey的1号位置存储一个整数变量int

如何,逐步看下来是不是和网吧的例子大同小异呢

那么,到此为止,我们可以说,大概了解了一下hashtable是什么,怎么来保存数据。接下来,我们只要再看看如何载入我们保存的数据就好了

还是网吧的例子,我们之前在阿拉希网络联盟,竞技区,竞技9527的电脑上存储了一个整数,如果我们下次要找到这个整数如何呢?

很容易,我们只需要明确目标,要找到这个整数,我们来到阿拉希网络联盟,走到竞技区,找到电脑9527,然后就能找到我们上次存储的整数了,写成代码的形式就是
LoadInteger( 阿拉希网络联盟, 竞技区, 竞技9527)

大家奇怪了,储存的时候,我们好像多写了一个变量名称叫做int, 载入的时候怎么不写了,hashtable每一个childkey对应只能存储一个类型的变量,也就是说,我们网吧的每一台电脑只能存储一个变量,所以我们只要找到竞技区的“竞技9527”, 就知道这台电脑上存储的是我们需要的整数变量int了。

还要注意一点, LoadInteger()本身是一个整数,那么LoadReal就是一个实数,LoadStr就是一个字符串,LoadUnitHandle就是一个单位了。。

所以我们在另外一个function使用Load 的时候,经常直接用local 来写

,比如local integer i = LoadInteger()
localrealr = LoadReal()
localstrings = LoadStr()
这里我省略了括号内的内容,只是做一个举例

那么我们现在就要来结合我们知道的hashtable的储存和载入数据的方法于一身,实际操作一下

我们需要回到第三章那里,实际的来操作一下,我们仍然来看看冲锋的技能怎么继续写下去,我们结合第三章的Timer,然后使用hashtable, 这里跨度有点大,请各位仔细的看下去:

第三章最后讲到function chongfeng_zhouqi takes nothing returns nothing
endfunction

function chongfeng takes unit u, unitpreturns nothing
call TimerStart( t, 0.1, true, function chongfeng_zhouqi)
endfunction
我们再写一个T来召唤function chongfeng如下:-事件任意单位开始技能的效果
-条件使用的技能 等于 冲锋
-动作自定义代码 call chongfeng( GetTriggerUnit(), GetSpellTargetUnit() )
我们这个是在不知道hash之前写的,那么我们现在直面这个function 究竟如何写下去,目前T的部分我们已经不需要再碰了,我们之后不再讲,我们只研究jass部分的两个function.

按照顺序,我们需要首先看function chongfeng takes unit u, unitpreturns nothing
call TimerStart( t, 0.1, true, function chongfeng_zhouqi)
endfunction
这里我们需要继续添加动作,并且是在TimerStart之前,因为我们要知道,我们的call TimerStart中的计时器t并没有定义~.~我讲在下面的表格中用绿色注释的方法对每一条添加的语句解释, 大家可以直接把下面的代码复制到WE中来看或者使用//我们首先需要定义一个全局变量的哈希表,我们叫它CF_hash, 注意,多数情况下,我们定义全部变量的哈希表,毕竟是储存数据用的
globals
hashtable CF_hash = InitHashtable()
endglobals
// 以上我们定义完毕全局变量哈希表,并且初始化他, 当然我们还需要其他全局变量,这里先不提

// 下面的chongfeng_zhouqi的function我们暂时不管,这个是主要函数,我们按照“后到先”的顺序先看下面的函数
function chongfeng_zhouqi takes nothing returns nothing
endfunction

//这里我们需要首先来设置好chonfeng这个函数,因为他是接受了从T传过来的施法单位和目标单位的变量的,这两个单位十分重要是我们以后设置各种行为的前提
functionchongfeng takes unit u, unit p returns nothing
// 首先我们创建一个timer计时器,叫做t, 并且=CreateTimer(),不这么做的话,后面就无法正常使用了
local timer t = CreateTimer()
// 接下来我们需要设定我们hashtable中的parentkey, 就像网吧的“竞技区"一样,一般我们投机取巧的赋值这个parentkey为刚才创建的计时器的handle, handle是什么,无关紧要,我们只需要知道这个东西每个t都对应一个,大家照做就好了
local in

相关文档