偶久网

 找回密码
 注册会员

QQ登录

只需一步,快速开始

搜索

网站魔兽热门地图

查看: 2999|回复: 0

jass基础教程之四[函数篇]

[复制链接]
邪恶叔 title=
发表于 2010-11-23 14:11:40
  如果你仔细读完并读懂前面两章, 应该对Jass函数并不陌生了. 也许我在前两章写的太过综合紧凑, 所以写到这觉得没什么好写了. 不管怎样, 还是需要进一步深入了解Jass函数规范和注意事项.
  注意, 在此章中不是教你动手写Jass函数, 而是让你基本可以读懂别人写的Jass脚本-->如果你英文足够好而且不傻(这是Jass中布尔并立条件, 上一章提到过).可以在Trigger Editor中把一些简单的触发器(Trigger)转化成自定义脚本, 因为事先知道程序逻辑, 这样读起来也比较容易.
  1)函数定义要素
  语法格式
  function 函数名 takes 参数表 returns 返回类型
  局部变量声明
  局部变量声明
  ...
  表达式
  表达式
  表达式
  ...
  return 返回表达式
  endfunction
  其中:
  a) 定义函数的关键字有: function ... endfunction, takes, returns
  b) 函数名的首个字母不能是数字, 函数名中不能有第2章中所说的操作符, 特殊字符和多数非字母符号(如: 空格[url=mailto:~%60!@#$%^&*()-+=|\{}[];:]~`!@#$%^&*()-+=|\{}[];:'"<>?,./[/url]), 也不能使用中文. 函数命名要尽量简明易懂, 突出函数功能.
  c) 参数表是规定传递入函数的数据类型和参数数量, 作用是函数间的数据交换(输入), 参数之间用逗号( , )分开. 不能定义数组作为参数, 这点在的第一章已经做了说明.
  参数必须规定数据类型或者是无参数, 如
  function myfunction1 takes nothing returns nothing
  endfunction
  function myfunction2 takes integer creatnum, unit myunit returns boolean
  call CreateNUnitsAtLoc(creatnum, GetUnitTypeId(myunit), Player(1),
  GetUnitLoc(GetTriggeringUnit()), bj_UNIT_FACING )
  return true
  endfunction
  myfunction1为无参数的函数, 调用时不需要传递参数, 即是用 call myfunction1()
  myfunction1为2参数函数, 参数类型规定为 integer 和 unit. 调用myfunction2时必须同时传递数据类型分别integer和unit的参数, 或者传递入空值(参看第二章的举例).
  注意: 数字的空值为 0, handle(句柄)类型及其子类别的空值为 null. 关于handle类型请参看第一章. 我在本章背后会列出各数据类型所对应的空值.
  d) 返回类型是规定函数返回的数据类型, 作用同样是函数间的数据交换(输出), 可以用returns nothing 说明没有返回参数
  e) 局部变量声明必须写在函数开始部分. Jass总是先声明, 再使用. 如:
  function myfunction3 takes nothing returns nothing
  local integer i = 5
  local integer j = 10
  call myfunction2(i,美女 )
  sleep(360)
  call myfunction2(j,美女 )
  endfunction
  下面的函数是错误的:
  function myfunction3 takes nothing returns nothing
  local integer i = 5
  call myfunction2(i,美女 )
  local integer j = 10
  sleep(360)
  call myfunction2(j,美女 )
  endfunction
  f) 表达式是函数的骨干, 是逻辑的实现部分. 写好这部分, 需要对common.j, blizzar.j,commond.ai有一定的了解.
  g) 返回表达式的值必须和该函数声明时的返回类型一致, 不要声明返回为恐龙类型, 却偷梁换柱返回个美女. 返回值不能是数组.
  2) 程序执行入口函数
  每个Jass脚本文件都需要定义执行程序的入口函数
  在触发器脚本文件war3map.j中, config()和main()是入口函数.
  config()函数作用是在开始游戏之前初始化地图, 如按设计时指定点放置单位/可破坏物,初始化单位状态等.
  main()函数在游戏开始才执行.
  在AI脚本文件中, 用户需自定义main()函数作为该脚本文件的程序入口.
  脚本文件执行入口函数的固定格式:
  function main takes nothing returns nothing //触发器和AI脚本均使用
  function config takes nothing returns nothing //触发器脚本使用
  注意: 一张地图中只能有1个触发器脚本文件war3map.j, 此文件包含在w3m或w3x中
  一张地图中可以包含多个AI脚本文件, 输入AI脚本文件的目录为: \scripts\
  有问有答:
  1) 什么是空值? 不同数据类型的空值是什么?
  当变量声明时, 如果在声明语句中不直接赋值, 则该变量的初始化值为空值, 表示没有赋值. 如:
  local unit myunit
  在上句声明中, myunit没有直接赋值, 那么myunit在声明后值是null
  以下是数据类型的空值对应表
  WORLD EDITOR和JASS变量类型对照表:World Editor
  变量名  Jass变量类型 空值
  Boolean  boolean 布尔型(用于真/假判断) false
  Destructible  destructable 可破坏物 null
  Dialog  dialog 对话 null
  Dialog Button  button 按钮 null
  Floating Text  texttag 漂浮文字 null
  Integer  integer 数值 0
  Item  item 物品 null
  Leaderboard  leaderboard 排行榜 null
  Player  player 玩家 null
  Player Group  force 玩家组 null
  Point  location 位置(点) null
  Real  real 真值型数字 null
  Region  rect 地区 0
  Special Effect  effect 特效 null
  String  string 字符串 null
  Terrain Deformation  terraindeformation 地形 null
  Timer  timer 计时器 null
  Timer Window  timerdialog 计时器窗口 null
  Unit  unit 单位 null
  Unit Group  group 单位组 null
  Player Score  playerscore 积分(1.13版新类型) null
  由上表可以看出, 除了integer, real的空值为 0 和 boolean 的空值为false以外, 其他数据类型的空值都是null. 原因很简单: 除了integer, real, boolean外, 其他数据类型都是handle(句柄)的子类型, handle类型的空值为null, 它的儿子们也跟着null到底了.
  2) 什么是common.j, Blizzard.j, common.ai?
  这3个文件都是支持War3 Jass的公共库文件, 文件里面包含和声明了所有(几乎是所有)Jass可以调用的函数和全局变量.
  common.j 是最基础的API库文件, 在Trigger Jass和AI Jass中都可以调用.
  Blizzard.j 包含使用Trigger Editor时生成的Trigger Jass中经常调用的库函数/全局变量, 实际上是基于common.j写的子函数. Blizzard.j只能在Trigger Jass中调用.
  common.ai 只能在AI Jass中调用, 它有AI中需要使用的函数和全局变量.
  注意: 在Trigger Jass中, 不能调用common.ai中的函数和全局变量; 同样地, 在AI Jass中, 也不能使用Blizzard.j的函数和全局变量.
  3) 如何获得最新的common.j, Blizzard.j, common.ai?
  在war3.mpq/war3x.mpq/war3patch.mpq都有common.j/Blizzard.j/common.ai, 要获得最新版本的文件, 可以用WINMPQ打开war3patch.mpq, 在WINMPQ右上方的输入框中输入: scripts\* ,如果你的war3patch.mpq没有"加密"过, 那么可以找到这3个文件. 如果是"加密"过的, 则需要把war3patch.mpq的文件全部解压到临时目录, 然后用Ultra-Edit中的在文件中搜索字符的功能寻找这3个文件.
  4) 可以在自己的地图中使用自定义的common.j, Blizzard.j, common.ai吗?
  可以. 用输入管理器输入自定义的common.j, Blizzard.j, common.ai, 输入目录应为\scripts\. 这样, 运行地图中的脚本时, War3就不会到war3patch.mpq寻找这3个文件了而直接从地图中的\scripts\目录调用. 这样做的好处就是可以使库文件的同步一致. 通常, 版本相同的WAR3, 其库文件也相同, 所以多数时候不需要在地图中输入这3个文件. 但如果你更改了这3个基本文件来支持你的Jass, 或者确保为了库文件的一致性, 便可以在地图中输入基础库文件.
  5) 怎么解读公共库文件(common.j, Blizzard.j, common.ai)的函数/全局变量?
  Blizzard对函数/全局变量的命名是相当规范简明易懂的(当然需要一定的英文基础), 它定义的函数/全局变量的名基本上是英语语句. 我举些例子:
  native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit
  本地函数CreateUnit() 即是Create Unit, 意思为创建单位, 从参数表(takes 和 returns之间的声明)来看, 这个函数需要输入玩家, 单位编码, 坐标x, 坐标y, 方向角度, 返回值是单位类型变量.
  从函数的名字和参数表, 可以猜测/确认CreateUnit()是用来在地图的x/y坐标中为指定玩家创建指定单位, 并且使创建单位面向所定义的角度, 返回值是创建单位.
  在地图入口函数main()中, CreateUnit()被大量调用, 用来重现地图设计时用WORLD EDITOR(GUI)放置的单位.
  注意: 就如前面所说过, integer可以用单引号括起4个英文字母/数字来表示. 所以CreateUnit()中integer unitid参数可以用 'Xxxx'来表示. 如:
  set u = CreateUnit( Player(5), 'ndgt', -2944.0, -3136.0, 270.000 )
  上句意思是为玩家5在地图坐标(-2944.0, -3136.0)中创建编码为'ndgt'的单位, 并使创建单位面向角度为270度, 然后把创建单位保存在变量u中.
  总结下Blizzard函数名/全局变量名的规律:
  a) 前坠Get 取得某属性, 此类函数一般有返回值
  b) 前缀Set 设置某属性
  c) 前缀Is 是否判断, 此类函数返回类型都是boolean, 返回true或false
  d) 中间有2 数据类型转化函数, 如S2I(), I2R, I2S()等
  e) 有Item/Unit/... 肯定是与物品/单位/...相关的函数
  f) 后缀BJ 此函数肯定是在Blizzard.j中定义的, 不能在AI中使用.
  g) 前缀bj_ 在Blizzard.j中定义声明的全局常量(常量是指定义并赋值后不能再改变的值)
  h) 前缀Create 创建
  i) 前缀Remove 移除
  k) 全部大写 在common.j中定义声明的全局常量
  6) 实用问题: 如何创建外观随机可变的地图?
  上面说过, main()是地图入口函数, 在main()调用了一些再现地图设计时原貌的子函数如CreateAllUnits(). 因此, 我们可以在CreateAllUnits()中加入随机函数GetRandomInt()来控制单位/物品/可破坏物等初始化过程.
  我们可以用WINMPQ打开要修改的地图, 提取war3map.j来修改main()函数及其相关函数. 修改完war3map.j后, 再用WINMPQ导入修改后的war3map.j.
  a) 函数修改/增加方法:
  这是3C地图中的CreateAllUnits()函数
  function CreateAllUnits takes nothing returns nothing
  call CreateNeutralPassiveBuildings() //用户自定义函数
  call CreatePlayerBuildings() //用户自定义函数
  call CreateNeutralHostile() //用户自定义函数
  call CreateNeutralPassive() //用户自定义函数
  call CreatePlayerUnits() //用户自定义函数
  endfunction
  注意: CreateAllUnits()也不是库函数, 是用户自定义函数):
  我们可以根据CreateAllUnits()创建另一个函数:
  function CreateAllUnitsRandom takes nothing returns nothing
  // randomint 是 1,2,3中的随机数
  local integer randomint = GetRandomInt(1, 3)
  call CreateNeutralPassiveBuildingsRandom(randomint)
  call CreatePlayerBuildingsRandom(randomint)
  call CreateNeutralHostileRandom(randomint)
  call CreateNeutralPassiveRandom(randomint)
  call CreatePlayerUnitsRandom(randomint)
  endfunction
  因为原来调用的函数都是无参数函数, 所以相应的函数应该做些修改, 如:
  //创建玩家单位, 以CreatePlayerUnits()为蓝本增加新函数
  function CreatePlayerUnitsRandom takes integer randomint returns nothing
  //此函数以CreateUnitsForPlayer0为蓝本增加新函数
  call CreateUnitsForPlayer0Random(randomint)
  ......
  endfunction
  //创建中立敌对单位
  //此函数以CreateNeutralHostile为蓝本增加新函数
  function CreateNeutralHostileRandom integer randomint returns nothing
  local player p = Player(PLAYER_NEUTRAL_AGGRESSIVE)
  local unit u
  local integer unitID
  local trigger t
  local real life
  //加入条件控制, 根据随机数创建不同单位组合.
  if randomint == 1 then
  //可掉宝物的单位
  set u = CreateUnit( p, 'nC26', -6533.2, 445.1, 220.680 )
  set t = CreateTrigger( )
  call TriggerRegisterUnitEvent( t, u, EVENT_UNIT_DEATH )
  call TriggerRegisterUnitEvent( t, u, EVENT_UNIT_CHANGE_OWNER )
  call TriggerAddAction( t, function Unit000012_DropItems )
  //不掉宝物单位
  set u = CreateUnit( p, 'nelb', -373.5, 3533.4, 44.518 )
  set u = CreateUnit( p, 'nomg', -233.6, 3436.2, 140.871 )
  elseif randomint == 2 then
  //不掉宝物单位
  set u = CreateUnit( p, 'a001', -373.5, 3533.4, 44.518 )
  set u = CreateUnit( p, 'a002', -233.6, 3436.2, 140.871 )
  else
  ......
  endif
  endfunction
  别忘了修改main()
  function main takes nothing returns nothing
  ........
  //call CreateAllUnits() //原版的单位初始化函数, 不再使用
  call CreateAllUnitsRandom() //使用新的单位初始化函数
  ........
  endfunction
  b) 初始设置复制方法:
  问题的关键就是, 这么多初始单位, 怎么样修改起来不累人?
  这里介绍一种方法:
  1. 先把要修改的地图复制
  2. 修改复制地图初始单位的类型/位置/宝物等, 删除所有TRIGGER, 并保存
  3. 用WINMPQ打开复制地图, 提取war3map.j
  4. 复制war3map.j中对应函数中创建单位的语句
  5. 把复制的语句粘贴在原地图中相应函数的适当位置
  6. 重复1-5, 复制更多的随机初始化设置
  注意: 如果用World Editor再次修改并保存, main()将使用GUI默认生成的call CreateAllUnits(), 而不是call CreateAllUnitsRandom(). 此时还需要打开war3map.j修改main()函数.
  同样, 使可破坏物等初始设置有随机变化也可以用此方法.
快速回复 返回顶部 返回列表