找回密码
 点一下
查看: 4372|回复: 4

VJass语法说明完全汉化(一)

[复制链接]
发表于 2008-1-29 01:14:25 | 显示全部楼层 |阅读模式
自由声明全局变量





自由声明全局变量会简化很多操作,你可以再任何地方,不管是在自定义地图脚本还是触发器脚本中声明全局变量。JassHelp的预处理器最终会将他们合并再一处。
[jass] function something takes nothing returns nothing
set somearray[SOMETHING_INDEX]=4
endfunction

globals
constant integer SOMETHING_INDEX = 45
integer array somearray
endglobals
[/jass]
  
但这里还有一个限制,即你只能赋予全局一个常数值如 19923 , 0xFFF, true, false, "Hi"等。而不能将器初始化为一个函数的返回值(不包括原生函数,但大部分原生函数有使游戏崩溃的可能).你也不能将一个全局变量的值赋给另一个全局变量。因为再全局变量合并时,你并不能确定这个变量声明再那个变量之后 。

注:
请保持良好的全局变量命名习惯, 一些程序的源代码中全局变量用全部的大写字母来标识,比如common.j和blizzard.j就是变量命名的典范。udg_这样的前缀可以保留。





Debug 与处理器

关键字“debug”可以让VJass编译器再调试的时候保留部分代码,在发布的时候将其忽略。就像一些用于调试的触发器。
[jass]
function Something takes integer a returns nothing
debug call BJDebugMsg("a is "+I2S(a))
call KillNUnits(a)
endfunction
[/jass]  
如果我们测试地图时。那么每当Something()被调用的时候我们都可以看到变量a的值。












但仍有一个问题,在保存地图的时候我们不可能控制各个触发器在脚本文件中的位置。虽然自定义脚本的代码会放在所有触发器的代码的前面。但触发器本来应该更模块化,本地化,不应该充斥着无关的内容。

库可以解决这些问题,预处理器会把你写在库中的代码放到最前面,甚至能智能地调整好嵌套的库中的代码先后。

其语法异常简单,只要你把代码写在library [library name]和endlibrary之间。
[jass]
library B
function Bfun takes nothing returns nothing
endfunction
endlibrary

library A
function Afun takes nothing returns nothing
endfunction
endlibrary
[/jass]
如果JassHelper发现了这样的代码,就会将Afun()和Bfun()移到脚本文件前面。整个脚本文件都可以自由的调用Afun()和Bfun()。

注意,库A和库B谁在前谁在后是不确定的。JassHelper仅仅是简单地将其移动到脚本文件的开始。

如果一个在库B中的函数需要调用库A中的函数。那么必须要让JassHelper知道库A中的函数要写在B的前面。这时要使用关键字requires
[jass]
library B requires A

function Bfun takes nothing returns nothing
call Afun()
endfunction

endlibrary

library A

function Afun takes nothing returns nothing
endfunction

endlibrary
[/jass]
注: 关键字requires, needsuses的用途是一样的。至于你想使用哪一个,全凭个人喜好。(但似乎needs,和uses没有语法加亮)

预处理器会在保存的时候把库A移动到库B之前。

库A可以要求更多的库放到它前面。
[jass]
library C needs A, B, D
function Cfun takes nothing returns nothing
call Afun()
call Bfun()
call Dfun()
endfunction
endlibrary

library D
function Dfun takes nothing returns nothing
endfunction
endlibrary

library B uses A
function Bfun takes nothing returns nothing
call Afun()
endfunction
endlibrary

library A
function Afun takes nothing returns nothing
endfunction
endlibrary
[/jass]
最终在脚本文件的顶端会是这样。
[jass]
function Afun takes nothing returns nothing
endfunction
function Dfun takes nothing returns nothing
endfunction
function Bfun takes nothing returns nothing
call Afun()
endfunction
function Cfun takes nothing returns nothing
call Afun()
call Bfun()
call Dfun()
endfunction
[/jass]
或者:
[jass]
function Dfun takes nothing returns nothing
endfunction
function Afun takes nothing returns nothing
endfunction
function Bfun takes nothing returns nothing
call Afun()
endfunction
function Cfun takes nothing returns nothing
call Afun()
call Bfun()
call Dfun()
endfunction
[/jass]
或者:
[jass]
function Afun takes nothing returns nothing
endfunction
function Bfun takes nothing returns nothing
call Afun()
endfunction
function Dfun takes nothing returns nothing
endfunction
function Cfun takes nothing returns nothing
call Afun()
call Bfun()
call Dfun()
endfunction
[/jass]
记住:

库名是区分大小写的
如果出现这样的情况:library A requires B ...library B requires A ,则会引起语法错误。
如果库A需要调用另外一个库B中的函数,这个库B学要调用库C中的函数,库C又需要库A的支持时,也会出现错误
不能把一个库写在另外一个库中
库必须写在全局域,即不能写在function... endfunction globals... endglobals之间。
控制哪些代码会被首先执行也是很困难的。所以关于库还有一个相关的关键字intializer用于初始化一个库。你可以在库名后添加 initializer FunctionName来表示初始化时自动调用的函数。这个函数会通过ExecuteFunc()来开启线程执行。很多库都需要大量的初始化操作,这样做可以防止崩溃。

当库A需要B的支持,并且两者都有初始化函数,那么B的初始化函数先被调用。

初始化函数必须不带参数。

[jass]
library A initializer InitA requires B


function InitA takes nothing returns nothing
call StoreInteger(B_gamecache , "a_rect" , Rect(-100.0 , 100.0 , -100.0 , 100 ) )
endfunction

endlibrary

library B initializer InitB
globals
gamecache B_gamecache
endglobals

function InitB takes nothing returns nothing
set B_gamecache=InitGameCache("B")
endfunction

endlibrary
[/jass]
当B的初始化函数在这里会最先调用。

提示:

