JASS

您所在的位置:网站首页 jass翻译 JASS

JASS

2023-09-03 19:31| 来源: 网络整理| 查看: 265

一、初识jass 1、概述(配合魔兽3编辑地图使用的脚本语言)

变量 存储数据,重点熟悉和掌握全局和局域变量 . 函数 执行功能,了解函数的结构、生成和运行等 .

2、详解 (1)、变量和函数声明格式如下: a、全局变量的声明: globals 变量类型 变量名 ( = 初始值 ) //括号表示不赋值亦可,下同 变量类型 array 数组名 //数组不可直接赋值,需在函数内逐个赋值 endglobals b、函数和局域变量的声明: function 函数名 takes 参数列表 returns 返回值类型 local 变量类型 变量名 ( = 初始值 ) ...... 执行语句 ...... return 返回值 endfunction (2)、全局与局域变量的区别:

全局任何函数皆可用,局域只作用于所声明的函数。不同函数的局域变量命名可相同,最好不要与全局和 参数名相同。全局占用内存大,执行速率快,全局则反之。触发中,属性变量诸如触发单位、循环整数A、 最后创建的单位、新建的单位组等均是全局变量。同样,变量编辑器(ctrl+B)中的变量亦是全局变量。

(3)、函数起步,掌握以下重点即可: ①、函数名唯一; ②、函数可以无参数和返回值,是为空函数; ③、参数列表为参数类型1 参数名1,参数类型2 参数名2,...,参数类型N 参数名N。逗号分隔即可; ④、返回值必唯一; ⑤、局域变量声明必在所有执行语句之前; ⑥、return必须写在函数末端,即endfunction上一行; ⑦、无返回值函数则无需写return; ⑧、注意returns和return及其后携带数据的区别,前者后接变量类型,后者接具体的数据; ⑨、数组不能做参数和返回值。

/********举例如下(有些地方可能有些啰嗦,有基础的同学可以略过)*********/

globals integer int = 3 //声明一个整数型全局变量int并赋值为3 real col //声明一个实数型全局变量col未赋值 string array str //声明一个字符串数组str boolean array b = false //错误!数组不能直接赋值! endglobals local integer index = 19 //错误!局域变量声明怎能脱离函数? function A takes nothing returns nothing local integer a = 1 //声明一个整数型局域变量a并赋值为1,下同 local integer b = 2 local integer c = a + b call BJDebugMsg(I2S(c)) //显示c的值,该句(带call)即为执行语句 endfunction function B takes real r, integer e returns nothing //接受一个实数型参数r和一个整数型参数e call BJDebugMsg(R2S(r)+I2S(e)) //显示r的值 endfunction function C takes nothing returns real //返回一个实数型变量 return 2 * 3 * 15 //正确,必须拥有返回值 endfunction function D takes nothing returns real, integer //该函数错误!一个函数只能返回一个数据类型! return ... endfunction function E takes real r returns real //该函数错误!有返回值类型,但函数结尾却无具体返回值! endfunction function F takes real r returns real //正确,有返回值类型也有具体返回值 return r * 33 endfunction function G takes nothing returns nothing call BJDebugMsg(I2S(h)) //错误!局部变量声明必须在所有执行语句之前! local integer h = 5 endfunction function H takes integer array num returns nothing //错误!数组不能作为函数的参数! endfunction 二、jass进阶 1、函数定义

想想还是略吧,没啥说的 太罗嗦.............................

2、生成和运行

通过使用 call 函数名(参数列表)即可,这里的参数列表的要求前面已经说过。需要注意的是,call后面接的是一定函数,此类函数叫被调用函数。被调用函数一定要在调用函数的前面,且传递状态下的参数列表要和被调用函数的参数列表的类型、次序和数量相一致(有点像C的语法结构)。例如:

function A takes integer i returns nothing call BJDebugMsg(I2S(i)) //显示31 endfunction function B takes nothing returns nothing call A(31) //正确,被调用函数A在函数B的前面。函数B将整数31传递给函数A call C(0.02) //错误!被调用函数C在函数B的后面! endfunction function C takes real r returns nothing call BJDebugMsg(R2S(r)) endfunction

如何让上述问题不被错误尼,这里需要使用函数的检索功能 ''ExecuteFunc'' 使用规范有两点(觉得有点鸡肋,写的时候细心一下就不需要这样做):

1.函数检索距离不宜过大,即2个函数之间的字节码有一定限制; 2.检索状态下,被调用的函数不能拥有参数,但可以有返回值;

