找回密码
 点一下
楼主: chyj4747

JASS教程—0基础新手向教程;内容多打开可能较慢;共十八章完结;2014/11/24进行了维护

[复制链接]
 楼主| 发表于 2011-7-12 04:29:10 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:29 编辑

第十章:循环(loop)

循环在程序中是个什么概念?
比如现在有个程序,功能是给甲乙丙丁四个人按顺序发牌,第一张给甲,第二张给乙,……,以此类推,直到所有的牌发完。这就是一个循环。

现在来看下J的写法:
  1. function 发牌 takes nothing returns nothing
  2.     local integer 牌数 = 52
  3.     loop
  4.         exitwhen 牌数==0
  5.             ……(发牌动作)
  6.         set 牌数=牌数-1
  7.     endloop
  8. endfunction
复制代码

很明显,引导词是“loop”。
exitwhen”这词看不懂就拆,exit:出去,when:当……时
再稍微联想一下,就是“当……时跳出循环”(:如果一直循环下去那就是死循环了,魔兽会崩的。。所有要避免死循环)

这个函数的解读方法如下:
1.    牌数是52不等于0
2.    发牌动作后设置牌数等于牌数减1(意思就是,新牌数=52-1)
3.    牌数是51不等于0
4.    发牌动作后设置牌数等于牌数减1(新牌数=51-1)
5.    之后重复
6.    直到牌数==0
7.    结束循环

接着完善下发牌动作:
我们需要在发到丁后重新发回给甲,常用的方法之一是给一个数字n赋值,一开始是0,n是0则发给甲,n=n+1==1,n是1则发给乙,n=n+1==2,n是2则发给丙,n=n+1==3,n是3则发给丁,n=0。
  1. function 发牌 takes nothing returns nothing
  2.     local integer 牌数 = 52
  3.     local integer n = 0
  4.     loop
  5.         exitwhen 牌数==0
  6.             if n==0 then
  7.                 发牌给甲
  8.                 n=n+1
  9.             elseif n==1 then
  10.                 发牌给乙
  11.                 n=n+1
  12.             elseif n==2 then
  13.                 发牌给丙
  14.                 n=n+1
  15.             elseif n==4 then
  16.                 发牌给丁
  17.                 n=0
  18.             endif
  19.         set 牌数=牌数-1
  20.     endloop
  21. endfunction
复制代码

PS:这段函数可以把n=n+1提出来,在发牌的判定句结束之后再写,缩短函数,不过写成这样比较直观,所以我就先不简化了。

练习(大家自己试一下~):
按下ESC在屏幕左下角按递增顺序显示1-10这10个数字。

思路(按Ctrl+A查看):
先设置局部整数变量n为1,循环 - 当n大于10时跳出循环,call DisplayTextToPlayer(……, I2S(n)),设置n=n+1

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:29:26 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:29 编辑

注:如果J过长自动换行,请切换到论坛的宽版模式,不知道切换方法请看顶楼的“补充”部分。
第十一章:触发器的创建、注册事件、添加条件和动作

一个触发器的“事件”就是运行“动作”的引导,即只有遇到了指定“事件”魔兽才会去运行指定的“动作”;至于“条件”呢,就是在遇到“事件”后,魔兽还要经过“条件”的同意后才会运行“动作”,即“条件”的结果是true才行。

我们先做一个T:
  1. Unit
  2.     事件
  3.         玩家 - 玩家1(红色) 选择 一个单位
  4.     条件
  5.         (触发单位) 等于 农民 0001 <预设>
  6.     动作
  7.         游戏 - 对 玩家1(红色) 在屏幕位移(0.00,0.00)处显示文本: 选则了单位
复制代码

这个名叫“Unit”的T作用是:当玩家1选择了农民后,显示“选择了单位”。

现在转成J来看下:
  1. function Trig_UnitConditions takes nothing returns boolean
  2.     return ((GetTriggerUnit() == gg_unit_hpea_0001))
  3. endfunction

  4. function Trig_UnitActions takes nothing returns nothing
  5.     call DisplayTextToPlayer( Player(0), 0, 0, "TRIGSTR_008" )
  6. endfunction

  7. //===========================================================================
  8. function InitTrig_Unit takes nothing returns nothing
  9.     set gg_trg_Unit = CreateTrigger()
  10.     call TriggerRegisterPlayerSelectionEventBJ( gg_trg_Unit, Player(0), true )
  11.     call TriggerAddCondition(gg_trg_Unit, Condition(function Trig_UnitConditions))
  12.     call TriggerAddAction(gg_trg_Unit, function Trig_UnitActions)
  13. endfunction
复制代码

额。。。眼睛开始转圈了。。。。这是神马。。。。

做下深呼吸,镇静一下~
开始分析:
先看最上面那个函数,名字叫“Trig_UnitConditions”,发现中间的这个单词“Unit”跟我们T的标题一样,然后Unit之后的这个单词“Conditions”不认识。。去google,于是知道了是“条件”的意思(Condition是单数,后面加s是复数),剩下开头部分的“Trig_”,这是个前缀,是T自动生成的,“Trig”是“Trigger”(触发器,本意是扳机、触发)的缩写
于是这个就可以译为“触发器_Unit的条件”,先不管这个函数是干嘛的,看下一个。

发现名字类似,“Trig_UnitActions”中现在唯一不懂的是“Actions”,去查。。于是知道了是“动作”的意思。合起来就是“触发器_Unit的动作”。

什么啊……原来一个T里的条件和动作是分开的两个函数啊。
现在看最后一个函数的名字,“InitTrig_Unit”,去掉Init的话就是“触发器_Unit”了,那这个Init呢,就是“Initializer”(初始化)这个单词的缩写,于是合起来就是“初始化触发器_Unit”。

现在我们知道了,一个完整的触发器是由三个函数,或者说至少三个函数组成的。

这里就要先讲个概念了,“触发器”这个东西实际上也是数据的一种,数据类型是:trigger。也就是说一开始是没有触发器这个东西的,需要新建这个数据。

--------------------------------------------  初始化触发器  --------------------------------------------

我们一样样来对应:
既然说触发器需要新建,那么新建的触发器在哪?
我们看到,在InitTrig_Unit中,第一句话是:
set gg_trg_Unit = CreateTrigger()
等号后面这个CreateTrigger()就是J的本地函数之一,作用是新建触发器。
然后看前面,发现引导词“set”和一个“=”,那么gg_trg_Unit肯定是个变量了。
gg_”是一种WE自建“全局变量”的前缀,这个变量其实就是:
  1. globals
  2.     trigger gg_trg_Unit
  3. endglobals
复制代码


现在来看InitTrig_Unit里第一个被调用的函数:
发现名字超级长……
TriggerRegisterPlayerSelectionEventBJ
但是有件事很开心,这是用首字母大写的单词组成的,于是拆开:
Trigger(触发器)Register(注册)Player(玩家)Selection(选择)Event(事件)BJ(BJ函数)
PS:BJ函数这个先别管。。以后会提到。