如果两次声明同一个库名,则会报错。
老的版本中的库是通过宏实现的(即以“//!”开始的预处理命令)。现在的版本仍然可以使用但这种写法应该被废弃。

私有成员
增加私有成员可以避免库与库之间函数名的碰撞。
[jass]
library privatetest
globals
private integer N=0
endglobals
private function x takes nothing returns nothing
set N=N+1
endfunction

function privatetest takes nothing returns nothing
call x()
call x()
endfunction
endlibrary

library otherprivatetest
globals
private integer N=5
endglobals
private function x takes nothing returns nothing
set N=N+1
endfunction

function otherprivatetest takes nothing returns nothing
call x()
call x()
endfunction
endlibrary
[/jass]
两个库的变量和函数具有相同的名字,但这并没有错误,因为他们都是私有的变量和函数。这些私有的变量和函数只能为同一个库里的函数使用或调用。

有时候,你的代码并不学要到前面去,但仍然希望私有属性得以保留,这时可以使用关键字scope也就是域。

关键字scope的语法是scope NAME ..... endscope

同样,域中的函数可以自由的使用域的私有变量,调用域的私有函数,但域外的函数则不能。

很多时候,这种私有的机制会极大的方便代码的移植
[jass]
scope GetUnitDebugStr

private function H2I takes handle h returns integer
return h
return 0
endfunction

function GetUnitDebugStr takes unit u returns string
return GetUnitName(u)+"_"+I2S(H2I(u))
endfunction
endscope
[/jass]
我们知道,H2I()是一个应用非常广泛的函数。我们有时需要从别出移植部分代码到我们的地图脚本,但H2I()这样的函数已经存在于我们的地图中了。一般情况下,为了不出错,只能在移植来的代码中找到H2I()并删除。在域中我们可以赋予其私有的属性,而不必担心函数名的冲突。

私有属性的实现,实际上是通过加上随机前缀来实现的。前缀与标识符中间会使用双下划线“__”来隔开,以防止生成的前缀与你所使用的前缀相同时冲突。同时你也应该注意在命名时尽量不要使用双下划线的前缀。

所以为了能够让域的函数通过ExecuteFunc()函数来调用域或库中的私有函数。我们需要通过SCOPE_PRIVATE来实现 (见后文)

提示:曾经,域的语法通过宏来实现,虽然这种方式仍得到承认,但应予废弃.

公有成员
与私有成员不同的是公有成员的前缀不是随机的。因此可用于域之外。在域内,公有成员的名称没有改变,在域外,只要加上SCP_前缀。

一个例子会让你更好地理解:
[jass]
library cookiesystem
public function ko takes nothing returns nothing
call BJDebugMsg("a")
endfunction

function thisisnotpublicnorprivate takes nothing returns nothing
call ko()
call cookiesystem_ko() //cookiesystem_ 前缀是可选的
endfunction
endlibrary

function outside takes nothing returns nothing
call cookiesystem_ko() //cookiesystem_ 前缀是必须的
endfunction
[/jass]

公有函数可以通过ExecuteFunc来调用。但在在这种方式中总是需要前缀来标识。

[jass]
library cookiesystem
public function ko takes nothing returns nothing
call BJDebugMsg("a")
endfunction

function thisisnotpublicnorprivate takes nothing returns nothing
call ExecuteFunc("cookiesystem_ko") //需要加上前缀

call ExecuteFunc("ko") //基本上,游戏会崩溃
call cookiesystem_ko() //虽然不需要前缀,但加了也没事
call ko() //虽然没有标识前缀,但这种写法是正确的,更为推荐的方法
endfunction
endlibrary
[/jass]
另外,你可以使用SCOPE_PREFIX (见后文)

注:另外,当一个域中有一个公有函数叫做InitTrig(),则JassHelper会将域名作为后缀来标识。这是为了方便在编辑触发器时,初始化的函数也能写入域中。这样当你的域名与触发器名相同,InitTrig()就会取代InitTrig_TriggerName()。

嵌套的域
范围可以嵌套,不要混淆这句话,库不可以嵌套。实际上,库甚至不能嵌套在域中。然而域不一样,域可以嵌套在库中,也可以嵌套在域中

嵌套在另一个域中的域可以被认为是子域,这个子域相对于父域来说是公有的,你也可以把这个子域当做父域的公有成员。

子域相对于父域的关系于一般域相对于整个脚本文件的关系是一样的。

子域的公有函数在父域外被调用时,如果需要前缀,则除了子域的前缀外还需要父域的前缀。

例子:
[jass]
library nestedtest
scope A
globals
private integer N=4
endglobals

public function display takes nothing returns nothing
call BJDebugMsg(I2S(N))
endfunction
endscope

scope B
globals
public integer N=5
endglobals

public function display takes nothing returns nothing
call BJDebugMsg(I2S(N))
endfunction
endscope

function nestedDoTest takes nothing returns nothing
call B_display()
call A_display()
endfunction

endlibrary

public function outside takes nothing returns nothing
set nestedtest_B_N= -4
call nestedDoTest()
call nestedtest_A_display()

endfunction
[/jass]

这个例子则存在语法错误:
[jass]
library nestedtest
globals
private integer N=3
endglobals

scope A
globals
private integer N=4 //Error: 'N' redeclared
endglobals
endscope

endlibrary
[/jass]

实际上,这是解析器作出了限制。下面的写法则没有错误。
[jass]
library nestedtest
scope A
globals
private integer N=4
endglobals
endscope

globals
private integer N=3
endglobals

endlibrary
[/jass]


由于子域的变量先被解析,因而解析其不再担心是否会混淆。(没有为什么,大概解析的算法不够成熟,如果子域的成员名字一定要与父域相同,那么,写在前面好了)

另外一点要记住的是,不像一般的全局变量,公有或私有全局变量必须在声明后方能使用,否则JassHelper会认为他们与一般的全局变量相同。(这里容易误会,因为JassHelper能让全局变量在任意地方声明,自然不存在什么必须先声明后使用的规则)

同一父域中的子域不能同名(在这里,可以把整个脚本文件看作最终的父域)。而子域与父域可以重名。因为解析后,它们的前缀是不同的。
[jass]
library nestedtest
scope A
function kkk takes nothing returns nothing
set N=N+5 // 在这之前,因为N没有被定义,所以JassHelper会认为他是一个全局变量
endfunction
endscope

endlibrary

scope X
scope A
//在这里定义一个域A,是没有问题的。
//因为最终,前面的会解析成nestedtest_A而这里是X_A
function DoSomething takes nothing returns nothing
endfunction

endscope
endscope
[/jass]
嵌套没有层次限制,但是最终变量名或函数名会越来越常,这也许会影响到性能。

SCOPE_PREFIX和SCOPE_PRIVATE
在域或库内,SCOPE_PREFIX和SCOPE_PRIVATE分别代表两个字符串常量。能够使得域或库的函数能通过ExecuteFunc()来执行。

SCOPE_PREFIX表示这个域或库的前缀的字符串。
SCOPE_PRIVATE表示这个域或库的私有成员的在解析是生成的随机前缀。
[jass]
scope test

private function kol takes nothing returns nothing
call BJDebugMsg("...")
endfunction

function lala takes nothing returns nothing
call ExecuteFunc(SCOPE_PRIVATE+"kol")
endfunction

endscope
[/jass]

在例子中,lala()能过通过ExecuteFunc来调用kol()。
 楼主| 发表于 2008-1-29 01:24:41 | 显示全部楼层
沙发自己做了。翻了4个小时,总算翻了一部分。剩下没翻的先留着。有什么建议说声呵..
回复

使用道具 举报

发表于 2008-1-29 11:39:43 | 显示全部楼层
虽然不懂VJ,但感觉貌似翻的很不错
回复

使用道具 举报

发表于 2008-2-3 00:23:27 | 显示全部楼层
VJ 让我看到了希望啊 C 和JAVA的语法都能用上一些 学起来应该很快 可恶的全局变量再也不用去T里定义了
回复

使用道具 举报

发表于 2008-2-3 09:37:07 | 显示全部楼层
朱朱不是翻译过了么?

vjass指南.rar

10 KB, 下载次数: 82

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-11-22 04:07 , Processed in 0.034098 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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