使用范例:

function A takes nothing returns nothing endfunction function B takes nothing returns nothing call ExecuteFunc("A") //正确,被检索函数无参数,下同 call ExecuteFunc("C") call ExecuteFunc("D") //错误!被检索函数D携带参数! endfunction function C takes nothing returns integer return 256 endfunction function D takes integer i returns nothing call BJDebugMsg(I2S(i)) endfunction 三、语法 1、循环 loop exitwhen 条件 //满足条件退出循环 // 执行语句........ endloop 2、条件控制 a,单层控制: if 条件 then //如果满足条件1则做... // 执行语句....... endif b,双层控制: if 条件1 then //如果满足条件1则做... // 执行语句..... else 否则做... // 执行语句..... endif c、多层控制: if 条件1 then //如果满足条件1则做... // 执行语句.... elseif 条件2 then //如果满足条件2则做... // 执行语句.... elseif 条件3 then //如果满足条件3则做... // 执行语句.... elseif 条件4 then //如果满足条件4则做... // 执行语句.... ....// 如果满足条件N.... ....// 执行N的语句.... endif 3、逻辑关系符:

and(与)、or(或)、not(非)。

4,比较和操作符: // [](数组) , [] = (数组赋值) , = (赋值); // ==(等于) , != (不等于) , >(大于) , =(大于或等于) , 7 //当i>7时退出循环 set index = 25 * i set i = i + 1 endloop endfunction // 循环嵌套条件 function C takes nothing returns integer local integer i = 1 local integer array index loop exitwhen i > 7 set index = 25 * i if (i==2 or i==6 ) then call BJDebugMsg(I2S(i)) endif //endif嵌套于循环内,因此应该先结束里层 set i = i + 1 endloop //再结束外层 endfunction 四、函数间的数据传递 1、利用全局数组传递(操作全变量。根本不需要传递,每个函数直接引用) 2,1.20的Gamechche(游戏缓存)

在游戏里我们知道,要实现一个技能多人运行,可以用全局数组。当然可以利用局域变量,因为局域变量是互不干扰的、稳定的,而且能并行 运行的。当然,我们可以通过传递参数传递局域变量的值,然而,对一些不允许携带参数的函数,局域变量的数据无法在函数间传递,怎么办?这个时候需要利用到缓存。像这样不允许参数的函数有哪些呢?归纳一下:如下几种

a,jass计时器调用的函数; b,队列事件(单位组、玩家组、可破坏物组等)的过滤(条件)函数; c,触发的条件函数;

缓存很好理解,就是一个仓库,这个仓库可以想象成很大的房子,里面有很多柜子,而每一个柜子里面又有很多抽屉,然而,每一个抽屉只能装指定的5种类型的东西,且每一种东西只能装载一个。这就是柜体原理,用不着多么抽象专业的术语,我想上面这个阐述已经让你理解了什么是缓存。那么,在实际的缓存中,房子可以看做一个缓存,当然,既然是很大的房子,一个地图一个缓存足矣。柜子可以看做缓存的目录,抽屉可以看做缓存的标签,由于缓存的目录和标签是采用字符串的形式,因此,我们不妨存储一本书 B 到房子 F 的 A 号柜子 K 号抽屉里:房子X → A号柜子 → K号抽屉 → 书B,同样的我们必须在存储后指定的路径下才能找到这个书 B,基于抽屉存放东西的唯一性,“房子X → A号柜子 → K号抽屉” 即指代所存放的书 B。由于抽屉一次一种东西只能放一样,如果再放一个书 C 进去的话,就会覆盖掉之前的书 B,此时,“房子X → A号柜子 → K号抽屉”指向书 C。当然,如果在其他抽屉比如L号抽屉方上书 C,丝毫不影响书 B 的存放,因为是2个毫不相干的路径嘛。

所以,对于缓存,一个道理,一个标签只能存5种类型的数据:整数、实数、布尔值、字符串和单位。每种数据只能存一个,否则后进来的数据会覆盖之前的属于这个种类的数据。下面,看看缓存是如何实现的。注意以下几点; 缓存必须在使用前初始化,方法为set 缓存变量名 = InitGameCache("缓存名"),缓存名随便取吧。 取:call Store数据种类(缓存, 目录名, 标签名, 该类数据的某个值); 存:GetStored数据种类(缓存, 目录名, 标签名); 清空标签:call FlushStored数据种类(缓存, 目录名, 标签名); 清空目录:call FlushStoredMission(缓存, 目录名); 清空整个缓存:call FlushGameCache(缓存); 检测缓存数据是否存在:HaveStored数据种类(缓存, 目录名, 标签名);