到函数列表里查一下就知道需要哪些参数了,需要(触发器,玩家,布尔值)
也就是说需要一个可以被注册事件的触发器,然后需要一个选择单位的玩家,最后一个布尔值的意思就是是否选择了单位(true的话就是选择了,false就是取消选择)
于是,
TriggerRegisterPlayerSelectionEventBJ( gg_trg_Unit, Player(0), true )
可以理解为:给触发器gg_trg_Unit注册玩家1(Player(0))选择单位事件

--------------------------------------------  条件  --------------------------------------------

然后来看第二个调用的函数:
TriggerAddCondition
意思马上能看懂,就是“触发器加条件”,同样的,需要作为目标的触发器,另外需要一个条件。
TriggerAddCondition(gg_trg_Unit, Condition(function Trig_UnitConditions))
触发器是gg_trg_Unit,看下条件的写法,需要一个叫“Condition”的函数,这个函数的作用就是把一个code(代码)变成“条件”(代码!?其实就是函数的另一种叫法)
所以,在函数Condition里加入一个函数参数
:这个加入的函数必须没有参数!返回值必须是布尔值!因为是条件嘛。

于是回到最前面那个函数:
  1. function Trig_UnitConditions takes nothing returns boolean
  2.     return ((GetTriggerUnit() == gg_unit_hpea_0001))
  3. endfunction
复制代码

GetTriggerUnit(),拆开来看:得到 触发 单位
gg_unit_hpea_0001:这个是我在地图上放的农民,WE给他的编号是0001
这个函数就是返回(触发单位==农民0001)

--------------------------------------------  动作  --------------------------------------------

最后一个调用的函数不用说肯定是“动作”了:
TriggerAddAction(gg_trg_Unit, function Trig_UnitActions)
触发器是gg_trg_Unit
由于是“动作”所以直接运行函数Trig_UnitActions就行

下面是“动作”部分:
  1. function Trig_UnitActions takes nothing returns nothing
  2.     call DisplayTextToPlayer( Player(0), 0, 0, "TRIGSTR_008" )
  3. endfunction
复制代码

这个大家都能看懂吧,看不懂的去回顾下第九章吧~
但是很奇怪,T里面不是写的显示“选择了单位”吗?这里怎么变成"TRIGSTR_008"了?
好吧。。其实我也不知道,可能是WE自动把“选择了单位”这个string记录在“TRIGSTR”里,编号是008
这个不用管的,大家可以直接写成:
  1. function Trig_UnitActions takes nothing returns nothing
  2.     call DisplayTextToPlayer( Player(0), 0, 0, "选择了单位" )
  3. endfunction
复制代码


-----------------------------------------------------  总结  -----------------------------------------------------

一个完整的最基础的触发器需要:
1.    初始化触发器函数(此函数无需参数和返回值,在这个函数里需要新建触发器,并为这个新建的触发器注册事件,然后添加条件和动作)
2.    条件函数(无参数且返回值必须为布尔值)
3.    动作函数(无参数且无返回值,所有要做的事全在这个函数内完成,即使用调用函数的方式完成动作)
要注意这三个函数在J中的顺序:
由于初始化触发器中调用了“条件”和“动作”,所以,初始化触发器这个函数必须放在“条件函数”和“动作函数”的下面。

下一章节教大家如何自己写触发器。

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:29:43 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:30 编辑

注:如果J过长自动换行,请切换到论坛的宽版模式,不知道切换方法请看顶楼的“补充”部分。
第十二章:写自己的触发器

PS:以下写J的顺序是我自己写J的顺序,同学们不一定要仿照。


先看下我们要写个什么样的J:
触发器名字:Unit
事件:玩家1输入a
条件:农民0001是存活的(该农民是地图上事先放置的农民,0001是WE给这个农民的编号,大家放置的时候也许编号会不同)
动作:创建一个玩家1的步兵在地图中心。
好像没什么思路额。。。但是不管写什么触发都需要很关键的第一步,那就是初始化
:一个初始化的函数的名字必须带上“InitTrig_”这个前缀。
  1. function InitTrig_Unit takes nothing returns nothing
  2. endfunction
复制代码

:“InitTrig_”后面跟的名字必须跟该触发器的名字相同,否则无法初始化,也就是无法使用。意思就是:

如果你在这里写的触发器名字是XXX:
QQ截图未命名.png
那么就必须写成:InitTrig_XXX

第二步创建触发器
我们不再使用上一章中的全局变量触发器,改用局部变量;至于区别。。这我还真不知道,至少程序长了以后不用每次都拉到顶端写到globals里再拉回来。
  1. function InitTrig_Unit takes nothing returns nothing
  2.     local trigger trig = CreateTrigger()
  3.     set trig = null
  4. endfunction
复制代码

set trig = null 这一步就是将这个局部变量触发器清空,并不是销毁这个触发器,而是将这个变量清空,也是排泄的一种。(至于排泄的概念……已经有很多相关教程了,搜索下就有了;总之就是不排泄地图就会越玩越卡)
PS:null 就是“没有”的意思

第三步注册事件
我们的事件是玩家1输入a,于是在T里找到这个事件,然后转成J之后自己照着样子打一遍,不要复制黏贴,尽量看懂那个函数名:
  1. function InitTrig_Unit takes nothing returns nothing
  2.     local trigger trig = CreateTrigger()
  3.         call TriggerRegisterPlayerChatEvent( trig, Player(0), "a", true )
  4.     set trig = null
  5. endfunction
复制代码


第四步加入条件
  1. function InitTrig_Unit takes nothing returns nothing
  2.     local trigger trig = CreateTrigger()
  3.         call TriggerRegisterPlayerChatEvent( trig, Player(0), "a", true )
  4.         call TriggerAddCondition( trig, Condition( …… ))
  5.     set trig = null
  6. endfunction
复制代码

由于我们还没写条件函数,所以Condition()的括号中先空着

第六步加入动作
  1. function InitTrig_Unit takes nothing returns nothing
  2.     local trigger trig = CreateTrigger()
  3.         call TriggerRegisterPlayerChatEvent( trig, Player(0), "a", true )
  4.         call TriggerAddCondition( trig, Condition( …… ))
  5.         call TriggerAddAction( trig, …… )
  6.     set trig = null
  7. endfunction
复制代码

同样的,由于还没写动作函数,先空着

第七步写条件函数
我们的条件是:农民0001是存活的
在T中找到这个条件,由于是条件,所以肯定在“布尔值”里
转成J看懂后输入到我们的程序里:
PS:因为函数名只要对应就可以使用和调用,所以不用再加什么“Trig_”这种非常麻烦而且难看的前缀了,只要写成自己能看懂的名字就行了。
  1. function Unit_Condition takes nothing returns boolean
  2.     return IsUnitAliveBJ( gg_unit_hpea_0001 )
  3. endfunction

  4. function InitTrig_Unit takes nothing returns nothing
  5.     local trigger trig = CreateTrigger()
  6.         call TriggerRegisterPlayerChatEvent( trig, Player(0), "a", true )
  7.         call TriggerAddCondition( trig, Condition( function Unit_Condition ))
  8.         call TriggerAddAction( trig, …… )
  9.     set trig = null
  10. endfunction
