2008年3月31日 星期一

啥SL—【如何讓字元顯示在Texture上做留言板】


  相信有研究過LSL一段時間的人都會知道,想在Second Life搞出動態的2D圖片,能用到的大概只有llSetTexture之類修改表面材質的method。而想要輸出動態更改的文字,所能用的大概也只有llSetText。因此,我這圖上的Weco Borad Beta,到底是如何做到的呢?

  如果仔細將我上面所提到的東西思考,我想大家應該都想得到原理,那就是準備所有字元的Texture,然後當使用者想要什麼字時,就顯示該Texture。

  可是問題是這要實在是太花錢了,所以在Sloodle(Second Life+Moodle)所提供免費的物件當中,你會發現,他們用了一個非常棒的方法。事實上,每一個被上傳的Texture都會有自己的id值,你只要知道其id值,你就可以利用llSetTexture拿來引用到你的物件上(eg.llSetTexture("701917a8-d614-471f-13dd-5f4644e36e3c", 0);//這個就是將index為0那一面材質設為透明,如何得知該面的index值,可以參考這個影片)。

  而Sloodel很聰明地將ASCII可顯現的95個字元上五個他們自訂的字元用成一個10*10的Texture,如圖:


  然後利用llSetPrimitiveParams一一取出每個字元,看到這裡會不會覺得發明這個的人很屌?他厲害的可還不只這裡,他並不是每一個字元一個Box,他是每五個字元一個Prism,利用參數修改,將五個面呈現在正面,並利用LSL將每個顯示的字元平均顯示在五個面。如圖:

  他的參數是將Prism的Patch Cut Begin and End 的B設為0.199,E設為0.800,Hollow設為68,如此一來,正面就會有五個面,他就利用了一個物件顯示五個字元,大大節省了物件成本(畢竟一個土地的物件量有限)。


  而由於中間的Texture所站的大小和其他Texture顯示不一樣,他還特地利用了llSetPrimitiveParams寫死,取出適當的大小。
  
  簡單來講原理就是這個樣子,而他所寫的程式碼如下:


////////////////////////////////////////////
// XyText v1.2 Script (5 Face, Single Texture)
//
// Written by Xylor Baysklef
//
// Modified by Kermitt Quirk 19/01/2006
// To add support for 5 face prim instead of 3
//
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING = 204000;
integer DISPLAY_EXTENDED = 204001;
integer REMAP_INDICES = 204002;
integer RESET_INDICES = 204003;
integer SET_CELL_INFO = 204004;
integer SET_FONT_TEXTURE = 204005;
integer SET_THICKNESS = 204006;
integer SET_COLOR = 204007;

// This is an extended character escape sequence.
string ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string EXTENDED_INDEX = "12345";

// Face numbers.
integer FACE_1 = 3;
integer FACE_2 = 7;
integer FACE_3 = 4;
integer FACE_4 = 6;
integer FACE_5 = 1;

// Used to hide the text after a fade-out.
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters. Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel = 1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0 means don't fade out.
float gCellHoldDelay = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
// \" <-- Fixes LSL syntax highlighting bug.
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
// Calculate the offset needed to display this character.
integer Row = index / 10;
integer Col = index % 10;

// Return the offset in the texture.
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
// Set the primitive textures directly.

// <-0.256, 0, 0>
// <0, 0, 0>
// <0.130, 0, 0>
// <0, 0, 0>
// <-0.74, 0, 0>

llSetPrimitiveParams( [
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
]);
}