假定缓存名为udg_GC

call StoreInteger(udg_GC, "A", "b", 15) //将整数15存到了缓存GC的A目录下的b标签下 call StoreReal(udg_GC, "A", "b", 0.2) //将实数0.2存到了缓存GC的A目录下的b标签下 call StoreIBoolran(udg_GC, "A", "b", false) //将布尔值false存到了缓存GC的A目录下的b标签下 call StoreString(udg_GC, "A", "b", "TigerCN") //将字符串TigerCN存到了缓存GC的A目录下的b标签下

由于同一个路径下每种数据可以存一个,所以不会冲突,但若是此时在这个数据下再存一个整数30,即:

call StoreInteger(udg_GC, "A", "b", 30)

那么GetStoredInteger(udg_GC, "A", "b")所指向的就是后存进来的30了,但这不影响该路径下其他类型的数据。

3 return bug原理(类型强制转换)

如果要存取一个特效怎么办?不用担心,return bug出场了!我们可以用他将其他种种句柄类的数据转换成整数,然后利用StoreInteger来存储。return bug原理很简单,利用编译器自下而上的检测,当碰到第一个return时,被虚假的结果所蒙蔽就自动跳过检测。这样说可能有抽象,引用某位大佬的段子:

由于变态太多,于是国家下令:禁止男性穿女生内裤!还设有policeman当街拦路检察。 一个变态他很想穿蕾丝花边内裤,可怎么瞒过警察呢——于是他就在外面多套了条四角内裤,也就是说,他穿了两条内裤。 警察检查时,看到他穿的是四角内裤,就放他过关,却不知道这内裤下另有玄机。于是,这就是一个检查机制的漏洞。 托这个漏洞的福,城里的变态更多了。。。

看看事例:

function H2I takes handle h returns integer return h //真实返回的值是h,即句柄所指向的内存地址(实际上是蕾丝花边裤) return 0 //编译器先检测这里,返回值是0,正确,跳过检测(检查到四角内裤被通过) endfunction function I2E takes integer i returns effect return i //从句柄所指向的内存地址中读取出句柄 return null //编译器先检测这里,返回值是null,正确,跳过检测 endfunction

所以我们要存句柄类的变量诸如单位、特效、点、单位组、物品等均可以利用此形式,自己按照这个格式套就是了. 例如 存储一个触发单位:

function H2I takes handle h returns integer return h return 0 endfunction function I2U takes integer i returns unit return i return null endfunction function run takes nothing returns nothing local unit u = I2U(GetStoredInteger(udg_GC, "Unit", "Caster")) //从缓存对应的路径下读取出存储的单位的地址,通过I2U转换成单位,再赋值给变量u call RemoveUnit(udg_GC, "Unit", "Caster", H2I(u)) //删除单位u call FlushStoredMisiion(udg_GC, "Unit") //清空Unit目录。注意,删除整个缓存会删除其他使用缓存的数据 set u = null endfunction function test takes nothing returns integer local unit u = GetTriggerUnit() //声明一个单位型局域变量u并赋值为触发单位 local timer t = CreateTimer() //声明一个局域计时器t并赋值为新建的计时器 call StoreInteger(udg_GC, "Unit", "Caster", H2I(u)) //将u转换成整数并存储到Unit目录的Caster标签下 call TimerStart(t, 5, false, function run) //开启计时器t,一次性,持续5秒,作用与函数run set t = null //清空句柄变量 endfunction 4,1.24的Hashtablee(哈希表) 很简单,只不过是把缓存的目录和标签字符串记录的形式改成了整数,也修正了RB,同时可直接存储一些 句柄类的数据。 取:call Save数据种类(哈希表, 目录名, 标签名, 该类数据的某个值) 存:Load数据种类(哈希表, 目录名, 标签名) 清空目录:call FlushChildHashtable(哈希表, 目录名) 清空整个缓存:call FlushParentHashtable(哈希表) 检测缓存数据是否存在:HaveSaved数据种类(哈希表, 目录名, 标签名) 五,触发器和函数

一般触发器无非是由三个函数组成:事件函数(初始化函数)、条件函数、动作函数,所以无需多疑,jass要完 成一个技能或系统也需要经历这3个步骤,当然,往往可以扩展出很多自定义的函数。总体思路不变;



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3