复制代码

Condition( function Unit_Condition )中使用Unit_Condition函数之前要加function这个关键词。这里Unit_Condition函数是作为Condition()函数的code(代码)参数,所以要跟电脑说明Unit_Condition是一个function。基本上当一个函数作为code被直接使用时就要加上function这个词,code这种参数在函数列表里可以看到。
PS:code和function是同一个东西,不过由于J中函数的参数需要说明数据类型,所以用code,明显code比function要短很多~

最后一步写动作函数
在T中找到“单位-创建单位(面向角度)”这个动作(我用的是(可用地图区域的中心点),其它没变):
  1. 未命名触发器 005
  2.     事件
  3.     条件
  4.     动作
  5.         单位 - 创建 1 个 步兵 给 玩家1(红色) 在 ((可用地图区域) 的中心点) ,面向角度为 默认建筑朝向 度
复制代码

转成J后,发现是这么个东西:
  1. CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetRectCenter(GetPlayableMapRect()), bj_UNIT_FACING )
复制代码

这个函数会创建一个“点”(数据类型为point)在地图中心,然后创建1个步兵(‘hfoo’)到这个“点”,并且让这个步兵面朝(bj_UNIT_FACING)(与T对应后即可知道是默认建筑朝向)
但是创建完步兵这个“点”就没用了,需要删除(也就是排泄)。

所以这里推荐一个J的本地函数
  1. native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit
复制代码

参数分别是:玩家,单位的ID,坐标X,坐标Y,面朝方向
返回值是单位
由于地图中心的坐标是(0,0),且坐标是实数,无需排泄
所以动作函数可以写成:
  1. function Unit_Condition takes nothing returns boolean
  2.     return IsUnitAliveBJ( gg_unit_hpea_0001 )
  3. endfunction

  4. function Unit_Action takes nothing returns nothing
  5.     call CreateUnit( Player(0), 'hfoo', 0, 0, bj_UNIT_FACING )
  6. endfunction

  7. function InitTrig_Unit takes nothing returns nothing
  8.     local trigger trig = CreateTrigger()
  9.         call TriggerRegisterPlayerChatEvent( trig, Player(0), "a", true )
  10.         call TriggerAddCondition( trig, Condition( function Unit_Condition ))
  11.         call TriggerAddAction( trig, function Unit_Action )
  12.     set trig = null
  13. endfunction
复制代码


至此,一个完整的触发就写完了。

有的同学问:这个步兵的代码(‘hfoo’)只能写个T的动作再转换来查看吗?
答:可以直接在物体编辑器里查看。到“单位”里找到“步兵”,然后按Ctrl+D即可。
1.png
-------------------------------  Ctrl+D  --------------------------------
2.png
要还原的话再按下~


练习
用纯J(不要做完T转换哦~,不过允许不知道的函数名用T转换看)写一个完整的触发:
在地图上放一个农民一个步兵
事件:玩家1选择一个单位;
条件:被选择的是步兵
动作:如果农民被选择,显示信息:农民被选择
           如果步兵被选择,显示信息:步兵被选择

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:30:04 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:30 编辑

第十三章:单位组&玩家组

单位组这个数据类型在J中是:group(本意是组、团队)
玩家组是:force(本意是力量)
虽然同是“组”,中文可以用单位和玩家来区别,但是英文不行,写成unit_group和player_group的话不仅占地方而且写起来很麻烦。所以玩家组就用force这个词,在这里就是“势力”的意思。

这两个数据使用方法其实差不多,这里主要讲group的使用。

首先,如果什么都不做,魔兽中是没有group这个东西的,所以我们要做的第一件事是新建一个单位组
之前我们已经见过了新建单位的写法,新建单位组也是类似:CreateGroup();也就是凡是新建,基本都是用Create这个单词。

在J中,几乎所有的数据都可以当做变量,所以单位组的申明可以在globals里,也可以使用local:
  1. globals
  2.     group g=CreateGroup()
  3. endglobals
  4. 或者
  5. function G takes nothing returns nothing
  6.     local group g=CreateGroup()
  7.     set g=null
  8. endfunction
复制代码

:若以局部变量的方式,则记得要排泄。
PS:两种方式需要根据不同的情况灵活使用。

单位组的作用呢就是可以让我们只发布一次命令就让一堆单位按这个命令行动,而不再是对这些单位一个个发布命令。当然还有其它的好处,之后遇到的时候就会明白的。

先看下如何往单位组里加单位以及移除单位:
首先我们需要一个单位组:
  1. globals
  2.     group g=CreateGroup()
  3. endglobals
复制代码

于是我们就有了一个新建的空单位组。
  1. globals
  2.     group g=CreateGroup()
  3. endglobals

  4. function add_del takes nothing returns nothing
  5.     call GroupAddUnit( g, gg_unit_hpea0000 )
  6.     call GroupRemoveUnit( g, gg_unit_hpea000 )
  7. endfunction
复制代码

:gg_unit_hpea0000是已经在地图上放置的农民,大家放置时的编号可能不同。
add_del这个函数中调用的两个本地函数的意思就是为一个单位组添加/删除单位。
Add:加;Remove:移除、去除
这两个函数需要的参数相同,都需要一个已经存在的单位组,一个将被添加/删除的单位

但是添加单位这个函数的缺陷很明显,一次只能添加一个,如果有一群单位的话一个个添加就太繁琐了。
这种时候就要使用“选取范围内所有单位”这个函数:
  1. native GroupEnumUnitsInRange takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
复制代码

:看不懂函数名的同学请回顾前面几章,已经教了很多次翻译函数名的方法了,之后也不再逐一翻译了。
boolexpr filter:这个参数是布尔表达式,简单来说就是一个判断对错的函数。
这个本地函数的意思是往一个group里添加以(x,y)为圆心范围radius之内且符合filter的所有单位。

那如果不要条件呢?可以这么写:
  1. call GroupEnumUnitsInRange( group, 0, 0, 100, null )
复制代码

null在这里就表示无条件,也就是添加范围内的所有单位。

添加完单位后就需要对这个单位组里的单位发布命令了:
我们先转化“单位组-选取(单位组)内的单位做动作”这个T动作到J看下函数
  1. call ForGroupBJ(……)
复制代码

转化后发现是这么个东西,再到function list查一下,原来这个函数是调用了ForGroup这个函数,另外还有一个变量以及调用了DestroyGroup(删除单位组)这个函数。
也就是T中的这个选取单位组做动作在做完动作后直接删除了这个单位组,但是这么写其实会降低运行效率,因为多调用了一次ForGroup这个函数。反正这个函数也是调用了ForGroup和DestroyGroup,我们直接写这两个函数就行了,而且这样写的另一个好处是什么时候删除单位组是我们自己掌控的。
注:ForGroupBJ并不是无用,当需要选取比如某个玩家的所有单位做一次动作的时候用这个函数写起来就比较省力了。

