学过编程的人就不用看了(除非是只学过那种老式的用行号的basic的)
我先来举个例子吧,假如有个t,触发的事件是一个单位死亡(any unit dies),在这个map中有一个string型的全局变量s,t的事件如下(用j来写了,语句前是注释):
function ****** takes nothing returns nothing
//申请一个force型局域变量,并设置成死掉的单位的玩家
local force foc = GetOwningPlayer(GetTriggerUnit())
//设置s为死掉的单位的名字——位置A
set udg_s = GetUnitName(GetTriggerUnit())
//等待30秒的游戏时间(Wait-Game Time)
call PolledWait(30)
//在屏幕上向foc指向的玩家输出死掉的单位的名字——位置B
call DisplayTextToForce(foc,"A "+udg_s+" has been slain.")
//释放内存
call DestroyForce(foc)
set foc = null
endfunction
下面来假设一个情节,首先是玩家1(player 1)的一个步兵(footman)被杀了,过了10秒player 2的一个食尸鬼(Ghoul)被杀了,结果将是:
在footman死后30s,player 1收到一条信息:A Ghoul has been slain.
在Ghoul死后30s,player 2收到一条信息:A Ghoul has been slain.
我来说明一下这个t的执行过程,当footman被杀的时候,t被触发了(t1),过了10s,这个t再次被触发(t2):
1.t1:set udg_s = "Footman",给局域变量foc分配了一块内存空间,并且set foc = player1
2.t1:开始wait
3.t2:在t1 wait了10s的时候被触发,set udg_s = "Ghoul",给局域变量foc分配一块内存空间,并且set foc = player2
4.t2:开始wait
5.t1:又wait了20s,这时t2已经wait了10s
6.t1:向foc(player1)输出udg_s的内容"Ghoul"
7.t2:又wait了20s后,向foc(player2)输出udg_s的内容"Ghoul"
看完这个过程后就大概明白了局域变量与全局变量的差别了吧?
在trigger editor中,全局变量是在菜单中编辑的(或者是“x”的图标)做的,在地图编译后,是在.j文件的最开始声明:
globals
unit udg_**** = null
......
endglobals
而局域变量,是指在每个t的最开始的位置声明:
function ***** takes ***** returns *****
local unit *** = *****
.....
endfunction
全局变量对所有的t过程都可见,所有的t访问一个全局变量的时候,实际上访问的都是同一个内存地址。局域变量在t的开始时,通过声明语句划分内存空间,一个t每次触发的时候都会为局域变量划分内存空间,局域变量仅对本次的执行过程可见;也就是说,同一个t同时执行了几个进程时,相同名称的局域变量有不同的内存地址,每个进程只能访问该进程申请的内存地址,无法访问其他进程的内存地址;在每个进程结束之后,局域变量的内存空间会被释放,再也无法得到这个变量的值
从编程的角度来说,局域变量是在过程结束的时候,除了使用了指针并为指针指向的地址划分了新的内存空间,所有的局域变量都是自动释放内存空间的,但war3的似乎没有那么高级,所有的指针变量都不但需要最后Destroy,而且还要set =null(最后这个让我费解了好久,后来人家说blz自己的人都那么做,你还是照着来吧)
再说一些其他的差别。全局变量的访问速度要低于局域变量,不过对于现在的cpu来说速度差别可以忽略。明白了全局变量和局域变量的差别后,就要了解两个变量的使用原则。具体说的话,全部使用局域变量,只有在整个map的过程都需要保留这个变量的值,或者是向其他的t传递某个变量或者数值的时候,才需要全局变量(这个也是因为trigger提供的接口不够丰富,不然的话传递变量也不需要用全局变量)。
另外还有一个原则上的使用方法问题,从执行的速度上讲:
局域变量的访问速度 > 全局变量的访问速度 > 函数的访问速度
(靠左的速度更快)
所以如果某个常函数需要经常使用的话(如GetPlayersAll,向所有玩家的屏幕上发送信息是map中经常会用到的),可以使用一个全局变量,不仅速度要比GetPlayersAll快,而且也不需要每次都在使用DisplayTextToForce的t当中:
local force foc=GetPlayersAll()
....
call DisplayTextToForce(foc,****)
call DestroyForce(foc)
set foc=null
这么麻烦了,而且这个里面2次访问了函数,效率也要低了许多
另外一个是局域变量的使用原则。首先要说一些大家都不大关心的的东西,补充一些知识。可能绝大多数人都不看blz.j的吧,t使用的函数绝大部分是在blz.j里的,而blz.j里的函数都不是真正的API(应用程序接口),只有common.j里面的才是真正的API,blz.j里面的东西是对com.j进行了封装,而在blz.j里面,大量使用了bj_前缀的全局变量。下面说一个t里经常会用到的函数:
function CreateNUnitsAtLoc takes integer count, integer unitId, player whichPlayer, location loc, real face returns group
call GroupClear(bj_lastCreatedGroup)
loop
set count = count - 1
exitwhen count < 0
call CreateUnitAtLocSaveLast(whichPlayer, unitId, loc, face)
call GroupAddUnit(bj_lastCreatedGroup, bj_lastCreatedUnit)
endloop
return bj_lastCreatedGroup
endfunction
这个是创建单位(unit)的t实际上所使用的函数,在这个函数中还使用了另外一个blz.j中的函数:
function CreateUnitAtLocSaveLast takes player id, integer unitid, location loc, real face returns unit
if (unitid == 'ugol') then
set bj_lastCreatedUnit = CreateBlightedGoldmine(id, GetLocationX(loc), GetLocationY(loc), face)
else
set bj_lastCreatedUnit = CreateUnitAtLoc(id, unitid, loc, face)
endif
return bj_lastCreatedUnit
endfunction
整个函数过程中使用了2个全局变量(bj_lastCreatedUnit, bj_lastCreatedGroup),一个循环(loop),一个判断(if),还掉用了n次函数,而事实上我们常常只需要创建一个unit而已。而且使用了这个函数后,还要考虑内存释放的问题——事实上我是第一次看bj中CreateNUnitsAtLoc的函数,不认为这个函数会存在group的内存泄漏的问题,现在正在和人讨论,但是loc的泄漏是肯定的,又多执行了许多语句。
这么多的过程,实际上只要一条语句就能完成,比如我们经常做的,在施法者的位置创建一个“辅助单位”(caster),用j只要这样:
local unit u = GetTriggerUnit() //等会儿再解释为什么要这句
local unit uCaster = CreateUnit( GetOwningPlayer( u ), '****', GetUnitX( u ), GetUnitY( u ), 0 )
而如果用t的话,翻译成j,需要这样几句:
local location loc = GetUnitLoc(GetTriggerUnit()) //这句需要手动添
call CreateNUnitsAtLoc( 1, '****', GetOwningPlayer(GetTriggerUnit), loc, bj_UNIT_FACING )
//下面这句到底需不需要晚些时候告诉大家答案,不过估计是需要,等会儿帖篇esper大大的文章
call Destroy(GetLastCreatedGroup())
call RemoveLocation(loc)
set loc = null
而且在每次访问caster的时候,t还要使用GetLastCreatedUnit(),效率低了,工作量大了
首先就是要明确一个原则,希望各位逐渐由t转到j,提高自己map的执行效率。接下来局域变量的原则是需要应用在j中的。根据soarchin的观点,哪怕使用了一次的函数也可使用局域变量(咔咔,他自己说过的,表怪我)。俺觉得除非要释放那处内存,要不然没必要为只使用一次的函数也创立一个局域变量。我的习惯是,如果是嵌套的的函数调用,大于一次就为嵌套内的函数创建变量,比如GetOwningPlayer(GetTriggerUnit()),我会为GetTriggerUnit申请一个局域变量;如果一个函数使用了大于2次,我也会为那个函数申请变量。其实这些是习惯问题,申请变量一方面是代码好读了,另外一方面也提高了效率,都是个人习惯的问题。
其他的像wait之后如果也要使用GetTriggerUnit的话就一定要用局域变量的常识我就不用介绍了吧?
再补充一点关于循环的问题
For each (Integer A) from 1 to 10使用的实际上是
set bj_forLoopAIndex = 1
set bj_forLoopAIndexEnd = 10
loop
exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
....
set bj_forLoopAIndex = bj_forLoopAIndex + 1
endloop
使用的3个变量都是全局变量,这个是有很大危险的(估计绝大多数人都知道了),所以一旦有for循环,一定要使用局域变量
|