
Chinese Lunar Calendar日歷程序提供西方占星術(shù)V1.0綠色免費(fèi)版
- 類型:定時(shí)工具大。846KB語(yǔ)言:多國(guó)語(yǔ)言[中文] 評(píng)分:6.6
- 標(biāo)簽:
使用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ò)盡管指出:) 謝謝