RenderString(string str) {
// Get the grid positions for each pair of characters.
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

RenderWithEffects(string str) {
// Get the grid positions for each pair of characters.
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

// First set the alpha to the lowest possible.
llSetAlpha(0.05, ALL_SIDES);

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); // Now turn up the alpha until it is at full strength.
float Alpha;
for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
llSetAlpha(Alpha, ALL_SIDES);
// See if we want to fade out as well.
if (gCellHoldDelay < 0.0)
// No, bail out. (Just keep showing the string at full strength).
return;
// Hold the text for a while.
llSleep(gCellHoldDelay);
// Now fade out.
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
llSetAlpha(Alpha, ALL_SIDES);
// Make the text transparent to fully hide it.
llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
// Look for escape sequences.
list Parsed = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
integer ParsedLen = llGetListLength(Parsed);

// Create a list of index values to work with.
list Indices;
// We start with room for 5 indices.
integer IndicesLeft = 5;

integer i;
string Token;
integer Clipped;
integer LastWasEscapeSequence = FALSE;
// Work from left to right.
for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
Token = llList2String(Parsed, i);

// If this is an escape sequence, just set the flag and move on.
if (Token == ESCAPE_SEQUENCE) {
LastWasEscapeSequence = TRUE;
}
else { // Token != ESCAPE_SEQUENCE
// Otherwise this is a normal token. Check its length.
Clipped = FALSE;
integer TokenLength = llStringLength(Token);
// Clip if necessary.
if (TokenLength > IndicesLeft) {
Token = llGetSubString(Token, 0, IndicesLeft - 1);
TokenLength = llStringLength(Token);
IndicesLeft = 0;
Clipped = TRUE;
}
else
IndicesLeft -= TokenLength;

// Was the previous token an escape sequence?
if (LastWasEscapeSequence) {
// Yes, the first character is an escape character, the rest are normal.

// This is the extended character.
Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

// These are the normal characters.
integer j;
for (j = 1; j < TokenLength; j++)
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
}
else { // Normal string.
// Just add the characters normally.
integer j;
for (j = 0; j < TokenLength; j++)
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
}

// Unset this flag, since this was not an escape sequence.
LastWasEscapeSequence = FALSE;
}
}

// Use the indices to create grid positions.
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

integer ConvertIndex(integer index) {
// This converts from an ASCII based index to our indexing scheme.
if (index >= 32) // ' ' or higher
index -= 32;
else { // index < 32
// Quick bounds check.
if (index > 15)
index = 15;

index += 94; // extended characters
}

return index;
}


  上面這個程式碼只是他在全域函式的程式碼,並不是完整的LSL code,而你只要把物件調好,在default前放入這個Script,就可以直接利用他寫的RenderString(string str)將五個字元顯示在一個物件之上。

  那本人那個留言板,就是將每個物件編上名字(Mx-y),代表他是顯示留言內容(以M代表)的第x行第y列,而NameX-Y就是顯示名字的第X行第Y列。將好幾個物件組成並改寫他們的名字,之後Link起來,利用llMessageLinked傳送,而這時每個物件就會因為他們的位置不同而取得適當的文字,並將他顯示出來。

  原理大概就是這個樣子,我那個留言板的功能只有當某人點Touch,說話後,他就會顯示是誰在那個板子說了什麼話。我列一下程式碼:
母物件程式碼(只是單純的一個白色板子):

integer listenHandle;

string message1="Please leave one message.There is 200 charactors that you can leave.(only English)";

integer oneLineLimit=20;// a line just has 20 charactors
integer messageLimit=200;//all message just has 200 charactors.

string guestName;
integer LM_NAME=100;
integer LM_MESSAGE=101;

default
{
state_entry()
{
llSetText("WECO Board Beta",<0,1,1>,1);

}

touch_start(integer total)
{
guestName=llDetectedName(0);

listenHandle=llListen(0,guestName,"","");

llSay(0,message1);//tell user leave message.

}

listen(integer ch,string name,key id, string str )
{
//llSay(0,guestName);//for test
//llSay(0,(string)llStringLength(guestName));

if( llStringLength(guestName) <= (oneLineLimit-6) )//because " said:"has 5 charactors
{
llMessageLinked(LINK_SET,LM_NAME,guestName,"");
}

else
{

}

if( llStringLength(str) <= messageLimit )
{
llMessageLinked(LINK_SET,LM_MESSAGE,str,"");

}

else
{
llSay(0,"OOPS!!Your message's charactors are over 200.Please reduce your message and say it again.Thanks!!");
}

llResetScript();

}
}

顯示字元的物件(這個有分顯示Name和訊息,但程式碼幾乎一模一樣,只是物件名稱不太一樣而已,該程式是顯示名字的,所以在Default那的截取是Name,不是M):

////////////////////////////////////////////
// XyText v1.2 Script (5 Face, Single Texture)
//
// Written by Xylor Baysklef
//
// Modified by Kermitt Quirk 19/01/2006
// To add support for 5 face prim instead of 3
//
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING = 204000;
integer DISPLAY_EXTENDED = 204001;
integer REMAP_INDICES = 204002;
integer RESET_INDICES = 204003;
integer SET_CELL_INFO = 204004;
integer SET_FONT_TEXTURE = 204005;
integer SET_THICKNESS = 204006;
integer SET_COLOR = 204007;

