偶久网

 找回密码
 注册会员

QQ登录

只需一步,快速开始

搜索

网站魔兽热门地图

查看: 2506|回复: 2

jass基础教程之五[库函数]

[复制链接]
邪恶叔 title=
发表于 2010-11-23 14:06:54
这章是Jass基础教程中最难的部分, 也是最实用的部分.  Jass的关键字有26个, 跟英文字母一样多. 我们来回顾一下:
  and, array, call, constant, else, elseif, endfunction, endglobals, endif, endloop, exitwhen, extends, function, globals, if, local, loop, native, not, or, return, returns, set, takes, type, then
  以此看来, Jass是语法结构最简单的一种准计算机语言. Jass实现功能基本依靠调用common.j, blizzad.j, common.ai中的库函数.
  本章所述内容不属于Jass语法部分, 最主要讲述:
  触发器(Triggers)
  跨脚本通讯(Inter-Script Communication)
  队列(Enumerations)
  队列过滤器(Filters)
  线程(Threads)
  1) 触发器(Triggers)
  触发器用于地图触发器脚本(Trigger Jass), 用于响应特定事件. 它是种响应信号(callback), 触发器不能应用于AI, 即是说AI Jass中不能有触发器的语句.
  触发器包含创建触发器, 设置触发条件, 设置动作.
  a)创建触发器
  触发器的数据类型为trigger(触发器), 是handle(句柄)的子类型.
  创建触发器的函数在common.j中的声明:
  native CreateTrigger takes nothing returns trigger
  参数:
  nothing //无参数
  返回:
  trigger //返回创建的触发器
  使用格式: set 表达式 = CreateTrigger()
  其中表达式是trigger类型的变量
  新数据类型:
  trigger类型: 是handle的子类型, 用于调用和处理触发器
  b)触发事件
  触发事件的数据类型为event(事件). 可以引起触发响应的触发器必须先在游戏中注册触发事件, 用于监视游戏事件发生.
  注册触发事件的函数在common.j中的声明:
  native TriggerRegister*Event takes
  trigger whichTrigger, ... returns event
  函数名:
  根据事件对象的不同, 有不同的事件响应. TriggerRegister*Event中的 * 号为对象名, 如: TriggerRegisterUnitEvent, TriggerRegisterEnterRegion 等, 注册不同对象的事件要求传递相应指定对象作为参数, 有些事件注册要求传递下面所说的过滤器(Filters)
  参数:
  trigger whichTrigger //触发器变量
  ... //相应指定对象的变量
  返回:
  event //返回该事件
  使用方法(举注册单位事件的例子):
  call TriggerRegisterUnitEvent(哪个触发器, 哪个单位, 哪种类型的单位事件)
  新数据类型:
  event //事件
  c)触发条件
  触发条件是一组布尔表达式(boolexpr)数据, 它的数据类型为条件函数(conditionfunc), 是布尔表达式(boolexpr)类型的子类型.
  建立触发条件的函数在common.j中的声明:
  native Condition takes code func returns conditionfunc
  参数:
  code func //函数代码作为参数
  返回:
  conditionfunc //返回建立的条件
  其中takes code func是指需要函数代码func作为参数, 参数函数 func 必须的声明格式必须是: takes nothing returns boolean. 即是说, 用作参数的函数本身应该是无参数且返回值为布尔型(boolean).
  使用格式: set 表达式 = Condition(function 布尔型函数名)
  其中表达式是条件函数(conditionfunc)
  新数据类型:
  boolexpr 布尔表达式
  conditionfunc 条件函数, 是布尔表达式的子类型
  比如:
  function Girl1 takes nothing returns boolean
  ...
  return true
  endfunction
  function Girl2 takes integer i returns boolean
  ...
  return true
  endfunction
  function Girl3 takes nothing returns nothing
  ...
  return
  endfunction
  // 假设 c 为conditionfunc类型变量
  set c = Condition(function Girl1) //(A)
  set c = Condition(function Girl2) //(B) 本身要求参数
  set c = Condition(function Girl3) //(C) 返回值不是boolean类型
  解释: Girl1() 是无参数且返回为boolean的函数, 可以用作 Condiction() 中的参数函数code func. 而Girl2() - 本身要求参数, Girl3() - 返回值不是boolean类型, 所以(B)(C)函数不可作为Condiction()的参数.
  在第一章变量篇中我没有详细说明code类型的数据, 因为怕读者不能理解. 现在我说明下code类型数据. 我们用例子说明, 比如:
  call myfuction1(IsGirl()) //(A)
  call myfuction2(function IsGirl()) //(B)
  (A)和(B)之间有什么不同呢? 按我的理解: (A)(B)都把IsGirl()运行返回后的值当作myfunction1()/myfunction2()的参数. IsGirl()在(A)中是一次性的处理过程. 而(B)是创建了一个运行IsGirl()逻辑的监视线程, 只要线程没给清除/终止, IsGirl()将一直监视变化. 注意这里所说的线程跟下面要说的AI线程是两码事.
  d) 增加触发条件:
  触发条件可以用TriggerAddCondition()增加
  增加触发条件的函数在common.j中的声明:
  native TriggerAddCondition takes trigger whichTrigger,
  boolexpr condition returns triggercondition
  参数:
  trigger whichTrigger //触发器变量
  boolexpr condition //触发条件变量
  返回:
  triggercondition //返回该触发条件的handle(句柄)
  使用格式:
  set tc = TriggerAddCondition(触发器, 触发条件)
  其中 tc 为triggercondition类型变量
  新数据类型:
  triggercondition 触发条件句柄
  (注意: 触发条件和上面要说的触发事件是不一样的!!!)
  例:
  这是增加/移除/改变触发条件的例子
  //文件头声明个全局变量tc
  globals
  ...
  triggercondition tc = null
  trigger mytrigger = null
  ...
  endglobals
  function Girl takes nothing returns boolean
  ...
  return true
  endfunction
  function notGirl takes nothing returns boolean
  ...
  return false
  endfunction
  //为 mytrigger 增加触发条件
  function addcondition takes nothing returns nothing
  ...
  set mytrigger = CreateTrigger()
  set tc = TriggerAddCondition(mytrigger, Condition(function Girl))
  ...
  endfunction
  //因为tc是mytrigger触发条件的句柄, 把tc清空便清除了指向的触发条件.
  function removecondition takes nothing returns nothing
  ...
  set tc = null
  ..
  endfunction
  //改变 mytrigger 的触发条件
  function modifycondition takes nothing returns nothing
  ...
  set tc = null //先把原来的触发条件移掉
  set tc = TriggerAddCondition(mytrigger, Condition(function noGirl))
  ...
  endfunction