ForGroup这个函数除了单位组外,还需要一个code,也就是我们需要另外写一个函数,其功能就是让这个单位组内的单位做动作。当然了,这个code是“被使用”,所以要放在前面。

先了解下下面这两个函数的作用,之后看实例来理解吧……
(其实是我没想到好的讲解方法。。)
这两个函数都不需要参数:
GetEnumUnit():相当于T中的“选取单位”,在选取单位组做动作时用来在动作函数中获取被选取的单位。
GetFilterUnit():相当于T中的“匹配单位”,在选取符合条件的单位添加到单位组时用来在条件函数中获取被选取的单位。

我们直接看一个实例:
山丘之王的雷霆一击大家都知道吧(不知道的同学请使用人族首发山丘之王学习第二个技能打一盘常规战……),现在我们想知道山王敲地板时有多少单位在该技能范围内(不包括友军)。
PS:暂时只考虑一级的雷霆一击,不然可能要用到还没学的东西。

先想下我们需要什么事件,既然是在山王施放雷霆一击时显示单位数,则我们需要山王开始放技能事件。不过由于山王不是一开始就放在地图上的,我们不能使用指定单位事件,只能使用任意单位事件。
触发器就叫“LTYJ”(Lei Ting Yi Ji)吧……
  1. function InitTrig_LTYJ takes nothing returns nothing
  2.     local trigger trig=CreateTrigger()
  3.         call TriggerRegisterAnyUnitEventBJ( trig, )
  4.         call TriggerAddCondition( trig, Condition( function ))
  5.         call TriggerAddAction( trig, function )
  6.     set trig=null
  7. endfunction
复制代码

:TriggerRegisterAnyUnitEventBJ就是“触发器注册任意单位事件”,不过这是个BJ函数。

在“物体编辑器”中找到“雷霆一击”,按Ctrl+D查看编号;或者在T中写一个含有该技能的动作然后转J查看。得到该技能的编号是:‘AHtc’。顺便看下一级雷霆一击的范围,是250码。

然后补上Condition:
  1. function LTYJ_Condition takes nothing returns boolean
  2.     return GetSpellAbilityId() == ' AHtc '
  3. endfunction

  4. function InitTrig_LTYJ takes nothing returns nothing
  5.     local trigger trig=CreateTrigger()
  6.         call TriggerRegisterAnyUnitEventBJ( trig, )
  7.         call TriggerAddCondition( trig, Condition( function ))
  8.         call TriggerAddAction( trig, function )
  9.     set trig=null
  10. endfunction
复制代码

GetSpellAbilityId()就是获取施放的技能的ID,无需参数,如果是T的话就相当于在“条件”中判断施放技能是否为是雷霆一击。

再补上动作部分,我们需要先选取山王周围250范围内的非友军单位(确定了“动作”的名字,就顺便把初始化函数里的事件条件动作也补上,我这里事件用的是“发动技能效果”):
  1. function LTYJ_Condition2 takes nothing returns boolean
  2.     return IsUnitEnemy( GetFilterUnit(), GetTriggerPlayer() ) == true
  3. endfunction

  4. function LTYJ_Action takes nothing returns nothing
  5.     local group g=CreateGroup()
  6.     local unit u=GetTriggerUnit()
  7.     call GroupEnumUnitsInRange( g, GetUnitX(u), GetUnitY(u), 250, Condition(function LTYJ_Condition2) )
  8. set g=null
  9. set u=null
  10. endfunction

  11. function LTYJ_Condition takes nothing returns boolean
  12.     return GetSpellAbilityId() == ' AHtc '
  13. endfunction

  14. function InitTrig_LTYJ takes nothing returns nothing
  15.     local trigger trig=CreateTrigger()
  16.         call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
  17.         call TriggerAddCondition( trig, Condition( function LTYJ_Condition ))
  18.         call TriggerAddAction( trig, function LTYJ_Action )
  19.     set trig=null
  20. endfunction
复制代码


接着就是显示单位组中的单位数量
有一个BJ函数叫CountUnitsInGroup,在T里就是获取“某单位组中的单位数量”,大家直接用这个就行,不过这里使用ForGroup,其实CountUnitsInGroup也调用了ForGroup。

我们先加上ForGroup,顺便先为其准备一个空代码:
  1. function LTYJ_Action2 takes nothing returns nothing
  2. endfunction

  3. function LTYJ_Condition2 takes nothing returns boolean
  4.     return IsUnitEnemy( GetFilterUnit(), GetTriggerPlayer() ) == true
  5. endfunction

  6. function LTYJ_Action takes nothing returns nothing
  7.     local group g=CreateGroup()
  8.     local unit u=GetTriggerUnit()
  9.     call GroupEnumUnitsInRange( g, GetUnitX(u), GetUnitY(u), 250, Condition(function LTYJ_Condition2) )
  10.     call ForGroup( g, function LTYJ_Action2 )
  11. set g=null
  12. set u=null
  13. endfunction

  14. function LTYJ_Condition takes nothing returns boolean
  15.     return GetSpellAbilityId() == ' AHtc '
  16. endfunction

  17. function InitTrig_LTYJ takes nothing returns nothing
  18.     local trigger trig=CreateTrigger()
  19.         call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
  20.         call TriggerAddCondition( trig, Condition( function LTYJ_Condition ))
  21.         call TriggerAddAction( trig, function LTYJ_Action )
  22.     set trig=null
  23. endfunction
复制代码


再往LTYJ_Action2中添加我们需要的动作,也就是数数(由于还没学过哈希表,所以用全局变量计数,若局部变量的话无法叠加,因为在一个函数里用完就自动被清除了);
最后添加显示最后的结果到ForGroup下面:
  1. globals
  2.     integer n=0
  3. endglobals

  4. function LTYJ_Action2 takes nothing returns nothing
  5.     set n = n+1
  6. endfunction

  7. function LTYJ_Condition2 takes nothing returns boolean
  8.     return IsUnitEnemy( GetFilterUnit(), GetTriggerPlayer() ) == true
  9. endfunction

  10. function LTYJ_Action takes nothing returns nothing
  11.     local group g=CreateGroup()
  12.     local unit u=GetTriggerUnit()
  13.     call GroupEnumUnitsInRange( g, GetUnitX(u), GetUnitY(u), 250, Condition(function LTYJ_Condition2) )
  14.     call ForGroup( g, function LTYJ_Action2 )
  15.     call DisplayTextToPlayer( Player(0), 0, 0, I2S(n) )
  16. set g=null
  17. set u=null
  18. endfunction

  19. function LTYJ_Condition takes nothing returns boolean
  20.     return GetSpellAbilityId() == ' AHtc '
  21. endfunction

  22. function InitTrig_LTYJ takes nothing returns nothing
  23.     local trigger trig=CreateTrigger()
  24.         call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
  25.         call TriggerAddCondition( trig, Condition( function LTYJ_Condition ))
  26.         call TriggerAddAction( trig, function LTYJ_Action )
  27.     set trig=null
  28. endfunction
