2014/05/19

C#とLuaの連携

NLuaというスクリプト言語LuaをC#から使えるようにするプロジェクトがあります。(https://github.com/NLua/NLua)

1.目的
スクリプト言語からC#で実装したモーター制御、通信、描画、入出力処理を制御し、 複数の期待に柔軟に対応できるアプリケーションづくりを支援する。

想定する使い方はたとえば次の通り。

-- 画面に文字を描画
screen = Screen.new();
screen.textOut("sample");

-- 文字の大きさを変更
screen.setFontSize(20);

-- 描画する位置を指定
screen.setPosition(100, 100);

-- 枠を描画
screen.drawRectangle(10, 10, 100, 100);

-- ボードの生成
board = {};
board[1] = PWMBoard.new();
board[2] = RotaryEncoderBoard.new();

-- ボードへ出力
t = board[1];
t:setPosition(2, 500); -- 出力2を50%にする
t:setPosition(3, 100); -- 出力3を10%にする
t:send(); -- データを送信する

-- ボードから入力
t = board[2];
t:recieve();
screen.textOut(t:getPosition());

-- コントローラからの入力受付
function onControllerInput(status) {
if status.maru then
MessageBox.show("○が押された");
end
}

-- 1フレームごと呼び出される関数
function onFrame() {
-- 描画処理など
}



2. 使い方
先ほどのページのREADME.mdの中のWin32: Downloadよりダウンロードし、展開する。中にある3つのDLLのうちNLua.dllを参照に追加し、残り2つは実行ファイルと同じディレクトリに入れる。

3. 使用例
 たとえばコンソール画面を次のように操作できるプログラムをC#で作る。
・8x8の画面上に適当なマークが設置できるようになっている。
・現在の位置はハイライトされる。
・メニューと称して、適当な数のアイテムから1つ選ぶことができる。
・ステータスと称して、画面の下にメッセージを表示できる。

using System;
using NLua;
using System.Collections.Generic;

namespace cstest {
 public class Board {
  const int max_size = 8;
  string[] put_chars;

  public Board(NLua.Lua lua) {
   x = 0;
   y = 0;
   this.lua = lua;
   menu_mode = false;
   lua["MenuMode"] = false;
   menu_position = 0;
   menu_list = new List<Tuple<string, LuaFunction>>();
   menu_size = 0;

   data = new int[max_size, max_size];
   put_chars = new string[] { " ", "○", "×", "☆" };

   for (int iy = 0; iy < max_size; ++iy) {
    for (int ix = 0; ix < max_size; ++ix) {
     data[ix, iy] = 0;
    }
   }
  }

  public void Show() {
   Console.Clear();

   for (int iy = 0; iy < max_size; ++iy) {
    for (int ix = 0; ix < max_size; ++ix) {
     var t = data[ix, iy];

     if (t < 0 || t >= 4) {
      throw new Exception();
     }

     if(ix == x && iy == y) {
      Console.BackgroundColor = ConsoleColor.Gray;
      Console.ForegroundColor = ConsoleColor.White;
     }
     else {
      Console.BackgroundColor = ConsoleColor.Black;
      Console.ForegroundColor = ConsoleColor.White;
     }
     
     Console.Write(put_chars[t]);

     Console.BackgroundColor = ConsoleColor.Black;
     Console.ForegroundColor = ConsoleColor.White;
    }
    Console.WriteLine();
   }

   if (menu_mode) {
    DrawMenu();
   }

   Console.SetCursorPosition(0, max_size);
   Console.WriteLine(status_message);
  }

  public void Move(int x, int y) {
   this.x = x;
   this.y = y;
  }

  public void Put(int type) {
   data[x, y] = type;
  }

  public string Read() {
   return Console.ReadKey(true).Key.ToString();
  }

  public void SetMenuList(NLua.LuaTable name_list, NLua.LuaTable event_list) {
   string name;
   LuaFunction func;
   int i = 1;

   menu_list.Clear();

   while ((name = (string)name_list[i]) != null && (func = (LuaFunction)event_list[i]) != null) {
    menu_list.Add(new Tuple<string, LuaFunction>(name, func));
    ++i;
   }

   menu_size = i - 1;
  }

  public void ShowMenu() {
   menu_mode = true;
   lua["MenuMode"] = true;
   DrawMenu();
  }

  public void MenuPositionUp() {
   if (menu_position > 0) {
    --menu_position;
   }
  }

  public void MenuPositionDown() {
   ++menu_position; 
   
   if (menu_position >= menu_size) {
    menu_position = menu_size - 1;
   }
  }

