林登脚本语言指南.docx

上传人:b****8 文档编号:30163673 上传时间:2023-08-05 格式:DOCX 页数:37 大小:37.06KB
下载 相关 举报
林登脚本语言指南.docx_第1页
第1页 / 共37页
林登脚本语言指南.docx_第2页
第2页 / 共37页
林登脚本语言指南.docx_第3页
第3页 / 共37页
林登脚本语言指南.docx_第4页
第4页 / 共37页
林登脚本语言指南.docx_第5页
第5页 / 共37页
点击查看更多>>
下载资源
资源描述

林登脚本语言指南.docx

《林登脚本语言指南.docx》由会员分享,可在线阅读,更多相关《林登脚本语言指南.docx(37页珍藏版)》请在冰豆网上搜索。

林登脚本语言指南.docx

林登脚本语言指南

1.导论

 

林登脚本语言(LSL)是一种简单易学,并能给SecondLife中的物体赋予生命的强大语言。

它的语法与C/JAVA语言的风格类似,每一个脚本中都有一个隐型状态机。

同一个物体可以被赋予多个不同的脚本,也允许利用一个只有简单函数功能的脚本来进行升级。

这使得脚本会表现出特定的功能(如“漂浮”,“跟随”等等)也允许他们联合起来组成新的行为。

    脚本语言会被编译成可执行的代码,就像JAVA一样,代码可以在仿真器的虚拟机上运行。

所有脚本分享仿真器分配的时间片,所以一个有很多脚本的仿真器能允许每个脚本执行时间更短,而不会降低性能。

另外,每一个脚本在其自己的内存空间中运行,以防止脚本对仿真器的保护内存或者对其他脚本进行改写,使得脚本程序很难影响仿真器。

这个指南为读者介绍了LSL语言的基本特征,如何编辑和应用你的脚本,如何完善的引用标准常量,事件和库函数。

 

2.现在开始

 

你可能很想知道你可以利用LSL做些什么,你多久能掌握它。

我们要开始一些简单的例子,解剖他们,并在同时向你介绍一些脚本的开发过程。

 

2.1.HelloAvatar

 

按照惯例,程序语言的教程总是以“Hello”开始,我们也不改变这个惯例。

虽然这个小程序没有什么特别的用处,但这个例子可以让我们知道以下几件事:

"创建一个基本脚本

"脚本状态

"调用函数

"脚本事件

"在物体上应用脚本

 

2.1.1.创建脚本

 

打开你的“清单栏(Inventory)”,在下拉列表中选择“建立(Create)|新脚本(NewScript)”。

这时在你的“脚本”目录中会出现一个名为“新脚本”的空白文件。

双击脚本的文件名或图标在编辑器中打开脚本文件。

当你打开这个脚本的时候,窗口中会自动的插入一些基本的LSL语言框架。

会像下面这样:

default

