西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁(yè)編程開(kāi)發(fā)其它知識(shí) → GameMonkey參考手冊(cè)官方資料翻譯

GameMonkey參考手冊(cè)官方資料翻譯

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:本站整理時(shí)間:2010/12/13 9:01:50字體大。A-A+

作者:佚名點(diǎn)擊:122次評(píng)論:0次標(biāo)簽: GameMonkey luna rotc

  • 類型:定時(shí)工具大。846KB語(yǔ)言:多國(guó)語(yǔ)言[中文] 評(píng)分:6.6
  • 標(biāo)簽:
立即下載
 GameMonkey 腳本參考手冊(cè)
使用lua已經(jīng)1年多了, 至今還常常驚嘆于其作者設(shè)計(jì)的簡(jiǎn)潔和提供給用戶的一套機(jī)制, "工具就在那里擺著, 去發(fā)揮你的想象力吧"~~~ lua的接口最好的體現(xiàn)了提供機(jī)制而不是策略的思想. 在游戲編程精粹中, 看到一篇關(guān)于介紹腳本語(yǔ)言的文章, 其中不光介紹了lua, 也介紹了GameMonkey :) 大概做了一下了解, 發(fā)現(xiàn)國(guó)內(nèi)好像尚無(wú)使用的先例, 資料也比較少, 本著學(xué)習(xí)借鑒, 開(kāi)拓思路的態(tài)度, 決定翻譯GameMonkey的官方資料, 希望能對(duì)需要的人有幫助. 其中也有我自己認(rèn)為要詳細(xì)說(shuō)一下的, 提醒一下的, 用rotc注出來(lái)了

comments 注釋
// 和c++一樣注釋到本行末

/*

和c / c++ 一樣的注釋塊

*/

注釋塊會(huì)被編譯器忽略掉, 它的作用是給代碼做注釋或者臨時(shí)使一段代碼失效[調(diào)試腳本時(shí)常用]


變量和常量
GameMonkey不像c, Pascal那樣的強(qiáng)類型語(yǔ)言, 它更像是Basic語(yǔ)言.

GM中的變量是以下類型中的一個(gè):

null -- 沒(méi)有值, 這有類型

int -- 32bit的有符號(hào)整數(shù)

float -- 32bit的浮點(diǎn)數(shù)

string -- null結(jié)尾的ansi字符串

table -- 數(shù)組/hash容器

function -- 函數(shù)

user -- 用戶自定義類型

rotc: 如何理解null 是一種類型, 而不是一個(gè)值?

對(duì)c++er 和 cer來(lái)說(shuō), null/NULL基本就是0, 或(void*)0, 它的確是一個(gè)具體的值. 這里說(shuō)GM中的null是個(gè)類型, 可能會(huì)在理解上有一定的困難. 根源是靜態(tài)類型和動(dòng)態(tài)類型造成的. 靜態(tài)類型語(yǔ)言中, 類型和值是分裂開(kāi)來(lái)的, 對(duì)于靜態(tài)類型語(yǔ)言中的變量i來(lái)說(shuō), 如果能夠通過(guò)編譯, 那么i的類型就肯定是確定的. 靜態(tài)類型語(yǔ)言中的類型, 只是用來(lái)說(shuō)明如何對(duì)&i這樣一個(gè)內(nèi)存地址做解釋(但問(wèn)題在于這個(gè)說(shuō)明是在編譯期就必須決定的). 也就是說(shuō)c中的變量直接映射到了內(nèi)存和如何解釋內(nèi)存. 而動(dòng)態(tài)語(yǔ)言通過(guò)引入一個(gè)間接層, 其值的結(jié)構(gòu)是 Value(type, realvalue), 也就是說(shuō), 一個(gè)變量由一個(gè)內(nèi)存和附帶一個(gè)指示如何解釋該內(nèi)存的標(biāo)志(及類型)組成的. 這樣的好處是顯而易見(jiàn)的, 可以在運(yùn)行時(shí)改變變量類型(也就是改變對(duì)內(nèi)存的解釋方式), 下面演示動(dòng)態(tài)語(yǔ)言中如何實(shí)現(xiàn)變量賦值時(shí)決定類型.

比如我創(chuàng)造了一門動(dòng)態(tài)語(yǔ)言, 這門語(yǔ)言中有2個(gè)類型, 那么這樣實(shí)現(xiàn)


enum {null, man, woman}; // 兩個(gè)類型加一個(gè)null類型

struct Value{ Value(){type = null; pData = 0;} char type; void* pData}; // 動(dòng)態(tài)語(yǔ)言中的變量

struct Man {Man(int h, int c){housevalue = h; carvalue = c;} int housevalue; int carvalue}; // 男類型內(nèi)容是房產(chǎn)和車產(chǎn)

struct Woman { Woman(char* name) {strcpy(sweetname, name);} char sweetname[12]; }; // 女類型有一個(gè)可愛(ài)的名字

在我的腳本中:

Value pp; // 定義個(gè)一個(gè)變量, 注意, 現(xiàn)在這個(gè)變量不是一個(gè)Man, 也不是一個(gè)Woman, [但它有類型--null, 但是它沒(méi)有值]

pp = Man(5,3); //制造一個(gè)富家男, 注意pp 現(xiàn)在的類型由null變成man, 值是一個(gè)Man

// 實(shí)現(xiàn) void operator = (Value& v, Man& m) {

v.type = man; // 賦類型

v.pData = &m; // 賦值

}

pp = Woman(“X姐"); // 制造了一個(gè)X姐[芙蓉姐, 鳳姐], 注意pp現(xiàn)在的類型由man變成women了, 值是一個(gè)Woman

// 實(shí)現(xiàn) void operator = (Value& v, Man& m) {

v.type = woman; // 賦類型

v.pData = &m; // 賦值

}

pp = null;

// 實(shí)現(xiàn) ..... v.type = null;
當(dāng)你掩去c++的實(shí)現(xiàn)時(shí), 腳本:




Value pp;

pp = Man(5, 3);

pp = Woman(“X姐”);

pp = null;
上面就展示了如何在腳本語(yǔ)言中實(shí)現(xiàn)所謂的一個(gè)變量既能存int (Man), 又能存string(Woman), 還能只有類型沒(méi)有值(type==null)的奧秘, 其實(shí)就是引入了一個(gè)間接層, 把靜態(tài)語(yǔ)言編譯時(shí)就要確定某個(gè)內(nèi)存地址要怎么解釋, 變成了{(lán)解釋方式, 內(nèi)存}這種形式, 這樣的話, 可以在運(yùn)行期改變解釋方式和值[當(dāng)然他們是匹配的], [ 可以注意到, 動(dòng)態(tài)分配內(nèi)存是支持這種實(shí)現(xiàn)的不可缺少的機(jī)制, 垃圾收集的需求也伴隨而來(lái)]