邪恶叔 title=
 楼主| 发表于 2010-11-23 14:11:04
e)触发器动作

  触发器动作是当指定事件发生并符合触发条件的执行的语句.

  增加触发器动作的函数在common.j中的声明:

  native TriggerAddAction takes trigger whichTrigger,

  code actionFunc returns triggeraction

  参数:

  trigger whichTrigger //触发器变量

  code actionFunc //执行函数

  注意: 执行函数必须是无参数无返回值(takes nothing returns nothing)的函数

  返回:

  triggeraction //触发动作

  新数据类型:

  triggeraction //触发动作, handle字类

  把有关触发器主要函数糅合在一起, 我们来看个例子, 研究下触发器是怎么创建的:

  //文件头声明全局变量mytrigger

  //假设mytrigger是监视单位死亡事件的触发器

  globals

  ...

  trigger mytrigger = null

  ...

  endglobals

  //判断是否掉物品

  function isDrop takes nothing returns boolean

  local boolean conditcion

  ...

  if conditcion = true then

  return true

  else

  return false

  endif

  endfunction

  //掉物品

  function DropItems takes nothing returns nothing

  //获得触发单位

  local unit trigUnit = GetTriggerUnit()

  ....

  call UnitDropItem( trigUnit, 'IC21' )

  ...

  endfunction

  function ThisIsMyTrigger takes nothing returns nothing

  local unit u

  //在-5630.5, -4723.1坐标为玩家5创建单位'n00I', 面向90.260度

  set u = CreateUnit(Player(5), 'n00I', -5630.5, -4723.1, 90.260 )

  //创建触发器(相当于mytrigger的初始化)

  set mytrigger = CreateTrigger()

  //为mytrigger注册触发器事件, 让游戏系统监视所创建单位u的死亡事件

  //EVENT_UNIT_DEATH是common.j中定义的常量

  call TriggerRegisterUnitEvent( mytrigger, u, EVENT_UNIT_DEATH )

  //为mytrigger增加触发条件

  //isDrop是无参数返回为布尔值的函数

  set tc = TriggerAddCondition(mytrigger, Condition(function isDrop))

  //为mytrigger增加触发器动作 - 掉宝物

  //DropItems是无参数无返回值的函数

  call TriggerAddAction(mytrigger, function DropItems )

  ...

  endfunction

  可以看出, 创建触发器顺序是:

  1. 触发器初始化 - CreateTrigger()

  2. 触发器事件注册 - TriggerRegister*Event()

  3. 定义触发条件(可选) - TriggerAddCondition()

  4. 触发器动作 - TriggerAddAction()

  注意:

  1. 创建触发器必须先用CreateTrigger()初始化.

  2. 没有注册事件的触发器只是个处理过程, 不会响应事件执行程序

  3. 触发器可以不加触发条件, 因为可以用触发动作调用的函数来控制逻辑.

  2)线程(Threads)

  (这部分属于AI部分, 作为入门者做一般性了解就行了, 因为AI都是纯JASS写的, 也没有真正好的AI EDITOR. 本人也对此一知半解, 关于AI的文章也不多, 没什么好参考的.)

  线程只应用于AI脚本(AI JASS), 不能用于触发器脚本(Trigger Jass). 通常, 当AI脚本开始运行时只创建一个线程, 创建更多的线程可以用comman.j的本地函数:

  native StartThread takes code func returns nothing

  调用 call StartThread(function myfunc) 将创建一个从函数myfunc开始执行的线程.

  每个玩家最多可以拥有6个线程, 包括一开始执行的主线程. 当一个玩家有6个线程数时, 调用StartThread()的语句将被忽略. 线程不能回收, 当你为某玩家创建了5个自定义线程, 将无法为该玩家创建更多的线程.

  当新线程创建时, 线程立即生效. 当线程让步执行时, 创建此线程的父线程将继续执行.

  在同一玩家中的所有线程都共享全局状态(包括变量). 即是修改某个全局变量, 修改后的值在此玩家的所有线程中都是可见的.

  线程在以下的情况让步执行, 返回父线程

  a) 当线程中的操作码(opcode)超出限制, 线程会自动休眠 1 秒

  b) 当线程中用使用 Sleep(n), 线程将休眠 n 秒, 然后继续执行.

  线程在以下情况会中止, 返回父线程

  a) 如果 call StartThread(null)中, 线程中止

  b) 当线程的主函数返回, 线程中止.

  (StartThread()中之间调用的函数就是主函数.)

  c) 当线程中使用没有声明的变量, 线程中止. 在使用之前, 变量必须声明.

  d) 当线程中出现零为被除数时, 线程中止

  e) 线程主函数出现语法错误.