{

    state_entry()

    {

        llSay(0,"Hello,Avatar!

");

    }

 

    touch_start(integertotal_number)

    {

        llSay(0,"Touched.");

    }

}

 

对这个脚本进行测试,当它进入某种状态,比如被触摸的时候。

会显示“Hello,Avatar!

”,和“Touched”。

因为这也许是你第一次看到一个程序指令,让我们来分解这段小指令,一个小段一个小段地进行解释。

 

2.1.2.默认状态

 

default

{

...

}

     

所有的LSL脚本都有一个简单的隐型状态机,它可以有一种或多种状态。

所有的脚本必须有一个默认状态,所以如果脚本只有一个状态,那么它就会是“默认”状态。

当一个脚本第一次启动或者重启时,它会从默认状态开始。

默认状态通过把default(默认)放在文档的最顶层,用花括号“{”开始,并以“}”结束。

因为默认状态是一个特殊状态,你必须把它和其他状态区别开。

每次当你进入一个状态,脚本引擎会自动的调用state_entry()事件,从那里开始生成执行代码。

当状态结束,脚本引擎会在调用下一个state_entry处理之前自动调用state_exit()事件。

在上面的例子中,我们在state_entry()中调用了llSay()函数,而并没有定义一个state_exit()。

进入(entry)和退出(exit)处理是一个初始化状态数据和清除诸如“listen”事件回调函数的地方。

在第五章中你能了解到关于默认状态,如何创建和利用其他状态的具体方法。

 

2.1.3.函数

 

LSL语言有超过200个内建函数,允许脚本和物体在环境中相互影响。

所有的内建函数均以“ll”开头。

上面的例子中两次调用了llSay()函数,llSay()的作用是在特定信道中显示文字。

llSay(integerchannel,stringtext);

 

Channel是信道的号,信道0是公共聊天信道,所有的人物都能够看到聊天信息。

信道1到信道2147483648是专用信道,人们无法看到,仅供其它脚本从中获取信息。

你可以定义自己的函数,但函数名不能是保留字,内建常量,或者内建函数。

 

2.1.4.触摸事件

 

touch_start(integertotal_number)

{

    llSay(0,"Touched.");

}

 

许多事件可以通过在你的脚本中声明一个事件处理程序来检测。

touch_start()事件会在用户触摸到物体的时候发生。

 

2.1.5.尝试

 

既然我们已经看过默认脚本,也解释了其中的一些细节,现在可以试着运行这个脚本。

点击“保存”保存这个脚本。

在保存处理中,编辑器会保存代码然后将代码编译成字节码然后再进行储存。

当你在预览窗口中看到提示“编译完成(Compilesuccessful)!

”时,编译已经完成并保存。

为了测试这个脚本,你要把它应用到一个物体中。

创建一个物体的方法是在主视窗中点击“创建”。

当那个棒棒出现的时候,你能够在世界中创建一个简单的物体。

当物体被创建时,你能把你新编写的脚本拖拽到物体上,运用这个脚本。

当你把脚本拖拽到物体上以后,你就能看到物体显示出“HelloAvatar!

”的信息。

确保触摸事件能够在你点击的时候发生。

当你点击物体时应该能在聊天历史中看到信息“Touched”。

 

2.2. 使用内建编辑器

 

内建编辑器有着基本文本编辑器所具有的大多数功能。

你可以用鼠标或shift键+方向键选中文本。

你可以使用Ctrl+X,Ctrl+C,Ctrl+V对文本进行剪切,复制,和粘贴,或者在“编辑”下拉菜单中选择上述操作。

 

2.3. 使用其他编辑器

 

因为内建编辑器支持从剪贴板粘贴,所以你可以用其他的编辑器进行脚本编辑,然后复制到SecondLife中。

3. 基础教程

既然我们已经看见一个简单程序的运行了,现在就看看如何自己编程吧.下面这些工具用来构架基本程序块从而编程,并且会在实际中应用到。

3.1. 注释

为你的脚本加上注释是一个很好的习惯,在你更新、修改或者使用别人的脚本的时候,注释会很有帮助。

除非脚本很容易懂,否则你应该加上注释。

1.在脚本的开始要注明脚本的功能

2.在每一个全局变量之前要注明它在哪一段是全局的

3.在每一个全局函数之前要注明它的功能

4.在你自己一眼看不出代码功能的地方,加些注释

LSL语言使用和Java/C++一样风格的单行注释。

//这个脚本触发一个物体的旋转

//g_is_rotating保存旋转的当前状态。

TRUE表示

//正在旋转,FALSE表示其他情况。

integerg_is_rotating=FALSE;

default

{

//在触摸时触发状态

touch(integernum)

{

if(g_is_rotating)

{

//关闭旋转

llTargetOmega(<0,0,1>,0,0);

g_is_rotating=FALSE;

}

else

{

//绕Z轴正半轴旋转,向上方。

llTargetOmega(<0,0,1>,4,1);

g_is_rotating=TRUE;

}

}

}

3.2. 算术操作

 

LSL中支持大多数算术操作,和C/JAVA语法相同。

3.2.1.赋值

大多数的算术操作都是赋值,使用“=”表示。

简单的说就是把等式右边的值赋予左边,但是左边只能是一般变量。

所有的基本类型支持赋值“=”,相等“==”,和不等“!

=”操作。

//保存目标物体信息的变量

keyg_target;

vectorg_target_postion;

floatg_target_distance;

//赋值函数范例

set_globals(keytarget,vectorpos)

{

g_target=target;

g_target_position=pos;

    //利用函数返回值来赋值

vectormy_pos=llGetPos();

g_target_distance=llVecDist(g_target_position,my_pos);

}

3.2.2.十六进制的使用

整数会以十六进制的形式输入,例如:

integerMask=0xff;  //等于整数255;

integerBit  =0x0100; //等于整数256;

3.2.3.二进制运算操作

二进制运算就像是一个有两个同类参数的函数,然后返回那个类型;然而语法有些不同。

表3-1二进制运算符

运算符

含义

+

加法

-

减法

*

乘法

/

除法

%

求模

^

异或

<< 

左移

>> 

右移

每一个运算符都有详细的解析,请参见类型章节获得详情。

3.2.4.布尔型操作

表3-2布尔运算符

运算符

含义

如果操作符左边小于右边则返回“TRUE”

如果操作符左边大于右边则返回“TRUE”

<=

如果操作符左边小于等于右边则返回“TRUE”

>=

如果操作符左边大于等于右边则返回“TRUE”

&&

如果操作符左右两边均为真则返回“TRUE”

||

如果操作符左右两边有一边为真则返回“TRUE”

!

否定运算符

3.2.5.位运算操作

表3-3位运算符

运算符

含义

&

按位与

|

按位或

~

取反

3.3. 类型

 

变量,返回值和变量都是有不同的类型的。

LSL中提供了几种基本的数据类型。

LSL类型

整型(Integer)

一个32位带符号的整型值的有效范围是从-2147483648到2147483647。

浮点(Float)

一个IEEE标准32位浮点型值得有效范围是从1.175494351E-38到3.402823466E+38。

键值(Key)

键值是在SL中一个唯一的,可以用来引用物体的标识。

矢量(Vector)

矢量值中包括3个浮点值。

矢量值可以用来指示3维位置,方向,速度,力量,脉冲,甚至颜色。

每个部分可以通过‘x’‘y’和‘z’来访问。

表3-4.矢量算术运算符

运算符

含义

+

两个向量相加

-

从一个向量中减去另一个

*

向量点乘

%

向量叉乘

旋转(rotation)

旋转型变量中有4个浮点型函数。

成员可以通过‘x’‘y’‘z’和‘s’访问。

表3-5.旋转运算符

运算符

含义

+

两个旋转量相加

-

一个旋转量减去另一个

*

将第一个旋转量转到第二个量所需要的旋转量(第二个量为目标量)

/

将第一个旋转量转到第二个量所需要的反向旋转量(第二个量为目标量)

列表(list)

列表是另外一种数据类型。

列表用‘[’‘]’括起来,内部的各个值用逗号隔开。

stringStringVar="Hello,CarbonUnit";

listMyList=[1234,ZERO_ROTATION,StringVar];

生成列表:

[1234,<0,0,0,1>,"Hello,CarbonUnit"]

列表可以和其他的列表进行组合。

比如:

MyList=3.14159+MyList;

生成列表:

[3.14159,1234,<0,0,0,1>,"Hello,CarbonUnit"]

更简单的:

MyList=MyList+MyList;

生成:

[3.14159,1234,<0,0,0,1>,"Hello,CarbonUnit",3.14159,1234,<0,0,0,1>,"Hello,CarbonUnit"]

一些库函数可以从列表中拷贝数据,分类列表,和移除子列表。

3.3.1.类型转换

类型转换分为明转换和暗转换。

明转换使用类似C语言的语法:

floatfoo_float=1.0;

integer  foo_int=(integer)foo_float;

3.3.1.1. 暗转换

LSL只支持两种暗转换:

整型(integer)到浮点型(float)和字符串(string)到键值(key)的转换。

因此,你可以在需要浮点量的地方使用一个整型,可以在需要键值的地方输入字符串。

3.3.1.2. 明转换

LSL支持以下形式的转换

IntegertoString     (整型到字符串)

FloattoInteger      (浮点型到整型)

FloattoString        (浮点型到字符串)

VectortoString           (矢量到字符串)

RotationtoString(旋转到字符串)

IntegertoList        (整型到列表)

FloattoList         (浮点型到列表)

KeytoList                 (键值到列表)

StringtoList        (字符串到列表)

VectortoList       (矢量到列表)

RotationtoList          (旋转到列表)

StringtoInteger   (字符串到整型)

StringtoFloat      (字符串到浮点型)

StringtoVector    (字符串到矢量)

StringtoRotation(字符串到旋转)

3.4. 全局函数

 

全局函数的声明也和Java/C的风格相似,只是没有void返回值。

也就是说,函数在没有返回值的时候不需要定义返回值(C语言中需要定义返回空值void):

make_physical_and_spin(vectortorque)

{

//扭矩变为2倍

vectordouble_torque=2.0*torque;

llSetStatus(STATUS_PHYSICS,TRUE);

llApplyTorque(double_torque);

}   

3.5. 全局变量

 

全局变量在文件的各处都可以调用,全局变量的声明也和Java/C语言类似,但每一行只能声明一个变量:

vectorgStartPosition;

如果需要,全局变量可以被初始化,没有初始化的全局和局部变量会被初始化成0值:

vectorgStartPosition=<10.0,10.0,10.0>

3.6. 局部变量

 

局部变量要在它的声明下才可被使用(定义必须放在调用前)。

在它们被定义的那个代码段中有效。

下面这段合法代码就类似C语言:

integertest_function()

{

//测试我们在函数中的任何地方都可以调用的向量

vectortest=<1,2,3>;

integerj;

for(j=0;j<10;j++)

{

//这个向量和上面定义的不同

//这是不好的编程习惯

vectortest=;

}

//测试失败

if(test==<9,9,9>)

{

//无法达到

}

}

4. 流控制

LSL有完整的处理条件问题的函数,比如循环或者从脚本的一点跳到另一点。

4.1. 条件语句

 

if状态操作符的语法和Java/C语言类似。

check_message(stringmessage)

{

if(message=="open")

{

open();

}

elseif(message=="close")

{

close();

}

else

{

llSay(0,"Unknowncommand:

"+message);

}

}

当小括号里面的值为非0值时,花括号里面的表达式才会有效。

当if后的括号中的值为真(不为零)时,else后的表达式就不再有效。

无效符号NULL_KEY等同于假FALSE。

当if或者elseif后面的表达式都不是非零整型是,即执行else后面的语句。

括号中的表达式可以使用常用的整型运算符或者比较运算符。

//一个函数,如果收到了一些用来指导它下一步做什么的信息。

这样的代码就像

//一个简单的块,一旦条件符合就会一下子被用到程序中。

assess_next_step(integerperm,integerattached,integerbalance,floatdist)

{

stringmsg;

if(!

attached)

{

if((perm&PERMISSION_ATTACH)&&(dist<10.0))

{

attach();

}

elseif((dist>10.0)||((dist>20.0)&&(balance>1000)))

{

move_closer();

}

else

{

llRequestPermissions(llGetOwner(),PERMISSION_ATTACH);

}

}

}

4.2. 循环结构

循环是大多数程序语言都有的一种基本构件,LSL中的循环与Java/C的结构相似。

4.2.1.for语句

当你知道一个操作需要重复多少次的时候使用for语句就显得很有用了。

就像Java或者C中的循环,括号里共有三部分,初值,循环条件,和步长。

循环在当中间的循环条件为真时运行,在循环结束时变量会自动增加。

//使一个非物理性的块延z轴向上做平缓的上移,移动的动作由许多次连续的移动组成

move_up(floatdistance,integersteps)

{

floatstep_distance=distance/(float)steps;

vectoroffset=<0.0,0.0,step_distance>;

vectorbase_pos=llGetPos();

integeri;

for(i=0;i<=steps;++i)

{

llSetPos(base_pos+i*offset);

llSleep(0.1);

}

}

4.2.2.do-while语句

当你想让一个操作不断循环但你又不知道应该循环多少次的时候do-while就显得十分有用。

对do-while的一个简单描述就是:

如果符合预先设置的条件,do-while后面括号里面的语句就会被执行,然后继续判断那个条件。

//输出附着在这个物体上的所有部件名称清单

talk_about_inventory(integertype)

{

stringname;

integeri=0;

integercontinue=TRUE;

do

{

name=llGetInventoryName(type,i);

if(llStringLength(name)>0)

{

llSay(0,"Inventory"+(string)i+":

"+name);

}

else

{

llSay(0,"Nomoreinventoryitems");

continue=FALSE;

}

}while(continue);

}

4.2.3.while语句

While循环和do-while循环很像,不同的是do-while循环先执行代码,再判断下次是否需要继续循环,而while循环先判断,符合条件以后再执行代码。

mention_inventory_type(integertype)

{

integeri=llGetInventoryNumber(type);

while(i--)

{

llSay(0,"item:

"+llGetInventory(i));

}

}

4.3. 跳转

 

Jump语句用来从一个函数或者一个事件中跳到脚本中的其他地方。

但不能跳到别的函数或者事件中。

通常,你可以在用if...else条件语句过于复杂的时候使用它。

4.4. 状态改变

 

状态改变允许你将你的脚本状态在默认状态和用户状态之间转变。

你可以通过在事件处理程序的花括号(‘{’和‘}’)之前使用一个‘state’关键字来定义你自己脚本的状态。

你可以使用这样的语法来使用新的状态调用过程:

‘state<状态名>’。

default

{

state_entry()

{

llSay(0,"Iaminthedefaultstate");

llSetTimer(1.0);

}

    timer()

{

stateSpinState;

}

}

stateSpinState

{

state_entry()

{

llSay(0,"IaminSpinState!

");

llTargetOmega(<0,0,1>,4,1.0);

llSetTimer(2.0);

}

    timer()

{

statedefault;

}

    state_exit()

{

llTargetOmega(<0,0,1>,0,0.0);

}

}

5. 状态

所有的脚本必须有一个‘默认(default)’状态,在脚本运行的时候会首先进入这个状态。

状态包括由LSL触发的事件处理程序。

所有的状态必须至少有一个事件处理程序——如果不包含任何事件处理程序,那将不是一个状态。

当状态改变时,所有的回调设置都会被保持,所有的未决事件都会被清除。

5.1.state_entry()函数

 

state_entry事件在进入一个新的状态时发生,在程序启动时,它总是作为程序的第一个事件来处理。

这个事件处理程序没有数据的传输。

你有的时候可能会为了让你的物体起作用而在state_entry()中为定时器或传感器设置一个回调函数。

警告:

这是一个常见的错误,出现这样的问题是因为假设了state_entry()会在你把一个物体从清单栏中拖出来的时候被调用。

当你把一个物体放回清单栏中的时候,这个物体当前的状态将被保存。

所以,当你再次将这个物体拖出清单栏的时候,state_entry()并没有被调用。

如果你想让每次这个物体被拖出来的时候都运行一个启动代码,你需要建立一个全局函数,并在state_entry()和on_rez()函数中都调用它。

//全局初始化函数。

init()

{

//为物体的拥有者设置一个listen函数。

keyowner=llGetOwner();

llListen(0,"",owner,"");

}

default

{

state_entry()

{

init();

}

    on_rez(integerstart_param)

{

init();

}

    listen(integerchannel,stringname,keyid,string

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 其它模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1