最后總結(jié): null表示不解釋~~~:) 你懂的


GM中, 變量名是大小寫敏感的, __t0 和 __t1保留做內(nèi)部使用.

變量名 = [a..zA..Z] + [a..zA..Z_0..9]*

例子:

a = null; // a 沒(méi)有值

b = 4; // b 是int類型

c = 4.4; // c 是float類型

d = “hello”; // d 是string類型

e = table(); // e是一個(gè)空的表

f = function() {}; // f 是一個(gè)函數(shù)
更多的例子:

a = ‘SMID’; // a 是一個(gè)int, 值為(‘S’<<24 | ‘M’<<16 | ‘I’<<8 | ‘D’)

b = .23; // b 是一個(gè)float

c = 2.4f; // c 是一個(gè)float

d = ‘c:\windows\sys’; // d是一個(gè)string


語(yǔ)言和它的標(biāo)準(zhǔn)函數(shù)總是試圖保留值, 然而不是保留類型. 具體規(guī)則是當(dāng)不同類型的變量在一起運(yùn)算時(shí), 高級(jí)別的類型將被保留. 類型從低級(jí)到高級(jí)的順序是: int, float, string.

例子:

print(“hello” + 4); // 輸出: hello 4, 4的類型被提高

print(2 + 5); // 輸出: 7, int類型被保留

print(2.0 + 5); // 輸出: 7.0, 5的類型被提高

print(sqrt(17.0)); // 輸出: 4.1231, float類型被保留

print(sqrt(17)); // 輸出: 4, int類型被保留


int類型賦值的例子:

a = 179; // 十進(jìn)制

a = 0xB3; // 十六進(jìn)制

a = 0b0011001 // 二進(jìn)制

a = ‘BLCK’; // 字符轉(zhuǎn)成4個(gè)byte分別賦予int的四個(gè)byte中


float類型賦值例子:

b = 45.34; // float十進(jìn)制

b = .345; // float

b = 289.0; // float

b = 12.34f; // c風(fēng)格float

b = 2.3E-3; // 科學(xué)計(jì)數(shù)法


字符串賦值例子:

c = “c:\\path\\file.ext”; // 標(biāo)準(zhǔn)雙引, 用\做轉(zhuǎn)義字符

c = ‘c:\path\file.ext’; // 和上面一樣, 單引情況下, \不做轉(zhuǎn)義字符用

c = “Mary says \”hello\””; // 相當(dāng)于'Mary says "hello"'

c = 'Chris' 's bike'; // 相當(dāng)于'Chris's bike', 也就是說(shuō)在單引內(nèi)部表示單引的方法是連續(xù)兩個(gè)單引

c = “My ” “house”; // 相當(dāng)于"My house"


基礎(chǔ)類型可以使用標(biāo)準(zhǔn)內(nèi)建庫(kù)進(jìn)行顯示的轉(zhuǎn)換, Int(), Float(), String()

例子:

a = 10;

b = a.String(); // 這樣是最好的, 顯示的調(diào)用類型轉(zhuǎn)化函數(shù), 返回轉(zhuǎn)化后的值

b = “” + a; // 這樣不好, 賦值會(huì)將a的類型提升到string, 但是效率底下

b = (10).String(); // 丑陋的

b = 10.String(); // 錯(cuò)誤的, 編譯不過(guò), 因?yàn)榫幾g器不認(rèn)同這種語(yǔ)法


引用類型變量的可引用類型有String, Function, Table, User. 當(dāng)這些變量被賦值時(shí), 并不發(fā)生full copy, 而只是讓變量指向具體的obj

例子:

a = table(“apple”, "orange"); // a是一個(gè)指向table的引用

b = a; // b 現(xiàn)在和a指向同一個(gè)table

b[1] = "banana"; // 設(shè)置b[1]

print(a[0], a[1]); // >> banana orange

print(b[0], b[1]); // >> banana orange


當(dāng)一個(gè)變量被賦新值時(shí), 該變量原來(lái)持有的值就有可能丟失掉了.

例子:

Oper = function(a, b){

return a + b

}; // Oper現(xiàn)在指向一個(gè)函數(shù)

Oper = “hello”; // Oper現(xiàn)在指向字符串, 原來(lái)的函數(shù)被丟失了


函數(shù)
語(yǔ)法: function(<params>) { <statements> };

一個(gè)函數(shù)體是一個(gè)值, 而函數(shù)是一個(gè)類型 {type = GM_FUNCTION, value=function...}

注意: 記住在將函數(shù)賦值給變量后面那個(gè)分號(hào), 這是語(yǔ)法必須的

例子

// 將一個(gè)創(chuàng)建一個(gè)rect table的函數(shù)賦值給CreateRect

CreateRect = function(posX, posY, sizeX, sizeY){

rect = table(x=posX, y=posY, width=sizeX, height=sizeY);

rect.Area = function() {return .width * height; };

return rect;

};

myRect = CreateRect(0, 0, 5, 10); // 創(chuàng)建一個(gè)用于描述rect的table

area = myRect.Area();

// 可以用:代替.來(lái)隱式的傳遞一個(gè)this指針

Size = function(){

return .width * .height;

};

s = myRect:Size(); // 調(diào)用時(shí), myRect會(huì)當(dāng)做this指針傳入Size中


作用域
和作用域有關(guān)的一些關(guān)鍵字, 語(yǔ)法:

global <variable>

Local <variable>

member <variable>

this

this.<variable>

.<variable>

函數(shù)中的變量.



默認(rèn)情況下, 一個(gè)在函數(shù)中使用的變量就是這個(gè)函數(shù)的本地變量. 如果要聲明一個(gè)全局變量, 需要使用global關(guān)鍵字. 訪問(wèn)成員變量必須通過(guò)this或者是使用member關(guān)鍵字聲明它是一個(gè)成員變量. 在局部使用的變量可以用local關(guān)鍵字聲明.

例子:

Access = function(){ // Access 是一個(gè)local變量, 它引用著一個(gè)函數(shù)

apple = 3; // apple 是函數(shù)的一個(gè)local變量

global apple; // 把a(bǔ)pple聲明成全局作用域

local apple; // 把a(bǔ)pple聲明成局部作用域

member apple; // 把a(bǔ)pple聲明成 this的member變量

this.apple; // 明確的訪問(wèn)this.apple

.apple // 隱式的訪問(wèn)this.apple

};
例子:

a = 13; // a 是一個(gè)local作用域變量

print(b); // b是null

global b = function() { // b是一個(gè)全局作用域的變量, 類型是GM_FUNCTION

global c = 2; // c是一個(gè)全局作用域的變量

d = 3; // d是函數(shù)局部作用域變量

{ if (c == 2)

{ local e = 3; } // e 從這一刻開(kāi)始成為函數(shù)局部作用域變量, 注意沒(méi)有塊作用域變量

}

print(e); // e = 3

}
在查找一個(gè)變量時(shí), 按照l(shuí)ocal 和 parameters, 然后global的順序查找.