复制代码

:别忘了把WE里触发器的名字改成初始化函数使用的名字,忘了的同学请回顾触发器写法那两章。

大家自己想几个简单的题目练习下吧,主要熟悉下GroupEnumUnitsInRangeForGroup这两个函数。
比如选取一定范围内的单位然后改变他们的移动速度,改变速度这个函数可以在T中“动作”的“单位”里找到,然后转J看下就知道了。

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:30:55 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:30 编辑

第十四章:注释 & CJ函数与BJ函数的区别&参数类型

所谓“注释”就是写给编程的人看的,电脑在运行时会直接无视之,引导符是“//”
  1. // 这里随便输入内容,切记在“//”之后的所有的内容都会变成注释
  2. // 注释需要换行的时候记得在开头补上个“//”
  3. 比如:
  4. //          雷霆一击
  5. function LTYJ takes ……
  6. endfunction
复制代码

用处大家应该也想到了,就是以后函数太多记不住哪个是干嘛的时候,用这玩意儿注明一下,具体的应用大家以后写J的时候自会想到~比如能标注参数等~
   

CJ和BJ函数呢最大的区别就在于BJ是CJ的复刻版。。。而且从某种意义上来讲还复杂化了……
比如“给(某个单位)添加(某个技能)”这个动作:
用T弄出来转J后发现是个BJ函数:
  1. function UnitAddAbilityBJ takes integer abilityId, unit whichUnit returns boolean
  2.     return UnitAddAbility(whichUnit, abilityId)
  3. endfunction
复制代码

但是这个函数本质上还是使用了UnitAddAbility 这个CJ函数,从效率上来讲,明显是直接使用原CJ函数更快,那为什么暴雪还要这么无聊地写个BJ函数出来?
以下仅我个人见解:
中文里我们可以说:给某个单位添加某个技能 / 添加某个技能给某个单位
两种都行,前者偏向“单位”,后者偏向“技能”
但在英文中,如果翻译以上两句的话:
For Unit, Add skill / Add skill for unit (额。。翻译得略为简陋。。能看就行了。。)
按英文语法来说,后者更通顺且能让读者更容易明白。

由于CJ函数是按照前者的顺序写参数的,考虑到英语为母语的WEER们看着不习惯,于是就写了套BJ函数出来。。不过对我们来说,反而是前者更合适,所以直接抛弃大部分BJ,用CJ,不仅效率比BJ高,而且读起来也顺。
   

现在大家看各种参数类型应该不会那么头疼了,反正本章也是过路的。。于是在这里贴出来:
boolean 布尔值destructable 可破坏物dialog 对话button 按钮texttag
漂浮文字
integer 整数
item 物品leaderboard 排行榜player 玩家force 玩家组location 位置(点)real 实数
rect 地区effect 特效string 字符串group 单位组timer 计时器unit 单位

:表格引用自acomc的文章。
以上这些是平时比较常用的,当然还有别的数据类型……
除了boolean、integer、real、string这四个最基础的类型,其它的都是句柄型(handle)的数据类型(“句柄型”这个概念大家暂时无视就好……单位就当单位类型,计时器就当计时器类型~),用作局部变量时在一般在一个函数的最后把这个变量清空(set x = null),用作全局变量的时候视情况而定。

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:31:15 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:30 编辑

第十五章:计时器

参数类型:timer
作用:倒计时,然后时间到了之后该干嘛干嘛……

玩家输入a,每过0.1s在屏幕显示数字1,2,3,4,……,n(n趋向无穷)

首先是初始化触发,由于不需要条件,于是就去掉TriggerAddCondition这个函数:
  1. function Num_Time takes nothing returns nothing
  2. endfunction

  3. function InitTrig_Num takes nothing returns nothing
  4.     local trigger t = CreateTrigger()
  5.         call TriggerRegisterPlayerChatEvent( t, Player(0), “a”, true )
  6.         call TriggerAddAction( t, function Num_Time )
  7.     set t = null
  8. endfunction
复制代码

让计时器运作,我们需要创建一个计时器
  1. function Num_ Time takes nothing returns nothing
  2.     local timer t = CreateTimer()
  3.     set t = null
  4. endfunction

  5. function InitTrig_Num takes nothing returns nothing
  6.     local trigger t = CreateTrigger()
  7.         call TriggerRegisterPlayerChatEvent( t, Player(0), “a”, true )
  8.         call TriggerAddAction( t, function Num_ Time )
  9.     set t = null
  10. endfunction
复制代码

之后使用TimerStart函数,设定时间、是否循环计时、及时间到了之后的运行的代码(注意是代码,无参):
  1. function Num_Action takes nothing returns nothing
  2. endfunction

  3. function Num_ Time takes nothing returns nothing
  4.     local timer t = CreateTimer()
  5.         call TimerStart( t, 0.1, true, function Num_Action )
  6.     set t = null
  7. endfunction

  8. function InitTrig_Num takes nothing returns nothing
  9.     local trigger t = CreateTrigger()
  10.         call TriggerRegisterPlayerChatEvent( t, Player(0), “a”, true )
  11.         call TriggerAddAction( t, function Num_ Time )
  12.     set t = null
  13. endfunction
复制代码

为这个触发准备一个整数变量,每次时间到了就加1,之后显示:
  1. globals
  2.     Integer i = 0
  3. endglobals

  4. function Num_Action takes nothing returns nothing
  5.     set i = i+1
  6.     call DisplayTextToPlayer( Player(0), 0, 0, I2S(i) )
  7. endfunction

  8. function Num_ Time takes nothing returns nothing
  9.     local timer t = CreateTimer()
  10.         call TimerStart( t, 0.1, true, function Num_Action )
  11.     set t = null
  12. endfunction

  13. function InitTrig_Num takes nothing returns nothing
  14.     local trigger t = CreateTrigger()
  15.         call TriggerRegisterPlayerChatEvent( t, Player(0), “a”, true )
  16.         call TriggerAddAction( t, function Num_ Time )
  17.     set t = null
  18. endfunction
复制代码

:全局变量的整数要有初始值,因为函数中是直接对其进行加1,若没有初始值似乎不能加。。。(额。。我没试过。。至少有些编程语言里不行)

计时器的用法就这么简单,如果要使计时器停止的话,目前就只能用全局变量和BJ函数的记录最后创建的计时器(这个说实话也是全局变量记录……),然后在需要暂停的时候,使用PauseTimer()这个函数。
当然能暂停就能继续,用ResumeTimer()