// This is an extended character escape sequence.
string ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string EXTENDED_INDEX = "12345";

// Face numbers.
integer FACE_1 = 3;
integer FACE_2 = 7;
integer FACE_3 = 4;
integer FACE_4 = 6;
integer FACE_5 = 1;

// Used to hide the text after a fade-out.
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters. Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel = 1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0 means don't fade out.
float gCellHoldDelay = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
// \" <-- Fixes LSL syntax highlighting bug.
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
// Calculate the offset needed to display this character.
integer Row = index / 10;
integer Col = index % 10;

// Return the offset in the texture.
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
// Set the primitive textures directly.

// <-0.256, 0, 0>
// <0, 0, 0>
// <0.130, 0, 0>
// <0, 0, 0>
// <-0.74, 0, 0>

llSetPrimitiveParams( [
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.12, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
]);
}

RenderString(string str) {
// Get the grid positions for each pair of characters.
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

RenderWithEffects(string str) {
// Get the grid positions for each pair of characters.
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );

// First set the alpha to the lowest possible.
llSetAlpha(0.05, ALL_SIDES);

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); // Now turn up the alpha until it is at full strength.
float Alpha;
for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
llSetAlpha(Alpha, ALL_SIDES);
// See if we want to fade out as well.
if (gCellHoldDelay < 0.0)
// No, bail out. (Just keep showing the string at full strength).
return;
// Hold the text for a while.
llSleep(gCellHoldDelay);
// Now fade out.
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
llSetAlpha(Alpha, ALL_SIDES);
// Make the text transparent to fully hide it.
llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
// Look for escape sequences.
list Parsed = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
integer ParsedLen = llGetListLength(Parsed);

// Create a list of index values to work with.
list Indices;
// We start with room for 5 indices.
integer IndicesLeft = 5;

integer i;
string Token;
integer Clipped;
integer LastWasEscapeSequence = FALSE;
// Work from left to right.
for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
Token = llList2String(Parsed, i);

// If this is an escape sequence, just set the flag and move on.
if (Token == ESCAPE_SEQUENCE) {
LastWasEscapeSequence = TRUE;
}
else { // Token != ESCAPE_SEQUENCE
// Otherwise this is a normal token. Check its length.
Clipped = FALSE;
integer TokenLength = llStringLength(Token);
// Clip if necessary.
if (TokenLength > IndicesLeft) {
Token = llGetSubString(Token, 0, IndicesLeft - 1);
TokenLength = llStringLength(Token);
IndicesLeft = 0;
Clipped = TRUE;
}
else
IndicesLeft -= TokenLength;

// Was the previous token an escape sequence?
if (LastWasEscapeSequence) {
// Yes, the first character is an escape character, the rest are normal.

// This is the extended character.
Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

// These are the normal characters.
integer j;
for (j = 1; j < TokenLength; j++)
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
}
else { // Normal string.
// Just add the characters normally.
integer j;
for (j = 0; j < TokenLength; j++)
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
}

// Unset this flag, since this was not an escape sequence.
LastWasEscapeSequence = FALSE;
}
}

// Use the indices to create grid positions.
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );

// Use these grid positions to display the correct textures/offsets.
ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
}

integer ConvertIndex(integer index) {
// This converts from an ASCII based index to our indexing scheme.
if (index >= 32) // ' ' or higher
index -= 32;
else { // index < 32
// Quick bounds check.
if (index > 15)
index = 15;

index += 94; // extended characters
}

return index;
}

integer LM_NAME=100;
integer LM_MESSAGE=101;