成員變量有微妙的不同:

h = function() { // h 是一個(gè)local變量

global a = 3; // a 是一個(gè)全局變量

member n; // n可以從this被訪問(wèn)和創(chuàng)建

d = 3; // d是函數(shù)局部作用域

this.b = 3; // b是member作用域

.b = .x + 1; // b, x都是member作用域

print(b); // b 是 null, 因?yàn)檫@里并沒(méi)有l(wèi)ocal的b

print(n); // 就像print(this.n)一樣, 因?yàn)樯厦骘@示聲明過(guò)了

};
全局作用域中的語(yǔ)句.

x = 7; // local

global x = 8; // global

a = function(y) {

local x = 5; // function local

dostring(“print(x);”); // 這里打出8, 和lua一樣, dostring總是在全局環(huán)境下編譯運(yùn)行的, 無(wú)法訪問(wèn)function的變量和parameters
};

變量可以是虛擬機(jī)全局作用域的, 也可以是某個(gè)函數(shù)作用域的, 或者是某個(gè)obj比如table的成員作用域的. 當(dāng)執(zhí)行一個(gè)文件或者是執(zhí)行一個(gè)字符串的時(shí)候, 和lua一樣, 文件或者是字符串被編譯成一個(gè)無(wú)名的函數(shù), 所以默認(rèn)情況下, 其中最外層的未加特別申明的變量是該無(wú)名函數(shù)的函數(shù)作用域的.

this總是存在的. 它或者是null, 或者是一個(gè)有效的值. 你可以傳遞this, 或者是使用重載冒號(hào)操作符默認(rèn)的this. 這一特性多用在創(chuàng)建諸如類似模板行為, 那些地方的obj的操作往往只有run-time時(shí)才能確認(rèn). this也用在創(chuàng)建線程, 例子:

obj:thread(obj.DoThings) // 開(kāi)始一個(gè)線程, 并把obj作為this傳遞給它

obj:MakeFruit(apple, orange) // 調(diào)用MakeFruit, 并把obj當(dāng)做this傳給它



語(yǔ)法和操作符


! Not 邏輯取反

~ 每一個(gè)bit位取反

^ bit位使用與或 XOR

| bit位使用或 OR

& bit位使用與 AND

>> bit位右移

<< bit位左移

~= bit位取反賦值

^= bit位 XOR 賦值

|= bit位 OR 賦值

&= bit位 AND 賦值

>>= bit位 右移 賦值

<<= bit位 左移 賦值

= 賦值

' 單引 其中的字符會(huì)當(dāng)做int值

" 雙引 字符串(處理轉(zhuǎn)義字符)

