新浪网 新浪游戏
新浪游戏 | 新浪首页
魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸
魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸 魔兽争霸
魔兽争霸 魔兽争霸
魔兽地图 | 对抗地图 | 角色扮演 | 战役地图 | 建塔防御 | 冒险剧情 | RPG 录像 | RPG 攻略
魔兽下载 | dota百科 | 魔兽地图 | 魔兽补丁 | 魔兽录像 | 魔兽工具 | 电竞视频 | 魔兽图库
资 讯 区 | 战术中心 | 魔兽新闻 | 新手必读 | 英雄详解 | 宝物大全 | 野外生物 | 种族资料
魔兽争霸
  新浪游戏 >> 单机游戏 >> 魔兽争霸  官方网站  责编:洋一  

简单讲解一下全局变量与局域变量

http://games.sina.com.cn 2004-09-20 16:41 作者: [GA]eGust         我要投稿    评论

  学过编程的人就不用看了(除非是只学过那种老式的用行号的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循环,一定要使用局域变量

[我要投稿]  [发表评论]
魔兽名人博客推荐:
新浪魔兽博客优秀作者:

·《冰封王座》v1.20E中文傻瓜包
·《冰封王座》v1.20傻瓜包
·《冰封王座》v1.19傻瓜包
·《冰封王座》v1.18傻瓜包
·《冰封王座》v1.17补丁大全

·《魔兽3:冰封王座》秘籍代码
·《魔兽3:冰封王座》全地图预览
·《魔兽3:冰封王座》魔兽官方术语表
·《魔兽3:冰封王座》战网命令大全详细
·人类联盟
[大法师] [圣骑士] [血法师] [山丘之王]
·暗夜精灵
[守望者] [恶魔猎手] [月之女祭司]
[丛林守护者]
·兽族部落
[牛头酋长] [暗影猎手] [剑圣] [先知]
·不死军团
[死亡骑士] [恐惧魔王] [地穴领主]
[巫妖]
·中立英雄
[火焰领主] [炼金术士] [深渊魔王]
[熊猫酒仙] [黑暗游侠] [驯兽师]
[地精修补匠] [娜迦女海巫]

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

Copyright © 1996 - 2006 SINA Corporation, All Rights Reserved

新浪公司 版权所有
北京网通提供网络带宽