  public void CloseMenu() {
   menu_mode = false;
   lua["MenuMode"] = false;
   Show();
  }

  public void MenuExecute() {
   LuaFunction func = menu_list[menu_position].Item2;
   func.Call(menu_position + 1);
  }

  public void ShowMessage(string str) {
   status_message = str;
   Console.SetCursorPosition(0, max_size);
   Console.WriteLine(str);
  }

  private void DrawMenu() {
   int i = 0;
   foreach (var item in menu_list) {
    Console.SetCursorPosition(max_size * 2, 2 + i);
    Console.Write("・");
    
    if (i == menu_position) {
     Console.BackgroundColor = ConsoleColor.Blue;
     Console.ForegroundColor = ConsoleColor.White;
    }
    else {
     Console.BackgroundColor = ConsoleColor.Black;
     Console.ForegroundColor = ConsoleColor.White;
    }

    Console.Write(item.Item1);

    Console.BackgroundColor = ConsoleColor.Black;
    Console.ForegroundColor = ConsoleColor.White;
    ++i;
   }
  }

  private bool menu_mode;
  private int menu_position;
  private List<Tuple<string, LuaFunction>> menu_list;
  private int menu_size;
  
  private string status_message;

  private int x;
  private int y;
  
  private int[,] data;
  private NLua.Lua lua;
 }

 class Program {
  static void Main(string[] args) {
   NLua.Lua lua = new NLua.Lua();
   Board board = new Board(lua);

   lua.RegisterFunction("Read", board, typeof(Board).GetMethod("Read"));
   lua.RegisterFunction("Put", board, typeof(Board).GetMethod("Put"));
   lua.RegisterFunction("Move", board, typeof(Board).GetMethod("Move"));
   lua.RegisterFunction("SetMenuList", board, typeof(Board).GetMethod("SetMenuList"));
   lua.RegisterFunction("ShowMenu", board, typeof(Board).GetMethod("ShowMenu"));
   lua.RegisterFunction("CloseMenu", board, typeof(Board).GetMethod("CloseMenu"));
   lua.RegisterFunction("MenuPositionUp", board, typeof(Board).GetMethod("MenuPositionUp"));
   lua.RegisterFunction("MenuPositionDown", board, typeof(Board).GetMethod("MenuPositionDown"));
   lua.RegisterFunction("MenuExecute", board, typeof(Board).GetMethod("MenuExecute"));
   lua.RegisterFunction("ShowMessage", board, typeof(Board).GetMethod("ShowMessage"));
   lua.RegisterFunction("Show", board, typeof(Board).GetMethod("Show"));

   lua.DoFile("./test.lua");
   LuaFunction func = lua.GetFunction("onKey");
   
   while(true) {
    string key = Console.ReadKey(true).Key.ToString();
    func.Call(key);
   }

   lua.Close();
  }
 }
}
そしてLua側から次のように制御する。
function F1(no)
 Mode = 0;
 ShowMessage("移動");
end

function F2(no)
 Mode = 1;
 PutType = 0;
 ShowMessage("設置");
end

function F3(no)
 ShowMessage("");
end

function onKey(key)
 if MenuMode then
  if key == "DownArrow" then
   MenuPositionDown();
   Show();
  elseif key == "UpArrow" then
   MenuPositionUp();
   Show();
  elseif key == "Enter" then
   MenuExecute();
   CloseMenu();
  end
 elseif Mode == 0 then
  if key == "DownArrow" then
   pos_y = pos_y + 1;
  elseif key == "UpArrow" then
   pos_y = pos_y - 1;
  elseif key == "LeftArrow" then
   pos_x = pos_x - 1;
  elseif key == "RightArrow" then
   pos_x = pos_x + 1;
  elseif key == "Enter" then
   ShowMessage("移動終了");
   Mode = -1;
  end
  
  Move(pos_x, pos_y);
  Show();
 elseif Mode == 1 then
  if key == "DownArrow" then
   PutType = PutType + 1;
  elseif key == "UpArrow" then
   PutType = PutType - 1;
  elseif key == "Enter" then
   ShowMessage("設置終了");
   Put(PutType);
   Mode = -1;
  end
  
  if PutType < 0 then
   PutType = 0;
  elseif PutType > 3 then
   PutType = 3;
  end
  
  Put(PutType);
  Show();
 else
  if key == "M" then
   ShowMenu();
  else
   print("KEY:" .. key);
  end
 end
end

Mode = -1;
pos_x = 0;
pos_y = 0;
name_list = { "移動", "設置", "閉じる" };
event_list = { F1, F2, F3 };
SetMenuList(name_list, event_list);

0 件のコメント:

コメントを投稿