计时器销毁
既然计时器是游戏开始后创建的,自然跟点一样需要排泄比如上面这个函数,我想在i==10之后便停止这个动作,于是我们就需要捕捉到这个正在为这个触发计时的计时器。
获取这个计时器的语句是:GetExpiredTimer()
PS:为了缩短页面方便大家浏览,我把这个触发截掉了一部分,之后也是如此,不再提醒。
  1. function Num_Action takes nothing returns nothing
  2.     local timer t=GetExpiredTimer()
  3.     set i = i+1
  4.     if i != 10 then
  5.         call DisplayTextToPlayer( Player(0), 0, 0, I2S(i) )
  6.     else
  7.         set i=0  //顺便清0
  8.         call PauseTimer( t ) //推荐删除前先暂停,不然直接删除可能会有BUG
  9.         call DestroyTimer( t )  //这个语句就是用来删除计时器的
  10.     endif
  11. endfunction

  12. function Num_ Time takes nothing returns nothing
  13.     local timer t = CreateTimer()
  14.         call TimerStart( t, 0.1, true, function Num_Action )
  15.     set t = null
  16. endfunction
复制代码

注意,只能在计时器的对应函数中才能捕捉该计时器,就想捕捉触发单位一样,只有触发了事件的单位才会被捕捉到,且这个捕捉命令必须在这个触发的动作里才行,计时器也是一样,在TimerStart里调用的函数里才能捕捉这个计时器

当然,上面讲的仅仅是直接捕捉的方法,之后会讲到用储存的方法储存计时器(可以理解为每个计时器都有个编号,储存编号而已),然后可以在别的非对应的函数中读取该计时器并进行暂停等相关操作。

计时器的应用:
比如制作击退技能,开启计时器每过Xs使目标单位后退Y距离,后退Z次后删除计时器~

本章完~
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 04:32:10 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:31 编辑

第十六章:哈希表——哈希表专用魔免盾


哈希表说:“我有全地图范围的风暴之锤,来学我者必中!”
于是新手们望而却步……
某某人说:“慌什么?我给你们套魔免盾,你们尽管冲!”

魔免盾——开始施法:
想象我们有一个空的大箱子,很多很多新买的空本子,每本本子呢都有很多页,不过每一页都是白纸,什么都没有的白纸,除此之外呢,我们还有个永远不睡觉的管理员。

现在进入剧情模式
        我们大家都住在一起,每人每天都为自己设置了闹钟,比如早晨起床时床边有一个闹钟,比如厨房的微波炉自带一个闹钟。由于闹钟太多了,所以编下号码,比如起床的闹钟就编1号吧。
由于每人起床时间段不同,我们不想因为某些人需要早起,设置了很早的闹铃而影响了别的人,所以我们把闹钟放在屋外,并在前一天晚上,我们告诉那个管理员,明天这个1号闹钟响的时候,一定要把我们中的某些人叫醒,因为这些人要去学校早自习;然后当2号闹钟响的时候,叫醒另外一些人,因为那些人要去上班,剩下的懒虫不用管。
但是我们人很多啊,管理员表示记不住,于是他拿来一本空本子,在封面贴了个大大的标签“1”,表示这是1号闹钟的记录本;然后他翻开本子开始记录我们这些人,他在第一页写了数字1,然后记录了一个人(比如记录面貌名字等);然后翻了一页写了个页码2,又记录了一个人,直到他记录完所有的需要在第一个闹钟响的时候起床的人。记完之后他就把那个本子放进箱子里,因为箱子外的本子太多了,怕弄混。
        随后他又以相同的方式在贴了标签“2”本子内记录了需要在2号闹钟响的时候起床的人,然后放进箱子,并把两本本子按顺序放好。
        第二天,1号闹钟响了,于是,箱子管理员从箱子里拿出封面贴着“1”的本子,翻到第一页后开始找对应的人,找到后就把他弄醒并告诉他需要做的事情。叫完所有人之后管理员发现这本子已经不需要了,于是他撕下标签“1”后就把本子扔进了垃圾桶。(好浪费本子……同学们要珍惜资源,不要学他,但是标签“1”可能还有用,因为1号闹钟可能还要用来计时,所以先留着)为什么要扔掉本子呢?因为没用的本子多了箱子会变重嘛~管理员也是人,本子太多了会搬不动箱子的。
        然后第二个闹钟又响了,管理员便找来标签“2”的本子开始找人,找完后撕下标签把本子扔了。
        于是,这三批人就该干嘛干嘛了~
魔免盾——施法结束

当然,并不是套了魔免盾就能完全抵抗强大的风暴之锤了,你们还需要另外一样东西,就是动脑技能。不过这技能是你们自带的。。。某某人是教不了你们的……

魔免盾施法完了~
PS:请接着看十七和十八章,不然魔免盾的功效到期后就要重新来套。。
回导航楼
回复

使用道具 举报

发表于 2011-7-12 07:25:11 | 显示全部楼层
强贴强力支持
回复

使用道具 举报

 楼主| 发表于 2011-7-12 07:36:18 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:31 编辑

第十七章:哈希表(Hashtable)——基础语句

PS:请结合十六章一起看。
其实呢~ 如果你被套了魔免盾后没有出现排斥现象的话~
那么恭喜这位同学,你已经会用哈希表在一个函数里绑定单位到计时器,并在另一个函数里等计时器到期后读取被绑定的单位并命令这个单位做动作了。当然了,还不止~ 你连哈希表的排泄都已经学会了~
好吧。。要是光看那个就能学会大家也不用那么辛苦了。。

现在让我们将第十六章中的故事翻译成J:
首先要理解下什么是哈希表:那个装本子的箱子就是哈希表,那些本子贴的标签叫哈希表的主目录(也有叫父目录之类的),本子的页码叫哈希表的子目录,最后每页记录的人名就是被记录在哈希表的主目录中的子目录中的值了。
至于管理员呢。。就是电脑

比如说我们在地图里放了个单位名字叫A(A就是这个单位的全局变量名,放到地图上WE会自动为其编个号)
然后试着用哈希表完成下面这三件事:
1.    将A存到一个哈希表的1号主目录1号子目录下
2.    从这个哈希表的1号主目录1号子目录中读取A
3.    将A从这个哈希表的1号主目录1号子目录中删除

第一步,我们需要初始化一个哈希表并为其取个名字:
  1. globals
  2. hashtable ht=InitHashtable()
  3. endglobals
复制代码

初始化哈希表一般就用全局变量初始化一个就够了,要存不同的东西完全可以存在一个哈希表的不同目录下。

然后就是储存这个单位:
  1. globals
  2. hashtable ht=InitHashtable()
  3. endglobals

  4. function Save takes unit A returns nothing
  5.     call SaveUnitHandle( ht, 1, 1, A )
  6. endfunction
复制代码