default
{
state_entry()
{
// Initialize the character index.
ResetCharIndex();

}

link_message(integer linknum, integer num, string str, key id)
{
//llSay(0,str);//for test
//llSay(0,(string)num);//for test

if(num==LM_MESSAGE)
{
//llSay(0,str);//for test

//llSay(0,llList2String( llParseString2List( llGetObjectName(),[],["Name"] ) ,1));//for test

integer x= (integer)llList2String( llParseString2List( llList2String( llParseString2List( llGetObjectName(),[],["M"] ) ,1),["-"],[]) ,0);

integer y=(integer)llList2String( llParseString2List( llList2String( llParseString2List( llGetObjectName(),[],["M"] ) ,1) ,["-"],[]) ,1);

//llSay(0,"x="+(string)x+",y="+(string)y);//for test

integer indexHead=(x-1)*5+(y-1)*20;
integer indexTail=indexHead+4;

RenderString( llGetSubString(str,indexHead,indexTail) );

}
}
}


  總之,差不多就是這樣,我有在Sand Box同學做的妙手回春那裡放置,我想有興趣的同學應該可以直接買來修改。而這程式碼最重要的功能是由Sloode提供的SloodleToolbar裡我抓出來的程式寫成的。所以並不完全是我做的,只是他開放大家可以修改而已……

  我想這個做出來後,想必有非常多的應用,比如把網頁上留言板的留言利用HTTPRequest傳進Second Life,並顯示,當然也可以做更多比我做的留言板還要好的功能,比如記錄每個留言者的內容寫入notecard(對於notecard程式我還沒碰過,不過我想應該可以),然後還有換頁功能……等

  當然類似的想法可能還有小畫家(利用touch event和更改texture畫出直線)……等,或許各位專題要是想不到做什麼,可以往這些方向去想……

這就是Sloodle提供的Sloodle Toolbar

2008年3月28日 星期五

啥SL—【利用llGetObjectDetails得知物件相關訊息】

  list llGetObjectDetails(key id, list params)這個method可以得知一個物件以下訊息:

1.物件或人物名稱

2.物件或人物位置

3.物件或人物的角度

4.物件或人物的速度

5.物件的擁有者

6.物件的Group

7.物件的創造者

8.物件的敘述

  這個method是會回傳一個list屬性,而你必須輸入一個key值的參數和一個list的參數。key值的參數就是一個物件或人物的id,在Second Life裡面每個人事物都有個id,其實也就是老師上課提到的UUID。而另一個list是要輸入什麼呢?就是你想要知道的 資訊,比如說,你想要知道該key值為"00000000-0000-0000-0000-000000000000"的物件名稱,你可以寫:


key id= "00000000-0000-0000-0000-000000000000";

list testList=llGetObjectDetails( id,[OBJECT_NAME]);


  他就會把這個key值名稱放入testList了,而想要say出來變成字串,只要多多利用List的llList2String(list src, integer index)(或llList2Key、llList2Rot、llList2Float、llList2Vector、llList2Integer),總之相當於get(index)取LinkedList裡面的東西就是了,只是LSL這個list他裡面能放的東西屬性不唯一。

所有的參數如下表格:

參數 變數型態 如果key值為物件 如果key值為人
OBJECT_NAME string

回傳物件名稱,有一點像 llKey2Name

這個method。

回傳該key值的人物名稱。
OBJECT_DESC string 回傳該物件敘述 回傳空字串
OBJECT_POS vector 回傳該物件位置 回傳該人物位置
OBJECT_ROT rotation 回傳該物件角度 回傳該人物角度
OBJECT_VELOCITY vector 回傳該物件的速度 Returns agent velocity.
OBJECT_OWNER key 回傳擁有者的key值

如果該物件被Deed給某Grouo的話,回傳NULL_KEY

回傳該人的key值
OBJECT_GROUP key 回傳該物件的Group 回傳NULL_KEY
OBJECT_CREATOR key 回傳該物件的創造者 回傳NULL_KEY

  其實和這個類似的method有:llGetKey、llGetCreator、llGetObjectName……等,但是這些都是只能得知該Script的物件相關訊息而已。 並不像我現在介紹的llGetObjectDetails那麼廣。

  而得知這些訊息有什麼用?像是llGetCreator我就認為超有用的,這個在我們專題開發時並沒有這個method,害我們不能百分之百過濾不是我們 的武器打到怪物不會損血(我們是判斷物件名字與物件的Group)。而像是得知一個人的位置,我也可以寫一個一直追你的Robot。

2008年3月2日 星期日