邪恶叔 title=
 楼主| 发表于 2010-11-23 14:29:11
  //比较生命值

  if life > mostLifeSoFar then

  //把较大的生命值储存

  set mostLifeSoFar = life

  //把有较大生命的单位储存

  set unitWithMostLifeSoFar = nextUnit

  endif

  endfunction

  ...

  //初始化全局变量的值为空值

  set mostLifeSoFar = 0

  set unitWithMostLifeSoFar = null

  //调用ForGroup

  //对单位组myGroup中的每个单位都运行函数MostLifeCallback比较生命

  call ForGroup(myGroup, function MostLifeCallback)

  //上句运行后, 全局单位类型变量unitWithMostLifeSoFar便指向单位组myGroup中最高生命的单位, 或:

  //如果单位组myGroup是空组, 那么unitWithMostLifeSoFar便是空值null

  ...

  当然, 实现队列操作, 也可以用数组的方法来处理. 但, 数组不能使用紧接着要说的队列过滤器, 也不能定义数组中包含数组. 这些都是队列所拥有的优势, 如可以有数组型的单位组(相当于数组中包含数组), 也可以用队列过滤器.

  5)队列过滤器(Filters)

  队列过滤器用于在队列中增加符合条件的元素. 比如, 在创建一个法力小于20的单位组时, 便可以用队列过滤器(Filters)来创建.

  //在单位组中增加指定单位名为unitname, 并符合队列过滤器filter的单位

  native GroupEnumUnitsOfType takes group whichGroup, string unitname,

  boolexpr filter returns nothing

  //在单位组中增加指定玩家为whichPlayer, 并符合队列过滤器filter的单位

  native GroupEnumUnitsOfPlayer takes group whichGroup, player whichPlayer,

  boolexpr filter returns nothing

  //在单位组中增加指定玩家为whichPlayer, 并符合队列过滤器filter的单位

  native GroupEnumUnitsOfTypeCounted takes group whichGroup, string unitname,

  boolexpr filter, integer countLimit returns nothing

  //在单位组中增加指定区域为r, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRect takes group whichGroup, rect r, boolexpr filter

  returns nothing

  //在单位组中增加countLimit个指定区域为r, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRectCounted takes group whichGroup, rect r,

  boolexpr filter, integer countLimit returns nothing

  //在单位组中增加在指定点坐标范围之内, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRange takes group whichGroup, real x, real y,

  real radius, boolexpr filter returns nothing

  //在单位组中增加在指定点范围之内, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRangeOfLoc takes group whichGroup,

  location whichLocation, real radius, boolexpr filter returns nothing

  //在单位组中增加指定个数, 在指定点坐标范围之内, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRangeCounted takes group whichGroup, real x, real y,

  real radius, boolexpr filter, integer countLimit returns nothing

  //在单位组中增加指定个数, 在指定点范围之内, 并符合队列过滤器filter的单位

  native GroupEnumUnitsInRangeOfLocCounted takes group whichGroup,

  location whichLocation, real radius, boolexpr filter,

  integer countLimit returns nothing

  //在单位组中增加被指定玩家选中, 并符合队列过滤器filter的单位

  native GroupEnumUnitsSelected takes group whichGroup, player whichPlayer,

  boolexpr filter returns nothing

  类似地, 对于势力也有相应的操作函数

  //在势力中增加符合队列过滤器filter的玩家

  native ForceEnumPlayers takes force whichForce, boolexpr filter returns nothing

  //在势力中增加指定个数, 并符合队列过滤器filter的玩家

  native ForceEnumPlayersCounted takes force whichForce, boolexpr filter,

  integer countLimit returns nothing

  // Add all units that are allies of 'whichPlayer' that satisfy 'filter'

  //在势力中增加和指定玩家同盟, 并符合队列过滤器filter的玩家

  native ForceEnumAllies takes force whichForce, player whichPlayer,

  boolexpr filter returns nothing

  //在势力中增加和指定玩家敌对, 并符合队列过滤器filter的玩家

  native ForceEnumEnemies takes force whichForce, player whichPlayer,

  boolexpr filter returns nothing

  以上函数中boolexpr filter在本章第1)节触发器中有提到, 通常可以使用过滤器(filterfunc).过滤器跟触发器的条件函数(conditionfunc)类似. 创建过滤器可以用以下语句:

  native Filter takes code func returns filterfunc

  其中参数函数code func必须是无参数返回值为布尔值的函数(takes nothing returns boolean), 过滤器用于在创建队列时增加额外的条件. 在过滤器中, 可以使用下面的函数获得下一个待查的单位/玩家/不可破坏物:

  //获得下个待查单位

  constant native GetFilterUnit takes nothing returns unit

  //获得下个待查玩家

  constant native GetFilterPlayer takes nothing returns player

  //获得下个待查可破坏物

  constant native GetFilterDestructable takes nothing returns destructable

  我们来看个创建一个法力小于20的单位组例子:

  //过滤函数, 是无参数返回值为布尔值的函数

  function LessThan20ManaCallback takes nothing returns boolean

  //获得下个检查的单位

  local unit nextUnit = GetFilterUnit()

  //检查待查单位的法力是否小于20

  //小于20则返回true, 否则返回false

  return GetUnitState(nextUnit, UNIT_STATE_MANA) < 20

  endfunction

  ...

  //创建过滤器, 过滤函数是LessThan20ManaCallback

  local filterfunc myFilter = Filter(function LessThan20ManaCallback)

  //在单位组中增加指定区域, 符合过滤条件的单位

  call GroupEnumUnitsInRect(myGroup, someRect, myFilter)

  // Destroy the filter if we are not going to use it again

  //不再使用过滤器, 销毁过滤器, 避免内存泄漏

  call DestroyFilter(myFilter)

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