额。。SaveUnitHandle()这个函数不是翻翻字典就能理解的,所以我解释下,前面两个单词很好理解,Save(储存)和Unit(单位);关键是最后一个单词,Handle(手柄、把柄),在第十四章最后部分有提过,除了integer、real、boolean、string之外,都是指针型的数据,所谓指针型数据都是只有一个整数作为地址,然后这些数据就像个路标指针一样指向这个地址,所以叫指针型数据(比如一个农民,其实并没有“农民”这样的数据,而是以一个整数作为数据,然后这个农民就是指向这个整数的指针)

那么这里Handle的意思就是指这个单位的整数地址。
顺带一提,即使数据类型相同,比如都是单位,但是不同的单位整数地址也是不同的。

要举一反三就很简单了,比如储存特效X:
  1. call SaveEffectHandle( ht, 1, 1, X )
复制代码

如果不是指针型的呢,去掉Handle就行了,比如储存实数8.0
  1. call SaveReal( ht, 1, 1, 8.0 )
复制代码


第二步读取:
就是在函数中使用LoadUnitHandle()等这种函数,但是一般读取出来为了方便使用,就直接用变量赋值。
  1. function Load takes nothing returns nothing
  2.     local unit u=LoadUnitHandle( ht, 1, 1 )
  3. endfunction
复制代码

在这个函数中就讲ht中主目录1的子目录1的单位地址读取出来,但后赋值给局部变量u
虽然单位地址之前说过是整数,但是那只是个地址,实际读取出来的是这个单位。

最后一步就是删除哈希表里这个储存的单位了:
  1. call RemoveSavedHandle( ht, 1, 1 )
复制代码

既然单位是指针型数据,也就是有handle,那么管它是什么数据的handle,反正都是handle,直接删handle就行,其它的不是handle型的数据,就要区别开来。
  1. call RemoveSavedInteger( ht,1,1 )
  2. call RemoveSavedReal( ht, 1, 1 )
  3. call RemoveSavedString( ht, 1, 1 )
  4. call RemoveSavedBoolean( ht, 1, 1 )
复制代码


删除一个主目录下所有的子目录:
  1. call FlushChildHashtable( ht, 1 )
复制代码

也就是把主目录1的所有子目录删除了

如果要清理整个哈希表的话,也就是将所有的主目录删除:
  1. call FlushParentHashtable( ht )
复制代码


紧接十八章……
回导航楼
回复

使用道具 举报

 楼主| 发表于 2011-7-12 07:51:13 | 显示全部楼层
本帖最后由 chyj4747 于 2014-11-24 14:31 编辑

注:本章于2011/08/16补充了StringHash的使用方法。
第十八章:哈希表——基础应用