啥?【慶祝電腦玩家創刊200期!!】


  我還記得……我第一本電腦玩家…是怎麼來的。

  1992年11月第16期,這是,我的第一本電腦玩家。

  那個時候,我還只是幼稚園大班呢。我從來沒有忘記,那天的情境……就跟我是如何拿到我人生第一款正版電腦遊戲"聖域傳說"……一樣清楚,我還記得我當年一直把它念成聖"城"傳說呢……雖然到現在仍然沒破關,但是,那個遊戲畫面,我一直沒有忘記……不過,這是題外話了。

  我記得那天我一如往常地和老爸在賣電腦遊戲的地方看著許許多多的遊戲。當時,我看到了火箭人的畫面……超帥……

  小時候我都不敢跟我爸說,我要買哪個玩具,或我要買哪個遊戲……不是因為老爸太嚴,而是自己很扭扭捏捏,自己想要的東西,都不敢說……每次都是老爸問起,我才敢講……雖然,我不確定那個時候是不是也是這樣。但我一直記得我想要玩這款遊戲的渴望……

  可是問題是,我那時才幼稚園,ABCD都不認識了,更何況要玩這款國外遊戲?所以老爸當時就買了另一款大宇出的遊戲—"激鬥戰士"來替代它。

  其實當時我是很沮喪的……我還記得那天老爸和我坐在台電大樓前面,有個很高台階的地方……(那地方還在呢……而且一點都沒有變……),他告訴我說火箭人是英文的,我不會懂。之後,就拿出
1992年11月第16期電腦玩家的雜誌,指著當時的評析,告訴我說"激鬥戰士"應該也不錯玩。
  小時候我是不看文章的,根本也不知道這個到底哪裡好玩。不過,我完全沒想到,後來我和我姊姊竟然也曾為了玩它而沉浸過好幾天。我還記得當時,看到每個角色全破畫面的莫名感動。嗯?我有那麼厲害?小時候就會玩這種格鬥遊戲?我還記得我應該不會招式表上的任何一招呀……後來我仔細回想,那時候的電腦玩家是有附秘技的……看了一下,是直接看結局……

  嗯……好像也不是這麼回事。不過我依稀記得當年的確有輸入秘技,然後我一路亂按按得很爽,就破關了。印象中不是直接看結局呀……這是和我回憶產生矛盾的地方……不過不管是怎樣的秘技,我一直記得當時我對著旁邊的方向鍵亂按,然後按到每個角色都破關……看著每個角色的結局……

  其實我現在仔細想想,那時候老爸買給我激鬥戰士不是沒有原因的……因為小時候,我其實最常玩的是紅白機。只愛玩動作遊戲,而電腦遊戲大部分都是要花腦筋思考的,只有動作遊戲是不必花腦筋的……只是這種動作遊戲,我覺得比一般一路砍到底,也就是瑪利兄弟那種ACT相比,難太多了……
  現在覺得蠻遺憾的就是,當年所有買的電腦遊戲,大部分的盒子封面都被我爸丟了。就連這款激鬥戰士,也不例外。(因為太大太站位了……我爸只好把所有遊戲集起來放在一個盒子)

  呵,這就是我第一本電腦玩家得手的經過,雖然中間廢話了不少……

  後來沒有繼續訂,但到了國小一年級的時候。我忘了是什麼原因,我爸問我要不要訂電腦玩家……那一期,是1993年9月號第26期。相隔10個月,我又再度看到了它。不過,我只訂了12期就沒續訂了。其實我爸訂電腦玩家給我的用意,是希望我能夠藉著遊戲學電腦,但我根本沒興趣,只想玩遊戲。而雜誌內的文章,我也都沒興趣,我只看圖和先賭為快看看會有什麼新遊戲,所以評析什麼的我都快速翻過,然後再看我最喜歡看的漫畫街。所以到現在如果講起什麼骨灰遊戲,不管我有沒有玩過,或我有沒有破關,我第一印象就是電腦玩家的圖……像是朱學恒先生的炎龍騎士團二評析,那下面一橫排的圖片,就是我對炎龍騎士團二的另一個印象。

炎龍騎士團的遊戲開頭