` 反引 字符串(不處理轉(zhuǎn)義字符)

[] 方闊 用index取talbe元素

. 取table元素

: 給函數(shù)傳遞this

+ 數(shù)學(xué)+

- 數(shù)學(xué)-

* 數(shù)學(xué)*

/ 數(shù)學(xué)/

% 模取余

+=, –=, *=, /=, %= 數(shù)學(xué)運(yùn)算并賦值

{} 界定語(yǔ)句塊

; 語(yǔ)句結(jié)束標(biāo)志

<, <=, >, >=, == 數(shù)學(xué)比較

&&或and 邏輯AND

|| 或or 邏輯OR



Tables 表
語(yǔ)法: table(<key> = <value>, ...);

table(<value>, …);

{<key>=<value>, …, };

{<value>, …, };

table可以被同時(shí)認(rèn)為是array 和 map. 因?yàn)閠able中可以容納data和function, 所以table也可以被認(rèn)為是class, table中也可以容納別的table, 這時(shí)它也已被認(rèn)為是Tree.

初始化table的例子:

fruit = table("apple", "banana", favorite= "tomato", "cherry");

fruit = {"apple", "banana", favorite="tomato", "cherry"};

這時(shí), fruit的樣子就是:

fruit[0] = “apple”;

fruit[1] = “banana”;

fruit[2] = “cherry”;

fruit[“favorite”] = "tomato"; 也可以寫作是 fruit.favorite = "tomato"
可以注意到, fruit.favorite="tomato"并沒(méi)有占據(jù) element[2], 雖然它在邏輯上應(yīng)該是element[2]的位置, 但是它不是一個(gè)index索引成員, 是一個(gè){key, value}成員.

從表中取得元素的例子.

a = thing.other; // other 是table thing中的一個(gè)成員

b = thing[“other”]; // 相當(dāng)于b = thing.other

c = thing[2]; // c取得了thing中的第三個(gè)indexd索引成員

index = 3;

d = thing[index]; // 用int做下標(biāo), 就可以把table當(dāng)數(shù)組訪問(wèn)

accoc = “fav”;

e = thing[accoc]; // 用string做下標(biāo), 就可以把table當(dāng)map訪問(wèn)
注意, thing["Fav"]和thing["fav"]是兩個(gè)不同的東西, 因?yàn)镚M是大小寫敏感的. 這樣做設(shè)計(jì)上的考慮是:

1) 賦值可能是string, 也可能是任何類型的值.

2) 要做到大小寫無(wú)關(guān), 底層需要一些額外的工作量, 這會(huì)產(chǎn)生一定量的效率問(wèn)題.

設(shè)置table中成員的值的例子.

thing.other = 4;

thing[3] = “hello”;

嵌套表的例子:

matrix = { {1, 2, 3,}, {4, 5, 6}, {7, 8, 9,}, } //

print(“matrix[2][1] = ”, matrix[2][1]); // 輸出"matrix[2][1] = 8"


關(guān)鍵字if和else
語(yǔ)法: if ( <condition> ) { <statements> }

或者 if ( <condition> ) { <statements> } else { <statements> }

或者 if ( <condition> ) { <statements> } else if ( <condition> ) { <statements> } else { <statements> }



例子:

foo = 3;

bar = 5;

if ((foo * 2) > bar){

print(foo * 2, “is greater than”, bar);

}

else{

print(foo * 2, “is less than”, bar);

}
// 輸出: 6 is greater then 5

if 會(huì)計(jì)算條件表達(dá)式的值, 并根據(jù)其結(jié)果的true/false來(lái)選擇執(zhí)行那一段語(yǔ)句.

if 在計(jì)算條件時(shí), 會(huì)像大多數(shù)語(yǔ)言那樣, 并且實(shí)現(xiàn)了短路求值, 下面是一些例子:

if (3 * 4 + 2 > 13) == if ( ( (3*4) + 2) > 13 )

if (3 > 0 || 0 > 1) 3 > 0恒真, 那么永遠(yuǎn)不會(huì)去對(duì)0 > 1求值

對(duì)c程序員的提示: 你不能把condition和一個(gè)單語(yǔ)句無(wú)語(yǔ)句塊標(biāo)示的statements寫在同一行, 這是語(yǔ)法不允許的

例: if ( a > 3) b = 4; // 錯(cuò)誤, b = 4 必須被{}包起來(lái)



關(guān)鍵字for
語(yǔ)法: for (<statement1>; <condition>; <statement2>) { <statements> }

例子:



for (index = 0; index < 6; index = index + 2){


print(“Index = ”, index);

}
輸出是:

Index = 0

Index = 2

Index = 4



for 語(yǔ)句的執(zhí)行和大多數(shù)語(yǔ)言一樣, 循序是

1. 執(zhí)行 statement1

2. 執(zhí)行condition, 是false就退出for

3. 執(zhí)行statements

4. 執(zhí)行statement2, goto 2



關(guān)鍵字foreach
語(yǔ)法: foreach (<key> and <value> in <table>) { <statements> }

foreach (<value> in <table>) { <statements> }

例子:

fruitnveg = table("apple", "orange", favorite = "pear", yucky="turnip", "pinapple");

foreach(keyVar and valVar in fruitnveg){

print(keyVar, “=", valVar);

}
輸出是:

2 = pinapple

0 = apple

favorite = pear

1 = orange

yucky = turnip

注意到遍歷tale的時(shí)候, 它并沒(méi)有按料想的順序來(lái)輸出. 事實(shí)上, 這種順序會(huì)在table中的元素填入和刪除時(shí)發(fā)生變化.

在foreach的每次迭代過(guò)程中, key和value將會(huì)作為循環(huán)體的local作用域變量. 在迭代過(guò)程中, 對(duì)table執(zhí)行刪除元素操作是安全的, 但是向table中新增元素和從table刪除元素是[原文是: Although the foreach iteration is ‘delete safe’, the behaviour of adding and removing items from a table while iterating is undefined. 我理解不了delete safe 和 removing items from a table] 請(qǐng)大家告訴我好的理解, 我好改正



關(guān)鍵字 while
語(yǔ)法: while( <condition> ) { <statements> }

例子:

index = 0;

while ( index < 3 ) {

print("index = ", index);

index = index + 1;

}
輸出:

index = 0

index = 1

index = 2

while結(jié)構(gòu)先檢查條件, 如果條件為真就執(zhí)行循環(huán)體并反復(fù)執(zhí)行這一過(guò)程直到第一次檢查到條件為假. 如果一開(kāi)始條件就為假, 那么循環(huán)體中的代碼一次也不會(huì)執(zhí)行.



關(guān)鍵字 dowhile
語(yǔ)法: dowhile (<condition>) { <statements> }

例子:

index = 0;

dowhile (index > 0) {

print("index = ", index);

}
輸出:

index = 0

dowhile和while不同, 它先執(zhí)行循環(huán)體, 然后檢測(cè)條件已決定是否要再次執(zhí)行循環(huán)體, 循環(huán)體中的代碼至少執(zhí)行一次.



關(guān)鍵字 break, continue, return
break的例子:

for (index = 0; index < 4; index = index + 1) {

if (index == 2) {

break;

}

print(“index =”, index);

}
輸出:

index = 0

index = 1



continue的例子:

for (index = 0; index < 4; index = index + 1) {

if (index == 2) {

coutinue;

}

print(“index = ”, index);

}
輸出:

index = 0

index = 1

index = 3



return 的例子:

Add = function(a, b) {

return a + b;

}

print(“Add res = ”, Add(4, 5));
輸出:

Add res = 9



Early = function(a) {

if (a < = 0) {

print(“Dont want zero here.”);

return ;

}

print(“Above zero we handle.”);

}

Early(-2);
輸出:

Dont want zero here.

break和continue用來(lái)退出或者是忽略 for, while, dowhile, foreach 循環(huán). break會(huì)使執(zhí)行流跳出循環(huán)語(yǔ)句塊, continue導(dǎo)致終止本輪迭代, 并立即進(jìn)行下一輪迭代, return不光是能跳出循環(huán), 它是直接跳出當(dāng)前函數(shù).



關(guān)鍵字true, false, null
在GM中, true和false分別表示0和1. 除此之外沒(méi)有別的意義. 注意, 和其他語(yǔ)言不太一樣的地方

例子:

a = 3;

if (a == true) {

print(a, “==", true);

}else{

print(a, “!=", true);

}
輸出:

3 != 1

null是一種類型, 而不是一個(gè)值, 它通常用來(lái)表示錯(cuò)誤. 當(dāng)它和其他類型混用時(shí), 它的類型會(huì)被提升, 值為0. 當(dāng)一個(gè)變量被聲明但沒(méi)有賦值時(shí), 這個(gè)變量就是一個(gè)null. 對(duì)于table中的元素, 如果被賦值為null, 就表示這個(gè)元素被從table中刪掉了.

例子:

local var;

if (var) { // var聲明了但沒(méi)賦值, 所以是null, 這里類型提升了, 值為0 == false

print(“var was initialised or non zero : ”, var);

} else {

print(“var is zero or null : ”, var);

}
輸出:

var is zero or null : null



--------------------------------------------------- 高潮分割線 ---------------------------------------------------------

上面那些我覺(jué)得正常人1~2個(gè)小時(shí)應(yīng)該能掌握了, 我翻譯的昏昏欲睡了, 下面是一些GM內(nèi)建的機(jī)制, 能夠體現(xiàn)出一些特色, 這個(gè)正是我想要的:), 其實(shí)可以把下面的這些東西看做是GM的庫(kù), 也可以看成是GM內(nèi)置的功能.



Thread
rotc: 這里有幾點(diǎn)要說(shuō)的

1. GM里的thread不是通常的線程, 其實(shí)就是lua里的協(xié)程.

2. GameMonkey開(kāi)發(fā)的初衷有彌補(bǔ)當(dāng)時(shí)的LuaV4沒(méi)有協(xié)程的遺憾, 現(xiàn)在luaV5已經(jīng)有了.

3. 從接口來(lái)看, GM中的協(xié)程接口更加豐富易用.



1. int thread(function a_function, …)

創(chuàng)建一個(gè)線程.

a_function 是線程的執(zhí)行體

... 是傳給a_function的參數(shù)

該函數(shù)返回一個(gè)thread id, 控制和查詢線程都必須通過(guò)這個(gè)id來(lái)進(jìn)行.



2. void yield()

調(diào)用該函數(shù)導(dǎo)致當(dāng)前執(zhí)行體讓出對(duì)GM虛擬機(jī)的控制權(quán).



3. void exit()

調(diào)用本函數(shù)導(dǎo)致當(dāng)前執(zhí)行體立即退出.



4. void threadKill(int a_threadId)

kill掉指定的線程, 被kill掉的線程不能再次運(yùn)行.

a_threadId是要kill的線程的id, 由thread()函數(shù)返回.

如果調(diào)用threadKill()將導(dǎo)致當(dāng)前線程被kill掉.



5. void threadKillAll(bool a_killCurrentThread = false)

kill掉所有的線程, 參數(shù)為false的話kill掉出自己外的所有線程, 否者連自己也kill掉.



6. void sleep(float a_time)

停止當(dāng)前執(zhí)行體指定的秒數(shù).



7. int threadTime()

返回當(dāng)前線程執(zhí)行的總時(shí)間, 單位是毫秒.



8. int threadId()

返回當(dāng)前線程的id



9. table threadAllIds()

用一個(gè)table返回所有的線程id.



10. void signal(var a_event)

引發(fā)一個(gè)事件. a_event是任意類型的, 表示一個(gè)事件.



11. void block(var a_event, ...)

讓當(dāng)前線程阻塞在一個(gè)或多個(gè)事件上. 只到事件發(fā)生, 該線程才能被轉(zhuǎn)化為可執(zhí)行的.



States
在游戲編程中, 常使用狀態(tài)的概念來(lái)描述一個(gè)游戲?qū)嶓w的行為, 就是常常說(shuō)到的有限狀態(tài)機(jī). 在GM中, states允許一個(gè)線程結(jié)束后馬上丟棄這個(gè)線程的棧并跳到另一個(gè)執(zhí)行點(diǎn)開(kāi)始執(zhí)行.



1. void stateSet(function a_function, …)

設(shè)置當(dāng)前執(zhí)行線程的新的狀態(tài)函數(shù).

a_function 表示要執(zhí)行的狀態(tài)函數(shù).

... 表示要傳給狀態(tài)函數(shù)的參數(shù).



2. function stateGet()

獲取當(dāng)前執(zhí)行的狀態(tài)函數(shù). 如果之前沒(méi)有調(diào)用過(guò)stateSet, 那么將返回null.



3. function stateGetLast()

獲取當(dāng)前狀態(tài)的前一個(gè)狀態(tài), 可以用來(lái)得知變遷信息.



4. void stateSetExitFunction(function a_function)

設(shè)置一個(gè)函數(shù), 該函數(shù)將在狀態(tài)變遷時(shí)調(diào)用. 可以用來(lái)在進(jìn)入下一個(gè)狀態(tài)前本次狀態(tài)函數(shù)本身的一些清理工作, 如果沒(méi)有下一個(gè)狀態(tài), 那么這個(gè)函數(shù)不會(huì)被執(zhí)行.



rotc: 其實(shí)這個(gè)state的實(shí)現(xiàn)要求虛擬機(jī)實(shí)現(xiàn)了尾遞歸, 否者在狀態(tài)過(guò)多的時(shí)候會(huì)導(dǎo)致滿棧, 實(shí)現(xiàn)了尾遞歸和協(xié)程的語(yǔ)言都可以做出states類似的功能, 但是GM中顯示的給出的支持, 也是很方便的.



System
1. void debug()

使調(diào)試器在這里產(chǎn)生一個(gè)斷點(diǎn).



2. void assert(int a_condition)

檢查a_condition, 它必須是非0值, 否者產(chǎn)生一個(gè)異常然后退出線程.



3. int sysTime()

返回虛擬機(jī)執(zhí)行的實(shí)現(xiàn), 單位是毫秒.



4. int doString(string a_script, int a_executeNow = true)

編譯并執(zhí)行a_script中的腳本

a_executeNow == true的話, script將馬上被執(zhí)行, 然后doString函數(shù)才返回, 否者返回新建的thread id.

實(shí)質(zhì)的步驟是:

1. 把 a_script 編譯成一個(gè)函數(shù)func

2. 調(diào)用 id = thread(func)

3. if a_executeNow == true

yield()

else

return id



5. int typeId(variable a_var)

返回a_var的類型值



6. string typeName(variable a_var)

返回a_var的類型名字



7. int typeRegisterOperator(int a_typeid, string a_opName, function a_func)

給指定的類型注冊(cè)一個(gè)操作

a_typeid 目標(biāo)類型

a_opName 操作名

a_func 現(xiàn)實(shí)的操作函數(shù)

返回1成功, 0失敗.

a_opName的取值: getdot, setdot, getind, setind, add, sub, mul, dov, mod, inc, dec, bitor, botxor, shiftleft, shiftright, bitinv, lt, gt, lte, gte, eq, neq, neg, pos, not



8. int typeRegisterVariable(int a_typeid, string a_varName, variable a_var)

給指定的類型注冊(cè)一個(gè)變量, 使用(type).varname的方式就可以獲得這個(gè)變量

a_typeid 目標(biāo)類型

a_varName 要加的變量名

a_var 要加的變量

返回1成功, 0失敗.



9. int sysCollectGarbage(int a_forceFullCollect = false)

如果當(dāng)前內(nèi)存使用量超過(guò)了指定的內(nèi)存使用量, 那么執(zhí)行垃圾回收.

a_forceFullCollect 如果垃圾回收可用的話, a_forceFullCollect=true將馬上開(kāi)始執(zhí)行

返回1表示垃圾回收?qǐng)?zhí)行了, 其他情況返回0.



10. int sysGetMemoryUsage()

返回當(dāng)前的內(nèi)存使用量, 單位byte.



11. void sysSetDesiredMemoryUsageHard(int a_desired)

設(shè)置內(nèi)存使用量硬限制. 在垃圾回收時(shí)會(huì)根據(jù)這個(gè)值來(lái)決定要不要執(zhí)行一次完整的回收.

a_desired 內(nèi)存使用硬限制, 單位是byte.



12. void sysSetDesiredMemoryUsageSoft(int a_desired)

設(shè)置內(nèi)存使用量軟限制. 在垃圾回收時(shí)會(huì)根據(jù)這個(gè)值來(lái)決定是否開(kāi)始增量回收. soft值必須小于上面的hard值, 謝謝

a_desired 內(nèi)存使用軟限制, 單位是byte.



13. void sysSetDesiredMemoryUsageAuto(int a_enable)

開(kāi)啟或者關(guān)閉在接下來(lái)的垃圾收集中是否能自動(dòng)調(diào)整內(nèi)存限制.

a_enable 1 開(kāi)啟 0 關(guān)閉



14. int sysGetDesiredMemoryUsageHard()

獲取內(nèi)存使用量硬限制, 單位是byte. 注意, 這個(gè)值是用在開(kāi)始一次完整的垃圾回收前的檢測(cè).



15. int sysGetDesiredMemoryUsageSoft()

獲取內(nèi)存使用量軟限制, 單位是byte. 注意, 這個(gè)值是用在開(kāi)始增量垃圾回收前的檢測(cè).



16. int sysGetStatsGCNumFullCollects()

返回虛擬機(jī)執(zhí)行完整垃圾回收的次數(shù).



17. int sysGetStatsGCNumIncCollects()

返回虛擬機(jī)執(zhí)行增量垃圾回收的次數(shù). 注意在restart的那一次回收中, 這個(gè)值會(huì)+2.



18. int sysGetStatsGCNumIncWarnings()

返回GC 或者VM因?yàn)榕渲玫膯?wèn)題[soft和hard限制]而導(dǎo)致的警告的數(shù)量. 如果這個(gè)數(shù)龐大而且急速增加, 那么gc的軟硬內(nèi)存限制應(yīng)該重新配置以獲得更好的性能表現(xiàn). 這些警告的出現(xiàn)一般意味著gc次數(shù)過(guò)于平凡或不足. 如果這個(gè)值很小, 或者是增長(zhǎng)很慢, 那么不用去擔(dān)心它. 可以閱讀介紹GM的gc的文檔[翻譯完這個(gè), 我就翻譯GM gc的文檔]來(lái)獲取關(guān)于gc話題的很多信息. 我們將在以后的版本中改進(jìn)這個(gè)函數(shù), 以便讓它的意義很明確易懂.



表操作
1. int tableCount(table a_table)

計(jì)算table中元素的個(gè)數(shù).



2. table tableDuplicate(table a_table)

返回傳入table的一個(gè)副本.

我測(cè)過(guò)了, copy的深度就是a_table的元素這一層, 比如

t1={a=9}; t2={uu=t1, b=45};

t3 = tableDuplicate(t2);

t3.b = 78;

t3.uu.a = 80;

print(“t2.b = ”, t2.b); // t2.b = 45

print(“t3.b = ”, t3.b); // t3.b = 78

print(“t2.uu.a = ”, t2.uu.a); // t2.uu.a = 80

print(“t3.uu.a = ”, t3.uu.a); // t3.uu.a = 80
啥內(nèi)涵大家一看就明白了





------------------------------------------華麗的風(fēng)格線-------------------------------------------



綁定C函數(shù)到GM腳本中
C函數(shù)可以綁定到類型上, 也可以綁定到一個(gè)全局變量.

一個(gè)可以綁定到GM中的C函數(shù)的原型是:

int GM_CDECL gmVersion(gmThread* a_thread)

a_thread->GetNumParams() 可以獲得參數(shù)的個(gè)數(shù)

a_thread->Param*() 獲取各個(gè)參數(shù)

a_thread->GetThis() 訪問(wèn)this

a_thread->Push*() 向腳本中返回值

還有一些有用的宏和簡(jiǎn)寫的功能函數(shù).



C函數(shù)的返回值描述如下:

GM_OK 函數(shù)執(zhí)行成功

GM_EXCEPTION 函數(shù)執(zhí)行失敗, 在函數(shù)運(yùn)行的thread中產(chǎn)生一個(gè)運(yùn)行時(shí)異常

當(dāng)然函數(shù)也可能返回一些控制thread行為的值, 比如GM_SYS_SLEEP, GM_SYS_YIELD, GM_SYS_KILL, 這些值可以讓腳本的高級(jí)用戶實(shí)現(xiàn)和修改虛擬機(jī)的行為. 用戶擁有強(qiáng)大的控制權(quán), 可以更高效的實(shí)現(xiàn)參數(shù)變量, 重載函數(shù)(通過(guò)支持不同類型的參數(shù)), 檢查錯(cuò)誤或無(wú)效的輸入.



一個(gè)GM操作符綁定函數(shù)的原型是:

void GM_CDECL dunc(gmThread* a_thread, gmVariable* a_operands)

a_operands[0] 是左參數(shù)

a_operands[1] 是右參數(shù)

a_operands[0] 同時(shí)也是返回值

如果操作符函數(shù)不能執(zhí)行該操作(比如錯(cuò)誤的參數(shù)類型等), 就把a(bǔ)_operands[0]置為null

對(duì)于二元操作符來(lái)說(shuō), 比如O_MUL, 調(diào)用操作符函數(shù)時(shí)將選擇兩個(gè)參數(shù)類型較高的參數(shù)的綁定函數(shù). NOT是一個(gè)一元操作符(這時(shí)將使用a_operands[0].m_type的綁定函數(shù)). 這一點(diǎn)和c++是不一樣的, 在c++中, 如果你創(chuàng)建了一個(gè)類 Vec3, 那么Vec3 * float 的運(yùn)算就需要重載一個(gè)*操作符, 而float * Vec3需要重載一個(gè)全局的友元函數(shù). GM這樣做是為了降低原生類型的處理代價(jià)和易于用戶定義類型的擴(kuò)展. 所以原生的int 和 float 類型不需要在意那些比他們高級(jí)的類型, 但是用戶自定義類型例如Vec3可以很有彈性的和低級(jí)類型一起工作, 它的綁定函數(shù)將被調(diào)用.

可能發(fā)生沖突的地方就是當(dāng)用戶自定義類型之間發(fā)生運(yùn)算時(shí), 如果用戶知道注冊(cè)的順序的話, 他們可以依據(jù)這個(gè)來(lái)編碼, 否者可能要實(shí)現(xiàn)同樣的操作符函數(shù)來(lái)保證不會(huì)發(fā)生因?yàn)樽?cè)順序而導(dǎo)致的問(wèn)題. 兩個(gè)用戶類型可以給一個(gè)操作符綁定同樣的操作符函數(shù), 這樣可以避免不必要的重復(fù).

rotc: 上面這兩段話我翻譯的不好, 先放著, 等對(duì)這部分知識(shí)有了更深的理解再來(lái)修改





例子1, 實(shí)現(xiàn)一個(gè)可以注冊(cè)到GM中的C函數(shù), 比較簡(jiǎn)單, 不寫注釋了

// int GetHealth(GObj* a_obj, int a_limit)

int _cdecl GetHealth(gmThread* a_thread) {

GM_CHECK_NUM_PARAMS(2);

GM_CHECK_USER_PARAM(GObj::s_scrUserType, userObj, 0);

GM_CHECK_INT_PARAM(limit, 1);

Gobj* gob = (Gobj* )userObj->m_user;

if (gob->m_health > a_limit) {

gob->m_health = a_limit;

}

a_thread->PushInt(gob->m_health);

return GM_OK;

}




例子, 向GM中導(dǎo)入一個(gè)函數(shù), 使得在GM中可以使用sqrt(56)或者sqrt(67.8), 過(guò)程比較簡(jiǎn)單就不寫注釋了

int __cdecl gmfSqrt(gmThread* a_thread) {

GM_CHECK_NUM_PARAMS(1);

if (a_thread->ParamType(0) == GM_INT) {

int intVal = a_thread->Param(0).m_value.m_int;

a_thread->PushInt((int)sqrt(intVal));

return GM_OK;

} else if (a_thread->ParamType(0) == GM_FLOAT) {

float floatVal = a_thread->Param(0).m_value.m_float;

a_thread->PushFloat(sqrtf(floatVal));

return GM_OK;

}

return GM_EXCEPTION;

}

static gmFunctionEntry s_mathLib[] = {

{"sqrt", gmfSqrt}, };

machine->RegisterLibrary(s_mathLib, sizeof(s_mathLib) / sizeof(s_mathLib[0]));




例子, 為String類型加上一個(gè)Compare操作的演示, 使得可以在GM中使用 "hihi".Compare("hihi") , 因?yàn)楸容^重要, 給出完整代碼.

#include "gmThread.h"

int GM_CDECL gmfStringCompare(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);

// Compare的參數(shù)必須是string, 因?yàn)檫@個(gè)函數(shù)預(yù)期將進(jìn)行字符串的比較

if (a_thread->ParamType(0) == GM_STRING)
{

// 獲取調(diào)用Compare的變量
const gmVariable* var = a_thread->GetThis();

// 這個(gè)變量一定也是一個(gè)string
GM_ASSERT(var->m_type == GM_STRING);

// gm str ----> c str
gmStringObject* obj = (gmStringObject* )GM_OBJECT(var->m_value.m_ref);
const char* thisStr = (const char* )*obj;
const char* otherStr = a_thread->ParamString(0);

// 具體的操作
a_thread->PushInt(strcmp(thisStr, otherStr) ? 0 : 1);
return GM_OK;
}
return GM_EXCEPTION;
}

static gmFunctionEntry s_stringlib[] = {
{"Compare", gmfStringCompare},
};

int main(int argc, char* argv[])
{

// 先創(chuàng)建虛擬機(jī)
gmMachine machine;

// 注冊(cè)到虛擬機(jī)
machine.RegisterTypeLibrary(GM_STRING, s_stringlib, 1);

// 好了可以用了:)
machine.ExecuteString("print("res = ", \"hihi\".Compare(\"hihi\"));");
getchar(); // Keypress before exit
return 0;
}
程序執(zhí)行結(jié)果是輸出 res = 1



從C中調(diào)用GM腳本
從C中調(diào)用GM腳本時(shí)使用gmCall輔助類會(huì)讓整個(gè)事情變得很簡(jiǎn)單, 下面就是一個(gè)例子:

#include “gmCall.h” // 要使用gmCall就必須包含這個(gè)頭文件

gmMachine machine; // 初始化一個(gè)GM虛擬機(jī)

// 我們要調(diào)用的函數(shù)是: global Add = function(a, b) { return a + b; };

gmCall call;

int resultInt = 0;

if (call.BeginGlobalFunction(&machine, “Add”)) {

call.AddParamInt(3);

call.AddParamInt(5);

call.End();

call.GetReturnedInt(resultInt); // 取結(jié)果

}
警告: 如果你從函數(shù)中返回一個(gè)string, 那么你就馬上使用它, 或者是把它c(diǎn)opy出來(lái), 不要長(zhǎng)期的持有這個(gè)指針. 因?yàn)檫@個(gè)字符串不會(huì)一直有效, 說(shuō)不定在下一輪的垃圾收集中就把它回收了, 這樣的話, 你再次使用它的指針時(shí)就很危險(xiǎn)了.



游戲?qū)ο髷U(kuò)展
我怎樣才能擴(kuò)展GM, 向它中添加一個(gè)我自己定義的類型, 就像table那樣子.

怎樣在GM中表達(dá)一個(gè)game obj?

下面的代碼就是完整的將GameObject類型導(dǎo)入到GM中, 包含創(chuàng)建, 訪問(wèn), 內(nèi)存管理的各個(gè)方面

struct GameObject {

gmTableObject* m_table; // 容納表功能

gmUserObject* m_userObject;

static gmType s_typeId; // 存儲(chǔ)自己定義的類型

};



gmType GameObject::s_typeId = GM_NULL;

#if GM_USE_INCGC

static bool GM_CDECL GCTrace(gmMachine* a_machine, gmUserObject* a_object, gmGarbagCollector* a_gc, const int a_workLeftToDo, int& a_workDone) {

GM_ASSERT(a_object->m_userType == GameObject::s_typeId);

GameObject* object = (GameObject* ) a_object->m_user;

if (object->m_table)

a_gc->GetNextObject(object->m_table);

a_workDone += 2;

return true;

}

static void GM_CDECL GCDestruct(gmMachine* a_machine, gmUserObject* a_object) {

GM_ASSERT(a_object->m_userType == GameObject::s_typeId);

GameObject* object = (GameObject* )a_object->m_user;

object->m_table = NULL;

}

#else

// 垃圾回收的標(biāo)記函數(shù)

void GM_CDECL GameObjectMark(gmMachine* a_machine, gmUserObject* a_object, gmuint32 a_mark) {

GM_ASSERT(a_object->m_userType == GameObject::s_typeId);

GameObject* obecjt = (GameObject* )a_object->m_user;

object->m_table->Mark(a_machine, a_mark);

}

// 垃圾回收的回收函數(shù)

void GM_CDECL GameObjectGC(gmMachine* a_machine, gmUserObject* a_object, gmuint32 a_mark) {

GM_ASSERT(a_object->m_userType == GameObject::s_typeId);

GameObject* object = (GameObject* )a_object->m_user;

object->m_table.Destruct(a_machine);

delete object;

}

#endif



// 設(shè)置一個(gè)用來(lái)描述類型的字符串以便在調(diào)用"AdString"得到它

static void GM_CDECL AsString(gmUserObject* a_object, char* a_buffer, int a_bufferLen) {

GM_ASSERT(a_ojbect->m_userType == GameObject::s_typeId);

GameObject* object = (GameObject* ) a_object->m_user;

char mixBuffer[128];

sprintf(mixBuffer, “GameObject Cptr = %x”, object);

int mixLength = strlen(mixBuffer);

int useLength = GM_MIN(mixLength, a_bufferLen - 1);

GM_ASSERT(useLenght > 0);

strncpy(a_buffer, mixBuffer, useLength);

a_buffer[useLengrh] = 0;

}

// get dot操作符用來(lái)訪問(wèn)table

void GM_CDECL GameObjectGetDot(gmThread* a_thread, gmVariable* a_operands) {

GM_ASSERT(a_operands[0].m_type == GameObject::s_typeId);

gmUserObject* user = (gmUserObject* )GM_OBJECT(a_operands[0].m_value.m_ref);

GameObject* object = (GmaeObject* )user->m_user;

a_operands[0] = object->m_table->Get(a_operands[1]);

}

// set dot操作符用來(lái)訪問(wèn)table

void GM_CDECL GameObjectSetDot(gmThread* a_thread, gmVariable* a_operands) {

GM_ASSERT(a_operands[0].m_type == GameObject::s_typeId);

gmUserObject* user = (gmUserObject* )GM_OBJECT(a_operands[0].m_value.m_ref);

GameObject* object = (GameObject* )user->m_user;

object->m_table->Set(a_thread->GetMachine(), a_operands[2], a_operands[1]);

}

// 從腳本中創(chuàng)建GameObject

// 注意: 游戲中像這樣直接創(chuàng)建對(duì)象實(shí)體并不常見(jiàn), 還有, 可能并不像保存對(duì)象的c指針, 取而代之的是保持一個(gè)32bit的UID來(lái)代表對(duì)象, 在使用的時(shí)候通過(guò)UID來(lái)查詢和驗(yàn)證對(duì)象

int GM_CDECL CreateGameObject(gmThread* a_thread) {

GameObject* object = new GameObject();

object->m_table = a_thread->GetMachine()->AllocTableObject();

object->m_userObject = a_thread->CreateUser(object, GameObject::s_typeId);

return GM_OK;

}

// 獲取一個(gè)object, 這種情況下的obj在c和腳本中是一對(duì)一的.

int GM_CDECL GetGameObject(gmThread* a_thread) {

GameObject* foundObj = NULL;

GM_CHECK_NUM_PARAMS(1);

if (a_thread->ParamType(0) == GM_STRING) {

// todo: foundObj = FindByName(a_thread->ParamString(0));

// 如果找到的話

a_thread->PushUser(foundObj->m_userObject);

a_thread->PushNull();

return GM_OK;

}

else if (a_thread->ParamType(0) == GM_INT) {

// todo: foundObj = FindById(a_thread->ParamInt(0));

// 如果找到的話

a_thread->PushUser(foundObj->m_userObject);

a_thread->PushNull();

return GM_OK;

}

return GM_EXCEPTION;

}

// 注冊(cè)函數(shù)

gmFunctionEntry regFuncList[] = {

{"GameObject", CreateGameObject},

}

// 向虛擬機(jī)注冊(cè)類型, 假定虛擬機(jī)已經(jīng)構(gòu)造好了

// 1. 注冊(cè)新類型

GameObject::s_typeId = machine.CreateUserType(“GameObject”);

// 2. 注冊(cè)垃圾回收要用到的

#if GM_USE_INCGC

a_machine->RegisterUserCallbacks(GameObject::s_typeId, GameObjectTrace, GameObjectDestruct, AsString);

#else

a_machine->RegisterUserCallbacks(GameObject::s_typeId, GameObjectMark, GameObjectGC, AsString);

#endif

// 為類型綁定get dot操作

machine.RegisterTypeOperator(GameObject::s_typeId, O_GETDOT, NULL, GameObjectGetDot);

// 為類型綁定set dot操作

machine.RegisterTypeOperator(GameObject::s_typeId, O_SETDOT, NULL, GameObjectSetDot);

// 注冊(cè)函數(shù)

machine.RegisterLibrary(regFuncList, sizeof(regFuncList) / sizeof(regFuncList[0]));


虛擬機(jī)的回調(diào)
如果一個(gè)應(yīng)用程序擁有它自己的gmObject, 它必須讓垃圾回收器知道這個(gè)對(duì)象正在被使用. 要做到這一點(diǎn), 你需要在虛擬機(jī)回調(diào)中捕獲MC_COLLECT_GARBAGE 消息, 這個(gè)回調(diào)發(fā)生在垃圾回收器開(kāi)始掃描根時(shí). 一個(gè)讓gc正確管理c++持有的gmObject的代換方案是使用gmMachine::AddCPPOwnedGMObject() 和 gmMachine::RemoveCPPOwnedGMObject(). 第三種方法是使用gmGCRoot<>指針來(lái)實(shí)現(xiàn).你可以通過(guò)查閱GM gc的文檔來(lái)獲取更多這方面的知識(shí).

應(yīng)用程序可能希望在thread創(chuàng)建和銷毀時(shí)執(zhí)行一些動(dòng)作, 比如管理這個(gè)thread專有的object等. 此外, 應(yīng)用程序可能希望將thread的異常信息導(dǎo)入到error stream中. 下面是一些列子.

// 假定你在別處已經(jīng)建立了虛擬機(jī), 現(xiàn)在你要注冊(cè)callback函數(shù)

gmMachine::s_machineCallback = ScriptCallback_Machine;

//

bool GM_CDECL ScriptCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void*a_context) {

switch (a_command) {

case MC_THREAD_EXCEPTION: {

// dump 異常信息導(dǎo)到標(biāo)準(zhǔn)輸出

bool first = true;

const char* entry;

while ( (entry = a_machine->GetLog().GetEntry(first)) ) {

fprintf(stderr, “%s“, entry);

}

a_machine->GetLog().Reset();

}

break;

case MC_COLLECT_GARBAGE": {

#if GM_USE_INCGC

gmGarbageCollector* gc = a_machine->GetGC();

// 對(duì)于所有的c 擁有的obj

// gc->GetNextObject(obj);

#else

gmuint32 mark = *(gmuint32 *)a_context;

// 對(duì)于所有的c 擁有的obj

// if (object->NeedsMark(mark)) {

// object->GetTableObject()->Mark(a_machine, mark);

// }

#endif

}

break;

case MC_THREAD_CREATE: {

// 線程創(chuàng)建時(shí)的回調(diào)

}

break;

case MC_THREAD_DESTROY: {

// 線程銷毀時(shí)的回調(diào)

}

break;

}

return false;

}

翻譯后記
前前后后翻譯了一周多, 終于算是告一段落了, 通過(guò)翻譯, 增加了對(duì)GameMonkey的一些理解.

因?yàn)樗菑膌ua發(fā)展起來(lái)的, 有很多概念有一些像, 但是經(jīng)過(guò)仔細(xì)的觀察和研究代碼, 發(fā)現(xiàn)GM除了借鑒了一些lua的概念, 從實(shí)現(xiàn)上和lua是完全不一樣的. 比如元方法的實(shí)現(xiàn)等等. GM使用起來(lái)將會(huì)感覺(jué)更加復(fù)雜, 有很多問(wèn)題都需要去coding解決, 而不像lua那樣美麗:) 但是從另外一方面來(lái)講, GM的確是給了程序足夠的控制力, 的確稱的上是一門面向程序員的語(yǔ)言.

翻譯的比較匆忙, 有什么錯(cuò)盡管指出:) 謝謝

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過(guò)難過(guò)
    • 5 囧
    • 3 圍觀圍觀
    • 2 無(wú)聊無(wú)聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(0)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)