现在我们来看下十六章中的剧情弄成J到底是怎么样的。
先假设事件为任意单位使用“睡眠”技能后触发,函数就叫F吧,技能目标叫A吧:
  1. function F_Act takes nothing returns nothing
  2.     local unit A=GetSpellTargetUnit()
  3.     set A=null
  4. endfunction

  5. function F_Cond takes nothing returns Boolean
  6.     return GetSpellAbilityId() == “睡眠”
  7. endunction

  8. function Init_F takes nothing returns nothing
  9.     local trigger trig=CreateTrigger()
  10.         call TriggerRegisterAnyUnitEvent( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
  11.         call TriggerAddCondition( trig, Condition(function F_Cond) )
  12.         call TriigerAddAction( trig, function F_Act )
  13.     set trig=null
  14. endfunction
复制代码

PS:还是再提醒下吧,之后我就把没改变的函数截掉了。

显然需要个计时器:
顺便简化下动作部分,就事件触发3s后叫醒A这个单位好了
  1. function F_TimeAct takes nothing returns nothing
  2. endfunction

  3. function F_Act takes nothing returns nothing
  4.     local timer t=CreateTimer()
  5.     local unit A= GetSpellTargetUnit()
  6.         call TimerStart( t, 3, false, function F_TimeAct )
  7.     set t=null
  8.     set A=null
  9. endfunction
复制代码

同学们应该发现了,这个F_TimeAct是“动作”或者说“代码”,也就意为着不能有参数,那么用局部变量记录的单位A无法用传参的方式从F_Act传递到F_TimerAct(注意局部变量只能在一个函数内部使用,别的函数无法使用的,要用的话就必须使用某种手段传递过去),有的同学说用全局变量就好了啊,的确能解决问题,但是注意全局有可能会造成多人使用时变量冲突,尤其是在做技能的时候,一个技能若被多次使用,那么这个全局变量会被最后使用的技能覆盖,也就造成了变量冲突,使用局部变量就能简单有效地避免这样的问题。

虽然这个单位变量无法传递,但是这个计时器却可以传递:
  1. function F_TimeAct takes nothing returns nothing
  2.     local timer t=GetExpiredTimer()
  3.     set t=null
  4. endfunction
复制代码

那么有什么方法能使单位随着这个计时器一起传过去吗?
这就要用哈希表把单位A和这个计时器绑定了,换成哈希表的术语说,就是把这个单位A储存在计时器的整数地址主目录下。

估计某些同学忘了,于是复习一下,除了基础数据外的数据都是指针型数据,都有个整数地址,计时器也是个数据类型,自然也有整数地址。
也就是先在F_Act里记录,之后便可以在F_TimeAct里读取,相当于管理员在闹钟响后取出对应闹钟编号的本子查看人名一样。

但是问题又来了。。如何获得计时器(或者其他指针型数据)的整数地址。。
这就要用到GetHandleId()这个函数了,所有的指针型数据都可以用这个来获取整数地址。
记得初始化哈希表~
  1. globals
  2.     Hashtable ht=InitHashtable()
  3. endglobals

  4. function F_TimeAct takes nothing returns nothing
  5.     local timer t=GetExpiredTimer()
  6.     set t=null
  7. endfunction

  8. function F_Act takes nothing returns nothing
  9.     local timer t=CreateTimer()
  10.     local unit A= GetSpellTargetUnit()
  11.     local integer i=GetHandleId( t )
  12.         call TimerStart( t, 3, false, function F_TimeAct )
  13.         // 被储存的是单位不是计时器,计时器只是作为主目录编号
  14.         call SaveUnitHandle( ht, i, 1, A )
  15.     set t=null
  16.     set A=null
  17. endfunction
复制代码

之后在F_TimeAct中读取这个单位,当然了,还要再获取次这个计时器的地址:
  1. globals
  2.     Hashtable ht=InitHashtable()
  3. endglobals

  4. function F_TimeAct takes nothing returns nothing
  5.     local timer t=GetExpiredTimer()
  6.     local integer i=GetHandleId( t )
  7.     local unit u=LoadUnitHandle( ht, i, 1 )
  8.     set t=null
  9.     set u=null
  10. endfunction

  11. function F_Act takes nothing returns nothing
  12.     local timer t=CreateTimer()
  13.     local unit A= GetSpellTargetUnit()
  14.     local integer i=GetHandleId( t )
  15.         call TimerStart( t, 3, false, function F_TimeAct )
  16.         // 被储存的是单位不是计时器,计时器只是作为主目录编号
  17.         call SaveUnitHandle( ht, i, 1, A )
  18.     set t=null
  19.     set A=null
  20. endfunction
复制代码

这样单位A就从F_Act传到F_TimeAct了,最后就是在F_TimeAct中写让A做的动作,比如要叫醒A,那么就使用UnitWakeUp()这个函数。。额。。不过这个函数不会影响催眠魔法效果(我没有试过,所以不是很清楚,想知道的同学可以自己试试~)
记得计时器的排泄~(这里的计时器是一次性的,所以不用先暂停以避免BUG了)
  1. function F_TimeAct takes nothing returns nothing
  2.     local timer t=GetExpiredTimer()
  3.     local integer i=GetHandleId( t )
  4.     local unit u=LoadUnitHandle( ht, i, 1 )
  5.         call UnitWakeUp( u )
  6.         call DestroyTimer( t )
  7.     set t=null
  8.     set u=null
  9. endfunction
复制代码


最后的最后,就要对哈希表进行排泄了,记得那个管理员把第一批人叫醒后做了什么吗?
撕下“1”这个标签,把本子扔了……
也就是把计时器整数地址的子目录全部清除了,因为之后绑定到闹钟1上的人可能不同了,虽然也可能重复,或者说可以新数据覆盖旧数据,但是一直放在箱子里很占重量的……
有人想偷懒不排泄,于是不服:一本本子能有多重?
那如果有十万本呢?并且其中99999本没用呢?

所以排泄有益于减少内存的占用,提高运行速度~
PS:一般比较常用的是清除一个主目录下的所有子目录,子目录里的数据一般都是被新数据覆盖的,不用替换数据前特地清除一下
  1. function F_TimeAct takes nothing returns nothing
  2.     local timer t=GetExpiredTimer()
  3.     local integer i=GetHandleId( t )
  4.     local unit u=LoadUnitHandle( ht, i, 1 )
  5.         call UnitWakeUp( u )
  6.         call DestroyTimer( t )
  7.         call FlushChildHashtable( ht, i )
  8.     set t=null
  9.     set u=null
  10. endfunction
复制代码

之前忘说了于是补上:每个子目录只能记录一个数据。

举个实例的思路吧:
比如击退,施放技能后局部变量获取被击退目标,记作u,绑定u到一个计时器t,然后开启计时器t每X秒运行另一个函数F;
F中获得到期的计时器t,读取绑定的单位u,移动u,用一个整数i记录移动的次数,也就相当于t的循环次数,当达到一定值后(用条件判定式判断)停止t,删除t,清空哈希表中储存在主目录t下的所有子目录。

小误区
有的同学可能觉得把计时器的地址作为子目录也行,主目录用普通整数(1,2,3,……),是没问题,但是存多个单位时就要改变主目录或者让计时器的地址做算术运算,对于前者,清理的时候要清除主目录而不是子目录,也就会把其它储存的主目录也删掉,自然不行;对于后者。。不仅麻烦,也可能会造成主目录冲突,因为主目录是用数字,而触发做多了之后又记不得哪个用的数字1,哪个用的数字2,万一不小心弄了两个一样的主目录,就冲突了,而且这问题排查的时候还很麻烦,但是用数据的地址就不同了,因为每个数据即使类型相同,地址也是不同的。

补充:
      如果要以一个string作为主目录储存怎么办?因为目录必须都是整数,所以字符串明显不能作为目录嘛……这时就需要用到StringHash这个东西,这个函数能把一个string转成一个整数地址,当然,相同的string得到相同的整数,不同的自然不同~
       比如说StringHash("a")就把a这个字符串转成了一个整数地址,具体是多少我没研究过不太清楚。。想知道的同学可以用 I2S 这个函数把得到的整数再转成string后看一下。
      用法非常简单,之前把单位绑计时器的时候是GetHandleId( GetExpiredTimer() )作为主目录,如果要把这个单位绑到“a”这个string上呢就StringHash( "a" )作为主目录即可~

现在同学们可以去看下血戮魔动冰前辈的哈希表教程了,链接在顶楼,虽然可能还是会晕 ^_^

本章完~
回导航楼
回复

使用道具 举报

发表于 2011-7-12 09:02:36 | 显示全部楼层
前两张无压力...看第三张开始啊...
回复

使用道具 举报

 楼主| 发表于 2011-7-13 05:03:35 | 显示全部楼层
后来想了下,如果大家现在就有意见或建议还是稍微回下吧……免得到时候全部弄完之后全部修改就很累了。。。
回复

使用道具 举报

发表于 2011-7-13 06:31:56 | 显示全部楼层
重点说说哈希表的使用与勾股定理以鼓励初学者做位移技能
我当时想学JASS就是想让技能多人使用
回复

使用道具 举报

 楼主| 发表于 2011-7-13 07:35:05 | 显示全部楼层
引用第23楼土包子于2011-07-13 06:31发表的  :
重点说说哈希表的使用与勾股定理以鼓励初学者做位移技能
我当时想学JASS就是想让技能多人使用
收到……勾股定理这个不属于编程本身的范畴了。。。那样的话就要去教小学初中的数学了。。。这个只能靠学JASS的人本身的数学程度了,这篇教程的主要目的是教会新人们JASS,不是制作中的各种算法,哪怕是最简单的算法。

其它建议已采纳~

谢谢~
回复

使用道具 举报

发表于 2011-7-13 07:49:51 | 显示全部楼层
多练习使用那个可以有效提升计算机语言的熟悉能力
入门jass和trigger没有差别啊,一般想学JASS的都是已经理解到trigger的软肋
回复

使用道具 举报

 楼主| 发表于 2011-7-13 08:47:34 | 显示全部楼层
引用第25楼土包子于2011-07-13 07:49发表的  :
多练习使用那个可以有效提升计算机语言的熟悉能力
入门jass和trigger没有差别啊,一般想学JASS的都是已经理解到trigger的软肋
跟我的原想法相差较远。。。我的本意是教会没有学过触发器的或者说仅仅了解了点T的皮毛的人。。。

不过此问题已解决~
我的方法是在导航中设置两个不同的导航,一个是专为新人的,另一个是挑出那些讲解比T要强大的功能的帖子
于是这个果断坑掉了……
回复

使用道具 举报

发表于 2011-7-13 10:26:54 | 显示全部楼层
这个教程比起我见过的其他语言系统化的教程差别很大。不过还是支持下,因为确实是新手向......
回复

使用道具 举报

 楼主| 发表于 2011-7-13 10:49:41 | 显示全部楼层
好吧。。。这一楼给宏……
回复

使用道具 举报

发表于 2011-7-14 15:24:38 | 显示全部楼层
这年头还有人写教程。。那就必须强烈支持一个!
回复

使用道具 举报

发表于 2011-7-15 10:08:34 | 显示全部楼层
其实你只要教存储就很好了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-11-21 18:43 , Processed in 0.128837 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表