當年朱學恒對炎龍2的評析

  耶,你或許會覺得奇怪,炎龍騎士團二評析不是應該在1995年才可能出的嗎?嗯,這評析是在1995年9月號第50期所刊登的,在這之前,我雖然都沒有訂電腦玩家,但我爸都有零星幫我買了幾份……大概是和他逛電腦店買的。

  之後,或許是因為我爸有訂PC Office的關係,所以從電腦玩家1996年11月第64期也開始幫我訂(那時我小四),這一訂,除了中間漏掉的77、78期,就一直看到現在……(後來聽說在7-11買比較能幫助電腦玩家業務,所以不再續訂,都跑去7-11買了XD)

  我一直沒有忘記,我會一直在遊戲基地發言,和大家參與討論。不知不覺增進我寫作實力,讓我看到網路世界的寬廣,就是看到有一次遊戲基地在電腦玩家打的廣告……拉霸可以抽ps2XD~

  雖然我沒有因此一直發言賺G幣,因為我也不喜歡為了這種事而亂灌水。所以只拉了幾次霸,就放棄了……而也讓我一待基地,就待了七年,這是另一個故事了……

  嗯,這真的是陪伴我長大的一本好雜誌。雖然我承認在我高中以前,我都只看圖片還有漫畫街!上高中後才慢慢地去看徐社長講了什麼、後面的專欄講了什麼。這時候我開始注意到了朱學恒的奇幻專欄……不過,這又是另一個故事了……

  後來真正跑去仔細看以前那些評析在講什麼,那些編輯在講什麼的時候,是在我大二時,想要整理電腦玩家的時候。那時候看了以前的雜誌,真的覺得,難怪他們能夠撐那麼久,因為,真的好有內容呀!

  而且,我開始從遊戲基地的電腦玩家板與社長說的話注意到一個很有意思的讀者—秋風。他的文章也有在骨灰集散地精華區裡被集結在一起,叫秋風雜談。

  我特地把那幾段文章掃瞄起來(123)(如電腦玩家或當事者認為不妥,請告知)……再看看遊戲基地裡,電腦玩家板裡當年徐社長和這位讀者的回應……我不知道有沒有人會像我一樣,有種莫名的感動……(雖然基地的搜尋並不完全,但還是可以利用"guan0000"關鍵字看得到一些秋風先生的文章)

  你可以看到,當一個雜誌在轉變時,一個死忠讀者的不捨;也看到他的支持,隨著徐社長的離開,也開始放棄。但你回頭看看這幾篇文章的時候,你又會覺得,這些,曾經是很溫暖的……

  我不知道有多少人能夠這樣把他們的故事看完,但是,我相信,只要是有看完的朋友們,只要你也是個老骨灰,你絕對、絕對,會有所感動的。你會看到徐社長當年創下ACE的精神,也會看到遇到市場轉變的衝擊,更會看到那些死忠讀者面對轉變的無奈,那種情感,是我這邊說再多,也無法表達的……

  而現在看到徐社長還在為台灣的電玩奮鬥、努力,真的是……又有種莫名的感動……從徐社長在之前電玩展演講中留下的個人資料(雖然應該要叫他徐總,但我還是習慣叫他社長……)可以看到,他目前正在努力推廣台灣電子競技,而這是台灣電競官網。我有去聽該場演講,目前社長想要做的是,將玩遊戲變成和打籃球與棒球一樣的理念轉為現實。也就是說,他希望台灣也能有夠像韓國一樣的職業團隊,而這些職業團隊就像是NBA一樣(沒騙你,看了該演講的介紹,韓國的職業玩家就像是NBA球星一樣……)……都有舉辦季賽之類的……雖然台灣目前規模不大,但這是徐社長為台灣遊戲界,所踏出的第一步……

  嗯,好像又扯遠離電腦玩家了……不過,也正是因為電腦玩家,才讓我了解更多的世界……遊戲基地、OOPS、遊戲的製作……等……很多……很多……還有,我要感謝電腦玩家在玩家論壇裡錄取了我七篇文章……真的,當我得知我的文章刊登在影響我這輩子最重要的一本雜誌時,我高興到在床上跳來跳去……這也是,讓我寫作,開始有自信的一步……真的,很謝謝你—電腦玩家

  題外話,電腦玩家的官網雖然找不到以前的的網頁。但我從google裡找到官網其實還保留一些老遊戲的秘技唷。

  在這邊說一下我因為對電腦玩家的感謝而製作的仿第一期偽封面……如有不妥請告知呢。我本來是想在旁邊文章的標題打上什麼三國志100之類的,總之就是希望電腦玩家能夠持續為我們報導遊戲,不過感覺好像會被認為幫三國志打廣告,所以就算了XD~總之,恭喜電腦玩家創刊200期!

  最後,贈詩一首!下台一鞠躬<(__)>

雷風火半片天,四面楚歌唯人強,
穿肚破誰知曉?把酒開懷笑四方。
世不恭且莫取,便化青史讓人賞,
家戶戶都能瞭,字字汗青留今朝!