|
楼主 |
发表于 2007-11-10 07:44:19
|
显示全部楼层
================= 范例 1==========
前面已经说过,function是线性运行的,而trigger是并行运行的,那么假如我在做一个既能的时候,想要生成一个范围内的效果。但是如果在程序中用PolledWait执行的时候会发现,实际上的时间没法和真实的技能效果时间对应上,那么怎么解决呢?
一般的方法是另外做一个trigger用来产生效果,在技能触发了t之后,使用来显示效果的t生效,然后再在本段t中进行伤害,到了一定时间之后在将那个效果t禁止。
用j的话,我们可以利用trigger的工作原理,将两段代码合并在一起。
首先,我们需要一个使function立即执行并不等待function结束的功能,下面介绍这个功能的实现:
jass: Copy code
// 参数用来传递一个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有返回类型:
jass: Copy code
//传递一个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的效果:
jass: Copy code
//用来产生效果,围绕一点(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
//用于在技能中拿来RunFunctionAsTrigger的
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放到里面,看看效果如何了:)
==================范例 2 ===============
在这个范例中,我将带大家实现一个多重物品栏的功能。
准备知识:
在war3种,blz为每个unit和item都提供了一个integer型的UserData,让各位mapper可以很方便的做一些事情(有点像windows标准控件的tag属性)。
思路:
首先是要在hero使用技能的时候触发这个trigger,然后把hero身上的item移走物品,下次的时候再把物品取回来,所以我们要给物品创建一个数组来保存。一个物品栏有6个物品,每个hero有n1个物品栏,又有n2个hero,可是jass只提供了1维数组,怎么做呢?聪明的你一定想到了x*a*b+y*b+z的办法,的确这样,我们就给每个hero一个特定的整数标志,每个物品栏一个数字,这样就可以了。下面是利用unit的userdata的实现方法。这个方法共支持8个物品栏
全局变量:
jass: Copy code
integer udg_iUnitIndex //用来使每个unit有独特的id
item array udg_itemGoods //用来存取所有的物品栏中的物品
函数:
jass: Copy code
//unit物品栏ID:使用userdata的1-3 bit;共支持2^3=8个物品栏,如果想再多的话,就把里面的数字乘以2就可以得到16个了
//用来读取物品栏id:
function fiGetUnitDataGoodsID takes unit whichUnit returns integer
//由于只是用0~7这几个数,所以我们只要去modulo(模,可理解为余数)就可以了
return ModuloInteger( GetUnitUserData( whichUnit ), 8 )
endfunction
//用来存放物品栏id:
function pSetUnitDataGoodsID takes unit whichUnit, integer newData returns nothing
local integer it = GetUnitUserData( whichUnit )
//新的id只要用newData再加上原来的数字除以8的整数部分乘以8就够了
call SetUnitUserData( whichUnit, ( it/8*8 + newData )
endfunction
//Unit - UnitID:7~ bit;用来记录单独的Unit
function fiGetUnitDataID takes unit whichUnit returns integer
//只要整数部分就可以了
return GetUnitUserData( whichUnit )/8
endfunction
function pSetUnitDataID takes unit whichUnit, integer newData returns nothing
local integer it = GetUnitUserData( whichUnit )
//将新的数字乘以8之后,再加上余数部分就可以了
call SetUnitUserData( whichUnit, newData*8+ModuloInteger( it, 8 ) )
endfunction
下一步,我们要设定一个全局变量,在每个hero出现的时候,加上这么两句:
jass: Copy code
set udg_iUnitIndex = udg_iUnitIndex + 1
call pSetUnitDataID( udg_iUnitIndex )
然后是unit切换物品栏的action:
jass: Copy code
//得到当前的unit
local unit u = GetTriggerUnit()
//得到下一个物品栏的id,由于只是0~7这几个数字,所以只要在每次+1之后取除以8的余数就可以了
local integer index = ModuloInteger(fiGetUnitDataGoodsID(u)+1,8)
//得到userdata,由于这个data已经有独立的unit的id和item的id,所以不用分别取了
local integer id = GetUnitUserData(u)
local integer i = 0
local integer it
//保存当前身上的物品,并丢在隐藏的地方
loop
exitwhen i>5
set it = id*6+i
//设定物品为当前物品栏第几栏的物品
set udg_itemGoods[it] = UnitItemInSlot(u, i)
//移到地图隐蔽处
call SetItemPositionLoc(udg_itemGoods[it],udg_locItemTemp)
call TriggerSleepAction(0)
//使物品无敌
call SetItemInvulnerable(udg_itemGoods[it],true)
//使物品不可见
call SetItemVisible(udg_itemGoods[it],false)
set i = i + 1
endloop
//设定下一个物品栏标号
call pSetUnitDataGoodsID(u,index)
set id = GetUnitUserData(u)
set i = 0
//取回物品
loop
exitwhen i>5
set it = id*6+i
//使物品可见
call SetItemVisible(udg_itemGoods[it],true)
//使物品不无敌
call SetItemInvulnerable(udg_itemGoods[it],false)
call TriggerSleepAction(0)
//放回物品栏
call UnitAddItem(u,udg_itemGoods[it])
set i = i + 1
endloop
* 补充: ModuloInteger是bj函数:
jass: Copy code
function ModuloInteger takes integer dividend, integer divisor returns integer
local integer modulus = dividend - (dividend / divisor) * divisor
if (modulus < 0) then
set modulus = modulus + divisor
endif
return modulus
endfunction
前面介绍过if语句的速度要比运算满很多,因为我们这个函数里面不涉及到负数,所以在这里我们可以自定义一个自己的函数:
jass: Copy code
function MyModuloInteger takes integer dividend, integer divisor returns integer
return dividend - (dividend / divisor) * divisor
endfunction
3. 指针变量
在jass中,有50多个继承自handle类型的指针类型变量。按照习惯handle翻译成句柄,但是我觉得这里的句柄不是太符合句柄的含义,还是用更根本的pointer更适合,所以我就直接称之为指针类型了。
前面已经说过,指针用来指向内存中的一块内存区域,这块区域有可能是个自定义类型的变量,有可能是一个function(code),有可能指向一个复合的单位(如unit,包括在we中看到的所有数据,这种类型应该是war3中最复杂的)……
在使用指针类型的时候,我们不能进行数学运算,只能通过API函数来存取。而在war3中,所有的单位都是只给一个句柄,如unit、item、region、camera……在给指针类型赋值的时候,大多数的函数过程是这样的:
首先分配给这种指针类型所指向的实体类型申请一块内存空间,然后把照着参数把相应的资料给这个实体变量,然后再返回这个变量在内存中的地址,也就是指针变量,最后这个被赋值的变量指向那块内存地址。如果在一段函数中使用了指针类型得到了一个值,使用完之后不做任何处理的话,那块被占用的内存虽然不会再使用了,但是由于前面已经申请了,所以系统会认为这块内存是被占用了的,不会再动用这块内存空间,这就是所谓的内存泄漏问题。
t虽然能够完成绝大部分的功能,但是对于内存处理这部分却是无能为力。例如存在最严重的就是location变量的内存泄漏问题,虽然一次才8个字节,但是常常我们一次性就使用了大量的location,导致时间越长,内存泄露越多。用j的话,就一定要记住:
释放内存!
什么样的变量需要释放内存呢?我们可以在je的搜索栏输入destroy和remove,destroy中的绝大部分都是需要释放内存的(也有少部分一旦释放,就是去了我们原来想要的效果了,例如fogmodifier类型),remove中location是一定要释放的,region和rect就要根据具体情况了,否则也会跟fogmodifier出现一样的情况,这些东西就要靠经验了。
最后要说的是,既然指针变量要用API,那么应该怎样得到自己想要的函数呢?最好要先对jass的API有一定的了解,例如soarchin写的API函数介绍(本教程不会提供jass的API函数介绍)。在最开始使用的时候,可以利用we的t,现做一个自己想要的语句,然后转成j,把语句拿过来用——不过要注意的是,trigger编辑器使用的绝大部分是bj里面的函数,最明显的是带有BJ后缀或者Swap(ped)后缀,一般都会有对应的cj函数,而两者几乎无差别(这个最好用jw来看)。等熟练一些之后,你就会发现一些blz的命名规则了,如判断一个unit,就是IsUnit***,得到unit的一个什么东西,就GetUnit****,设置就是SetUnit***,想要得到一个group,就GetUnits***,得到force就GetPlayers***……总之这个要靠经验的,旁边开着个金山词霸,基本上就能把一个函数的功能猜个八九不离十。当然,最好还是看看jass API的中文介绍,这个最保险,然后可以照着别人的jass学习。了解了jass的动作原理后,你会发现通过jass来学习别人的东西要比看别人的trigger来学习快很多——我经常是把t的演示先转成j然后再看的。
-----------------------------------------
第三章结束
本章剖析了jass的工作原理,熟练掌握了这三章的内容,实际上你就已经是个jass高手了,剩下的只差实际做几个东西的经验和对API函数的了解和熟练程度了。 |
|