请选择 进入手机版 | 继续访问电脑版

GA地精研究院

 找回密码
 立即注册
楼主: chyj4747

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

[复制链接]
 楼主| 发表于 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 | 显示全部楼层
其实你只要教存储就很好了
回复 支持 反对

使用道具 举报

发表于 2011-7-15 10:17:13 | 显示全部楼层
小点子看起来很会JASS的样子
虽然我看不懂,但是总觉得好厉害啊
求教我
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-15 11:11:09 | 显示全部楼层
引用第30楼·于2011-07-15 10:08发表的  :
其实你只要教存储就很好了
教储存?神马储存?。。。
HT吗?
回复 支持 反对

使用道具 举报

发表于 2011-7-15 11:31:10 | 显示全部楼层
对于我这种一看英语就晕的人 ....
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-15 11:37:05 | 显示全部楼层
引用第33楼y329987311于2011-07-15 11:31发表的  :
对于我这种一看英语就晕的人 ....  
耐下心慢慢看嘛~基本上所有单词意思我都写过了~
回复 支持 反对

使用道具 举报

发表于 2011-7-15 11:48:20 | 显示全部楼层
引用第31楼土包子于2011-07-15 10:17发表的  :
小点子看起来很会JASS的样子
虽然我看不懂,但是总觉得好厉害啊
求教我
。。。。。。
回复 支持 反对

使用道具 举报

发表于 2011-7-15 14:07:45 | 显示全部楼层
话说我看到库和域了。。那求顺带的写个zinc的教程吧 重点说说语法变化吧。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-15 21:29:11 | 显示全部楼层
引用第36楼月无痕于2011-07-15 14:07发表的  :
话说我看到库和域了。。那求顺带的写个zinc的教程吧 重点说说语法变化吧。。
我可以说我没学过zinc吗。。。。

如果需要的话,我看看写完这篇教程后如果我有空+如果我原意的话。。我就花个2-4天学一下。。
回复 支持 反对

使用道具 举报

发表于 2011-7-16 09:17:38 | 显示全部楼层
chyj4747你学过哪些语言
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-16 09:45:26 | 显示全部楼层
引用第38楼·于2011-07-16 09:17发表的  :
chyj4747你学过哪些语言
你是指别的编程语言吗?。。。都只学过些皮毛……
scheme和python

当然还有J~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2019-8-25 19:06 , Processed in 0.050854 second(s), 14 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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