日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測下載網站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)其它知識 → GameMonkey參考手冊官方資料翻譯

      GameMonkey參考手冊官方資料翻譯

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

      作者:佚名點擊:122次評論:0次標簽: GameMonkey luna rotc

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

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

      /*

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

      */

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


      變量和常量
      GameMonkey不像c, Pascal那樣的強類型語言, 它更像是Basic語言.

      GM中的變量是以下類型中的一個:

      null -- 沒有值, 這有類型

      int -- 32bit的有符號整數

      float -- 32bit的浮點數

      string -- null結尾的ansi字符串

      table -- 數組/hash容器

      function -- 函數

      user -- 用戶自定義類型

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

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

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


      enum {null, man, woman}; // 兩個類型加一個null類型

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

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

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

      在我的腳本中:

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

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

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

      v.type = man; // 賦類型

      v.pData = &m; // 賦值

      }

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

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

      v.type = woman; // 賦類型

      v.pData = &m; // 賦值

      }

      pp = null;

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




      Value pp;

      pp = Man(5, 3);

      pp = Woman(“X姐”);

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

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


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

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

      例子:

      a = null; // a 沒有值

      b = 4; // b 是int類型

      c = 4.4; // c 是float類型

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

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

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

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

      b = .23; // b 是一個float

      c = 2.4f; // c 是一個float

      d = ‘c:\windows\sys’; // d是一個string


      語言和它的標準函數總是試圖保留值, 然而不是保留類型. 具體規(guī)則是當不同類型的變量在一起運算時, 高級別的類型將被保留. 類型從低級到高級的順序是: 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; // 十進制

      a = 0xB3; // 十六進制

      a = 0b0011001 // 二進制

      a = ‘BLCK’; // 字符轉成4個byte分別賦予int的四個byte中


      float類型賦值例子:

      b = 45.34; // float十進制

      b = .345; // float

      b = 289.0; // float

      b = 12.34f; // c風格float

      b = 2.3E-3; // 科學計數法


      字符串賦值例子:

      c = “c:\\path\\file.ext”; // 標準雙引, 用\做轉義字符

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

      c = “Mary says \”hello\””; // 相當于'Mary says "hello"'

      c = 'Chris' 's bike'; // 相當于'Chris's bike', 也就是說在單引內部表示單引的方法是連續(xù)兩個單引

      c = “My ” “house”; // 相當于"My house"


      基礎類型可以使用標準內建庫進行顯示的轉換, Int(), Float(), String()

      例子:

      a = 10;

      b = a.String(); // 這樣是最好的, 顯示的調用類型轉化函數, 返回轉化后的值

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

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

      b = 10.String(); // 錯誤的, 編譯不過, 因為編譯器不認同這種語法


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

      例子:

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

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

      b[1] = "banana"; // 設置b[1]

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

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


      當一個變量被賦新值時, 該變量原來持有的值就有可能丟失掉了.

      例子:

      Oper = function(a, b){

      return a + b

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

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


      函數
      語法: function(<params>) { <statements> };

      一個函數體是一個值, 而函數是一個類型 {type = GM_FUNCTION, value=function...}

      注意: 記住在將函數賦值給變量后面那個分號, 這是語法必須的

      例子

      // 將一個創(chuàng)建一個rect table的函數賦值給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)建一個用于描述rect的table

      area = myRect.Area();

      // 可以用:代替.來隱式的傳遞一個this指針

      Size = function(){

      return .width * .height;

      };

      s = myRect:Size(); // 調用時, myRect會當做this指針傳入Size中


      作用域
      和作用域有關的一些關鍵字, 語法:

      global <variable>

      Local <variable>

      member <variable>

      this

      this.<variable>

      .<variable>

      函數中的變量.



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

      例子:

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

      apple = 3; // apple 是函數的一個local變量

      global apple; // 把apple聲明成全局作用域

      local apple; // 把apple聲明成局部作用域

      member apple; // 把apple聲明成 this的member變量

      this.apple; // 明確的訪問this.apple

      .apple // 隱式的訪問this.apple

      };
      例子:

      a = 13; // a 是一個local作用域變量

      print(b); // b是null

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

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

      d = 3; // d是函數局部作用域變量

      { if (c == 2)

      { local e = 3; } // e 從這一刻開始成為函數局部作用域變量, 注意沒有塊作用域變量

      }

      print(e); // e = 3

      }
      在查找一個變量時, 按照local 和 parameters, 然后global的順序查找.



      成員變量有微妙的不同:

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

      global a = 3; // a 是一個全局變量

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

      d = 3; // d是函數局部作用域

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

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

      print(b); // b 是 null, 因為這里并沒有l(wèi)ocal的b

      print(n); // 就像print(this.n)一樣, 因為上面顯示聲明過了

      };
      全局作用域中的語句.

      x = 7; // local

      global x = 8; // global

      a = function(y) {

      local x = 5; // function local

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

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

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

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

      obj:MakeFruit(apple, orange) // 調用MakeFruit, 并把obj當做this傳給它



      語法和操作符


      ! Not 邏輯取反

      ~ 每一個bit位取反

      ^ bit位使用與或 XOR

      | bit位使用或 OR

      & bit位使用與 AND

      >> bit位右移

      << bit位左移

      ~= bit位取反賦值

      ^= bit位 XOR 賦值

      |= bit位 OR 賦值

      &= bit位 AND 賦值

      >>= bit位 右移 賦值

      <<= bit位 左移 賦值

      = 賦值

      ' 單引 其中的字符會當做int值

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

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

      [] 方闊 用index取talbe元素

      . 取table元素

      : 給函數傳遞this

      + 數學+

      - 數學-

      * 數學*

      / 數學/

      % 模取余

      +=, –=, *=, /=, %= 數學運算并賦值

      {} 界定語句塊

      ; 語句結束標志

      <, <=, >, >=, == 數學比較

      &&或and 邏輯AND

      || 或or 邏輯OR



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

      table(<value>, …);

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

      {<value>, …, };

      table可以被同時認為是array 和 map. 因為table中可以容納data和function, 所以table也可以被認為是class, table中也可以容納別的table, 這時它也已被認為是Tree.

      初始化table的例子:

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

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

      這時, fruit的樣子就是:

      fruit[0] = “apple”;

      fruit[1] = “banana”;

      fruit[2] = “cherry”;

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

      從表中取得元素的例子.

      a = thing.other; // other 是table thing中的一個成員

      b = thing[“other”]; // 相當于b = thing.other

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

      index = 3;

      d = thing[index]; // 用int做下標, 就可以把table當數組訪問

      accoc = “fav”;

      e = thing[accoc]; // 用string做下標, 就可以把table當map訪問
      注意, thing["Fav"]和thing["fav"]是兩個不同的東西, 因為GM是大小寫敏感的. 這樣做設計上的考慮是:

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

      2) 要做到大小寫無關, 底層需要一些額外的工作量, 這會產生一定量的效率問題.

      設置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"


      關鍵字if和else
      語法: 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 會計算條件表達式的值, 并根據其結果的true/false來選擇執(zhí)行那一段語句.

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

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

      if (3 > 0 || 0 > 1) 3 > 0恒真, 那么永遠不會去對0 > 1求值

      對c程序員的提示: 你不能把condition和一個單語句無語句塊標示的statements寫在同一行, 這是語法不允許的

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



      關鍵字for
      語法: for (<statement1>; <condition>; <statement2>) { <statements> }

      例子:



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


      print(“Index = ”, index);

      }
      輸出是:

      Index = 0

      Index = 2

      Index = 4



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

      1. 執(zhí)行 statement1

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

      3. 執(zhí)行statements

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



      關鍵字foreach
      語法: 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的時候, 它并沒有按料想的順序來輸出. 事實上, 這種順序會在table中的元素填入和刪除時發(fā)生變化.

      在foreach的每次迭代過程中, key和value將會作為循環(huán)體的local作用域變量. 在迭代過程中, 對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] 請大家告訴我好的理解, 我好改正



      關鍵字 while
      語法: while( <condition> ) { <statements> }

      例子:

      index = 0;

      while ( index < 3 ) {

      print("index = ", index);

      index = index + 1;

      }
      輸出:

      index = 0

      index = 1

      index = 2

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



      關鍵字 dowhile
      語法: dowhile (<condition>) { <statements> }

      例子:

      index = 0;

      dowhile (index > 0) {

      print("index = ", index);

      }
      輸出:

      index = 0

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



      關鍵字 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用來退出或者是忽略 for, while, dowhile, foreach 循環(huán). break會使執(zhí)行流跳出循環(huán)語句塊, continue導致終止本輪迭代, 并立即進行下一輪迭代, return不光是能跳出循環(huán), 它是直接跳出當前函數.



      關鍵字true, false, null
      在GM中, true和false分別表示0和1. 除此之外沒有別的意義. 注意, 和其他語言不太一樣的地方

      例子:

      a = 3;

      if (a == true) {

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

      }else{

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

      }
      輸出:

      3 != 1

      null是一種類型, 而不是一個值, 它通常用來表示錯誤. 當它和其他類型混用時, 它的類型會被提升, 值為0. 當一個變量被聲明但沒有賦值時, 這個變量就是一個null. 對于table中的元素, 如果被賦值為null, 就表示這個元素被從table中刪掉了.

      例子:

      local var;

      if (var) { // var聲明了但沒賦值, 所以是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



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

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



      Thread
      rotc: 這里有幾點要說的

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

      2. GameMonkey開發(fā)的初衷有彌補當時的LuaV4沒有協(xié)程的遺憾, 現(xiàn)在luaV5已經有了.

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



      1. int thread(function a_function, …)

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

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

      ... 是傳給a_function的參數

      該函數返回一個thread id, 控制和查詢線程都必須通過這個id來進行.



      2. void yield()

      調用該函數導致當前執(zhí)行體讓出對GM虛擬機的控制權.



      3. void exit()

      調用本函數導致當前執(zhí)行體立即退出.



      4. void threadKill(int a_threadId)

      kill掉指定的線程, 被kill掉的線程不能再次運行.

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

      如果調用threadKill()將導致當前線程被kill掉.



      5. void threadKillAll(bool a_killCurrentThread = false)

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



      6. void sleep(float a_time)

      停止當前執(zhí)行體指定的秒數.



      7. int threadTime()

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



      8. int threadId()

      返回當前線程的id



      9. table threadAllIds()

      用一個table返回所有的線程id.



      10. void signal(var a_event)

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



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

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



      States
      在游戲編程中, 常使用狀態(tài)的概念來描述一個游戲實體的行為, 就是常常說到的有限狀態(tài)機. 在GM中, states允許一個線程結束后馬上丟棄這個線程的棧并跳到另一個執(zhí)行點開始執(zhí)行.



      1. void stateSet(function a_function, …)

      設置當前執(zhí)行線程的新的狀態(tài)函數.

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

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



      2. function stateGet()

      獲取當前執(zhí)行的狀態(tài)函數. 如果之前沒有調用過stateSet, 那么將返回null.



      3. function stateGetLast()

      獲取當前狀態(tài)的前一個狀態(tài), 可以用來得知變遷信息.



      4. void stateSetExitFunction(function a_function)

      設置一個函數, 該函數將在狀態(tài)變遷時調用. 可以用來在進入下一個狀態(tài)前本次狀態(tài)函數本身的一些清理工作, 如果沒有下一個狀態(tài), 那么這個函數不會被執(zhí)行.



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



      System
      1. void debug()

      使調試器在這里產生一個斷點.



      2. void assert(int a_condition)

      檢查a_condition, 它必須是非0值, 否者產生一個異常然后退出線程.



      3. int sysTime()

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



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

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

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

      實質的步驟是:

      1. 把 a_script 編譯成一個函數func

      2. 調用 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)

      給指定的類型注冊一個操作

      a_typeid 目標類型

      a_opName 操作名

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

      返回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)

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

      a_typeid 目標類型

      a_varName 要加的變量名

      a_var 要加的變量

      返回1成功, 0失敗.



      9. int sysCollectGarbage(int a_forceFullCollect = false)

      如果當前內存使用量超過了指定的內存使用量, 那么執(zhí)行垃圾回收.

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

      返回1表示垃圾回收執(zhí)行了, 其他情況返回0.



      10. int sysGetMemoryUsage()

      返回當前的內存使用量, 單位byte.



      11. void sysSetDesiredMemoryUsageHard(int a_desired)

      設置內存使用量硬限制. 在垃圾回收時會根據這個值來決定要不要執(zhí)行一次完整的回收.

      a_desired 內存使用硬限制, 單位是byte.



      12. void sysSetDesiredMemoryUsageSoft(int a_desired)

      設置內存使用量軟限制. 在垃圾回收時會根據這個值來決定是否開始增量回收. soft值必須小于上面的hard值, 謝謝

      a_desired 內存使用軟限制, 單位是byte.



      13. void sysSetDesiredMemoryUsageAuto(int a_enable)

      開啟或者關閉在接下來的垃圾收集中是否能自動調整內存限制.

      a_enable 1 開啟 0 關閉



      14. int sysGetDesiredMemoryUsageHard()

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



      15. int sysGetDesiredMemoryUsageSoft()

      獲取內存使用量軟限制, 單位是byte. 注意, 這個值是用在開始增量垃圾回收前的檢測.



      16. int sysGetStatsGCNumFullCollects()

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



      17. int sysGetStatsGCNumIncCollects()

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



      18. int sysGetStatsGCNumIncWarnings()

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



      表操作
      1. int tableCount(table a_table)

      計算table中元素的個數.



      2. table tableDuplicate(table a_table)

      返回傳入table的一個副本.

      我測過了, 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
      啥內涵大家一看就明白了





      ------------------------------------------華麗的風格線-------------------------------------------



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

      一個可以綁定到GM中的C函數的原型是:

      int GM_CDECL gmVersion(gmThread* a_thread)

      a_thread->GetNumParams() 可以獲得參數的個數

      a_thread->Param*() 獲取各個參數

      a_thread->GetThis() 訪問this

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

      還有一些有用的宏和簡寫的功能函數.



      C函數的返回值描述如下:

      GM_OK 函數執(zhí)行成功

      GM_EXCEPTION 函數執(zhí)行失敗, 在函數運行的thread中產生一個運行時異常

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



      一個GM操作符綁定函數的原型是:

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

      a_operands[0] 是左參數

      a_operands[1] 是右參數

      a_operands[0] 同時也是返回值

      如果操作符函數不能執(zhí)行該操作(比如錯誤的參數類型等), 就把a_operands[0]置為null

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

      可能發(fā)生沖突的地方就是當用戶自定義類型之間發(fā)生運算時, 如果用戶知道注冊的順序的話, 他們可以依據這個來編碼, 否者可能要實現(xiàn)同樣的操作符函數來保證不會發(fā)生因為注冊順序而導致的問題. 兩個用戶類型可以給一個操作符綁定同樣的操作符函數, 這樣可以避免不必要的重復.

      rotc: 上面這兩段話我翻譯的不好, 先放著, 等對這部分知識有了更深的理解再來修改





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

      // 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中導入一個函數, 使得在GM中可以使用sqrt(56)或者sqrt(67.8), 過程比較簡單就不寫注釋了

      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類型加上一個Compare操作的演示, 使得可以在GM中使用 "hihi".Compare("hihi") , 因為比較重要, 給出完整代碼.

      #include "gmThread.h"

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

      // Compare的參數必須是string, 因為這個函數預期將進行字符串的比較

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

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

      // 這個變量一定也是一個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)建虛擬機
      gmMachine machine;

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

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



      從C中調用GM腳本
      從C中調用GM腳本時使用gmCall輔助類會讓整個事情變得很簡單, 下面就是一個例子:

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

      gmMachine machine; // 初始化一個GM虛擬機

      // 我們要調用的函數是: 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); // 取結果

      }
      警告: 如果你從函數中返回一個string, 那么你就馬上使用它, 或者是把它copy出來, 不要長期的持有這個指針. 因為這個字符串不會一直有效, 說不定在下一輪的垃圾收集中就把它回收了, 這樣的話, 你再次使用它的指針時就很危險了.



      游戲對象擴展
      我怎樣才能擴展GM, 向它中添加一個我自己定義的類型, 就像table那樣子.

      怎樣在GM中表達一個game obj?

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

      struct GameObject {

      gmTableObject* m_table; // 容納表功能

      gmUserObject* m_userObject;

      static gmType s_typeId; // 存儲自己定義的類型

      };



      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

      // 垃圾回收的標記函數

      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);

      }

      // 垃圾回收的回收函數

      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



      // 設置一個用來描述類型的字符串以便在調用"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操作符用來訪問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操作符用來訪問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)建對象實體并不常見, 還有, 可能并不像保存對象的c指針, 取而代之的是保持一個32bit的UID來代表對象, 在使用的時候通過UID來查詢和驗證對象

      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;

      }

      // 獲取一個object, 這種情況下的obj在c和腳本中是一對一的.

      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;

      }

      // 注冊函數

      gmFunctionEntry regFuncList[] = {

      {"GameObject", CreateGameObject},

      }

      // 向虛擬機注冊類型, 假定虛擬機已經構造好了

      // 1. 注冊新類型

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

      // 2. 注冊垃圾回收要用到的

      #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);

      // 注冊函數

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


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

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

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

      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 異常信息導到標準輸出

      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();

      // 對于所有的c 擁有的obj

      // gc->GetNextObject(obj);

      #else

      gmuint32 mark = *(gmuint32 *)a_context;

      // 對于所有的c 擁有的obj

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

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

      // }

      #endif

      }

      break;

      case MC_THREAD_CREATE: {

      // 線程創(chuàng)建時的回調

      }

      break;

      case MC_THREAD_DESTROY: {

      // 線程銷毀時的回調

      }

      break;

      }

      return false;

      }

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

      因為它是從lua發(fā)展起來的, 有很多概念有一些像, 但是經過仔細的觀察和研究代碼, 發(fā)現(xiàn)GM除了借鑒了一些lua的概念, 從實現(xiàn)上和lua是完全不一樣的. 比如元方法的實現(xiàn)等等. GM使用起來將會感覺更加復雜, 有很多問題都需要去coding解決, 而不像lua那樣美麗:) 但是從另外一方面來講, GM的確是給了程序足夠的控制力, 的確稱的上是一門面向程序員的語言.

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

        相關評論

        閱讀本文后您有什么感想? 已有人給出評價!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評論

        最新評論

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

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字數: 0/500 (您的評論需要經過審核才能顯示)