這篇文章因為有考慮到完全沒寫過程式的初學者,所以寫得有點雜亂(其實可能沒有這種人來這裡XD~),建議已經掌握一些要點的朋友可以大約略看看有哪些型態,並注意每一段的注意事項和紅色字體即可。
如圖,這就是LSL全部的基本架構了。切記,基本State區為default區塊,該區塊前後順序如圖,不能隨意更動位置,也就是說你自訂的State一定要在default後面,全域變數與全域函式一定要在default前面;而全域變數與全域函式的順序就沒有那麼嚴格了。
看!!LSL沒有繼承、沒有abstract、沒有overloading當然也沒有interface!!!而且他每個Script還有16KB的限制!!(16KB的限制請參考這裡:http://lslwiki.net/lslwiki/wakka.php?wakka=Memory)
很好,自high完後開始正式介紹一下LSL基本架購,由於自己程式語言學藝不精,要是自己有哪方面用詞說錯,敬請指正。
在說明前有幾點列表,算是我之後沒有詳細談到的部分,希望對那些完全沒學過程式的初學者有所幫助:
- 只要輸入"//"這兩個符號後,其後面的字皆為註解,不列入為執行的程式碼,但注意,LSL沒有像java一樣方便的駐解方式,就是/*.....*/,它只有"//"
- LSL是是case-sensitive的,也就是大小寫區分的意思
- 每個區塊均以"{"、"}"這兩個符號代表區格
- 每個全域函式與event在其名稱後面都會有個"("、")"這兩個括符
- 宣告變數、state轉移、return 型態、jump、使用@標計jump以及使用function或自訂的全域函式都得在最後加上";"這個符號
- compiler指的就是把LSL編譯成電腦看得懂的語言,也就是當你對你的script按save時下方會出現compile....等字樣。詳細相關資料可看維基百科(http://zh.wikipedia.org/w/index.php?title=Compiler&variant=zh-tw)
- save的快捷鍵為ctrl+S
- 存檔會需要一點時間,請耐心等候,要是你在comipler期間關掉檔案,檔案是會回復原來狀態的
- ctrl+z為回上一步,ctrl+y為回下一步。
- 編輯器上方多少還是有可用的工具,在Edit中有Search/Replace這個工具方便使用者找尋或替代自己要的英文字母
- 在LSL編輯器內無法輸入中文(新的Second Life軟體已經可以輸入)
- LSL的程式碼是有可能會消失在SecondLife的Database的,請記得作好備份
變數型態
- string(字串)(eg. string str="test";)
- integer(整數)(eg. integer int=2;)
- float(浮點數,意即含小數之數字)(eg. float fl=2.5;)
- vector(向量,x、y、z三方向為浮點數的向量)(eg. vector location=<1.25,4.22,3.2>;)
- rotation(角度,除了x、y、z外,還多了個s的四個浮點數。這四個浮點數字代表意義可參考這裡:http://www.lslwiki.net/lslwiki/wakka.php?wakka=quaternions,至於為何Liden不用直觀的x、y、z調角度我就不清楚了,希望知道的人可以告知一下,謝謝。)(eg. rotation rot=<0.1,02.,0.3,1.0>;)
- key(在SecondLife內,每個物件與人都有一個key,這是用來區別一個物件或人的代號,因此是一個有固定長度的特別字串)(eg. key object="595c90dc-feff-5a80-9401-0cc111e8d8bb";)
- list(有點像一般程式都會有的陣列,但不必宣告此陣列是屬於何型態。你只要想成是放很多不同型態的變數放在裡面的列表就可以了,不過list內不可以放list,雖然compiler會過,但程式碼正式執行會有run-time error)(eg. list test=["test",2,5.33,<1.0,2.0,4.2>,<0,0,0,1>];)
變數型態 變數名稱;
例如:
integer test;
代表test這個變數是integer(整數)
若要給他一個值,就加個"=",
例如:
integer test=2;
意思便是test這個變數是2的意思。
給值的部分當然可以之後的全域函式或Event內再給,但是絕對不可以在全域變數區寫:
integer test;
test=2;
以上寫法只可以在全域函式或Event內寫。
名詞解釋:
全域變數:只要把變數宣告在default前就是全域變數。
區域變數:只要把變數宣告在event或全域函式(在LSL的函式只有全域函式)內就是區域變數。變數名稱可以和其他區域的區域變數重複。
注意事項:
- 全域變數的宣告名稱不可重複,也不可以和state或函式名稱重複
- 變數宣告不可宣告在event與state之間,不然不能compiler
- vector和rotation內的浮點數其實可以輸入整數,只是這樣會比較慢。來源:這裡的第三段文字(LSL does do some implicit typecasting but the LSL compiler does not actualy convert types or simplify code; it just makes implicit typecasts explicit. So while <0,> is valid, it will use more bytecode and be slower then <0.0,>. For this reason please be sure to use a decimal point. 0. is enough you don't need an extra zero.
)
強制轉型
通常一個function(我習慣稱method)會要求你傳入的是字串或浮點數或整數,如果要傳入的是浮點數你直接傳整數,是行得通的,它會自動幫你轉成浮點數。但如果是要求傳入整數,你傳入浮點數,或字串是不行的,此時要在前面加個(integer)例如:float fl=5.2;
string str="5";
integer int1=(integer)fl;
integer int2=(integer)str;
此時int1和int2都為5。
同理,像是llSay這個function有需要string,你只要這樣寫llSay(0,(string)5),他就會把5變成"5"了。以上範例只能適用在宣告區域變數,宣告全域變數不可這樣用,不然compiler不會過(很有可能是LSL的bug),不過基本上轉型在合理區塊都是可以用的。
布林值
LSL沒有boolean這個變態,喔,是變數型態,但是他可以藉由integer來得知這是true還是faluse。例如:integer T=TRUE;
integer F=FALSE;
TRUE和FALSE都要大寫,代表這是LSL的Constant,也就是說你不可以宣告一個變數名稱叫TRUE。而TRUE代表1,FALSE代表0。
LSL的流程控制有以下幾個,這些都可在event或全域函式內撰寫:
- if-else(如果if()內的條件為TRUE就執行,之後跳到下一個else if()內判斷,如再為FALSE就執行else內的程式碼。注意,這裡的else if與else可以不寫)
(eg.
if(TRUE)
{
......
}
else if(test==2)
{
......
}
else
{
......
}
)
流程圖可看此篇文章的下面圖片(http://squall.cs.ntou.edu.tw/cprog/Materials/IfStatement.html),基本上所有程式語言都是大同小異的。 - while(和do-while、for都是迴圈。只要符合while()內的條件,就會不斷重複執行{}內的程式碼,直到條件不符合為止)
(eg.
while(TRUE)
{
.........
}
)
以上為無限迴圈,永遠跳不出來,除非你使用了jump。 - do-while(先執行do內的程式碼再判斷while內的條件,如果成立後便一直執行。)
(eg.
do
{
......
}while(進迴圈條件)
) - for(()內的格式如下:
for(初始化;條件;改變)
初始化和改變可以不寫,只有條件的話跟while是沒啥兩樣的。)
(eg.
integer index;
for(index=0;index<5;index++)
{
......
}
在index等於5前都不會跳出,也就是說這程式碼會執行五次,index++意即index=index+1一直遞增下去。) - jump(嗯……就是……跳!在流程內的程式碼跳到指定的名稱那一行,從那一行之後開始執行,而這標計用@自訂名稱表示。jump 自訂名稱,@自訂名稱。)
(eg.
jump 自訂名稱;
................程式敘述
@自訂名稱;) - return(回傳所需型態的結果,有何用處會在之後介紹如何使用全域函式解釋)
(eg. return 所需變數型態的結果;) - state(轉移state)
(eg.state 你想要轉移的state名稱; )
- 在全域函式要寫轉移State有一個很大的bug,就是你直接在函式內寫state 你想要轉移的state名稱;,compiler是會error的,這時要脫褲子放屁一下,你要寫:
if(TRUE)
state 你想要轉移的state名稱;
default state如下:
default
{
event名稱()
{
}
}
而自訂State方式如下:
state 你想要的名稱
{
event名稱()
{
}
}
event的列表全部都有列在上面Event的連結,在這邊只會提到幾個較常使用的:
state_entry:這是所有state進入後都會先執行的event。
touch_start:只要玩家點選PieMenu的Touch,就會啟動目前程式跑到的state裡面的這個event。
timer:只要啟動了llSetTimerEvent(浮點數)這個function,這個event就會啟動。那個浮點數代表每幾秒啟動一次timer內的程式碼。
注意:
- 同樣的Event可以被允許重複寫在同一個state上,雖然這個一點用都沒有,但是LSL是不會當例外處理的,當event被啟動時,他會啟動寫在最後面的event。因為本人曾經發生過不小心把程式碼複製貼上兩次,在debug時覺得自己沒寫錯怎會出錯,原來出錯是出在這種地方。
- 如果該Event被啟動,但程式碼還停留在迴圈內,那這個程式碼會先處理迴圈,再來處理Event的啟動。如:
state_entry()
{
while(TRUE)
{
llSetTimerEvent(1);
}
}
timer()
{
llSay(0,"fuck!!!!!!");
}
由於這個程式很乖,所以它不會在每秒鐘喊fuck!!!!!!
主要是因為無限迴圈永遠跑不出去,所以他永遠不會執行Event,代表只要有迴圈在,Event這裡並不能同步執行 - 當state轉移時,event會被抽離,如果你下一個state有上一個state想要繼續啟動的event,這很有可能會造成資訊遺漏,所以要是能的話,我在這邊建議state轉換儘量少使用(但這個前題是有想要做遊戲這種需要每秒一直有event持續啟動的專題)
- 雖然state轉換時會把event抽離,但只有timer這個event例外,他會從先前state的timer轉移執行現在state的timer。如果有必要在兩個state都用到timer的話,務必請在轉移state前把timer啟動設為0(代表不啟動timer),請重新在另一個state設定llSetTimerEvent()。以下為發生轉移state照樣執行另一個state的timer的情形:
default
{
state_entry()
{
llSetTimerEvent(1);
}
timer()
{
state A;
}
}
state A
{
state_entry()
{
}
timer()
{
llSay(0,"state A timer()");
}
}
全域函式
在全域函式中,包含了要回傳和不回傳的型式。而你的自訂函式格式可以如以下四種:
- 變數型態 自訂函式名稱(){.......程式碼.......}
- 變數型態 自訂函式名稱(變數型態 自訂傳入變數名){.......程式碼.......}
- 自訂函式名稱(){.......程式碼.......}
- 自訂函式名稱(變數型態 自訂傳入變數名){.......程式碼.......}
(eg.
string caculateYourCup(string name)
{
if(name=="Tina")
{
return "A Cup";
}
return "B Cup";
}
這涵式的意思是,當遇到名字為Tine的人就回傳"A cup",其餘為"B Cup"
)
注意:
- 這裡可沒有什麼overloading這種東西。overloading就是同名字的函式,但傳入不同的變數
簡單來說,連結內可以說是LSL的API,如前面有用到的llSay(0,"fuck!!!!")便是使用了LSL的Function。
基本上我介紹LSL會把重心擺在這,藉由要做的物件來分享一些LSL的Function使用方法。所以基本的介紹就到此告一段落了……如果有LSL的相關問題,歡迎來詢問(跟SL有關的問題,也可以唷!SL不是SecondLife,是ShortLin!!!嗚……SecondLife把我的SL搶走了……Zzz)。
沒有留言:
張貼留言