Использование Lua скриптов в .NET с LuaInterface

Izaron 28 января 2014 в 20:34 21,2k
Привет, Хабрахабр!

Этот небольшой пост родился после того, как я решил узнать, как можно запускать скрипты Lua совместно с игрой на C# (либо на другом .NET-языке). с использованием библиотеки LuaInterface. Я был впечатлен легкостью этого интерфейса по сравнению с lua.h на C++

image

Что нужно знать


C# на приличном уровне, иметь понятие об основах программирования, а также о подключении ссылок в проекте на Visual Studio

Начало



Исходники (со всеми dll, конечно) выложены в конце поста

Первое, что нужно сделать — подключить к нашему проекту LuaInterface.dll. Просто добавляем ссылку на файл .dll. Если вы еще не в курсе, как это делается, то можете найти мануалы в интернете. Также для подключения требуется luanet.dll

Небольшой ликбез

LuaInterface — библиотека для удобной интеграции между Lua и CLR
Lua — очень легкий скриптовый язык программирования. Вот его разбор
Для чего нужны скрипты — выдержка из другого моего поста

Скрытый текст
Если вы разрабатывали большие проекты (к примеру, масштабные игры), замечали, что с каждой новой сотней строк кода компиляция идет медленней?
В игре создается больше оружия, больше диалогов, больше меню, больше etc.
Одна из самых главных проблем, возникающих в связи с нововведениями — поддерживать бессчетное множество оружия и бейджиков довольно сложное занятие.
В ситуации, когда просьба друга/босса/напарника изменить диалог или добавить новый вид оружия занимает слишком много времени, приходится прибегать к каким-то мерам — например, записи всей этой фигни в отдельные текстовые файлы.
Почти каждый геймдевелопер когда-нибудь делал карту уровней или диалоги в отдельном текстовом файле и потом их считывал. Взять хотя бы простейший вариант — олимпиадные задачи по информатике с файлом ввода

Но есть способ, на голову выше — использование скриптов.

Решение проблемы

«Окей, для таких дел хватает обычного файла с описанием характеристиков игрока. Но что делать, если в бурно развивающемся проекте почти каждый день приходится немножко изменять логику главного игрока, и, следовательно, много раз компилировать проект?»
Хороший вопрос. В этом случае нам на помощь приходят скрипты, держащие именно логику игрока со всеми характеристиками либо какой-либо другой части игры.
Естественно, удобнее всего держать, логику игрока в виде кода какого-нибудь языка программирования.
Первая мысль — написать свой интерпретатор своего скриптового языка, выкидывается из мозга через несколько секунд. Логика игрока определенно не стоит таких жутких затрат.
К счастью, есть специальные библиотеки скриптовых языков для С++, которые принимают на вход текстовый файл и выполняют его.

Об одном таком скриптовом языке Lua пойдет речь.


Теперь, когда у нас есть проект с подключенным LuaInterface, переходим к коду!

LuaInterface — основы


В основном .cs файле пишем
using LuaInterface;


Основной класс этой библиотеки — Lua

Lua lua = new Lua();


Объявление констант


Очень просто можно объявить константы. Делается это так
lua[ключ] = значение;

lua["version"] = 0.1;
lua["name"] = "YourName";
lua["test"] = 200;
lua["color"] = new Color();
lua["my"] = this;

В качестве значения может выступать что угодно — число, строка, даже классы и структуры (о том, как с ними работать, будет дальше)

Регистрация функций


В Lua можно зарегистрировать функцию из C#
lua.RegisterFunction(название функции в Lua, this, функция);

lua.RegisterFunction("puts", this, typeof(Program).GetMethod("Test"));


Регистрация классов и структур


Одна из самых приятных сторон LuaInterface, которая может удивить тех, кто использует Lua совместно с C++, это то, что можно регистрировать объект класса и после этого вызывать в скрипте разные функции «напрямую»
То есть можно сделать так:

C#
    class LuaDebug
    {
        // Запись любого текста с указанным цветом
        private void Print(string message, ConsoleColor color)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(message);
        }

        public void Log(string message)
        {
            Print("Log: " + message, ConsoleColor.White);
        }

        public void Warning(string message)
        {
            Print("Warning: " + message, ConsoleColor.Yellow);
        }

        public void Error(string message)
        {
            Print("Error: " + message, ConsoleColor.Red);
        }

        public string ConsoleRead()
        {
            return Console.ReadLine();
        }
    }

// ...

lua["Debug"] = new LuaDebug();


И после этого сделать Lua скрипт с таким содержанием:
str = Debug:ConsoleRead() -- считывание строки
Debug:Log("Приложение запущено")
Debug:Warning("Введенная строка: " .. str)
Debug:Log("Удачного дня!")
Debug:ConsoleRead() -- пауза


Выполнение Lua кода


Выполнить Lua код (со всеми зарегистрированными функциями и константами) можно двумя способами
Первый — прямиком из C#
lua.DoString(код)

lua.DoString("Debug:Log('Hello, Habr!')" + "\n" +
      "Debug:ConsoleRead()");

Второй — из файла
lua.DoFile(file)

lua.DoFile("script.lua")

(Оба метода возвращают значение object[] — это то, что возвращает Lua скрипт после выполнения)

Обработка исключений


Для обработки исключений — ошибок, которые могут выскочить во время выполнения скрипта, следует использовать LuaException err

            try
            {
              // Lua, lua, lua
            }
            catch (LuaException err)
            {
               // Обработка ошибки
            }


Вызов методов из Lua


Для вызова метода из Lua надо выполнить скрипт, выудить метод, и выполнять его когда потребуется.
Пример
lua.DoFile("file.lua")
LuaFunction func = lua["func"] as LuaFunction; // function func() {...} end
func.Call();

Также в Call(params object[] args) можно передавать входные параметры для функции
Тот же финт срабатывает и со значениями, только вместо LuaFunction используем string, int, double и так далее

Дополнительные материалы


  • Для таблиц в LuaInterface предусмотрен класс LuaTable, регистрируется в объекте класса Lua он как обычная переменная, а запись переменных в саму таблицу мало чем отличается от записи переменных в самом Lua объекте
  • Также есть класс LuaDLL, используемый для «низкоуровневой» работы с Lua (из lua.h). Толку от него немного, и вряд ли кто-то использует его по-серьезному
    Пример
                LuaDLL.lua_open();
                LuaDLL.lua_createtable(luaState, 1, 1);
    


image
Символика Lua

image
Love2D — один из самых популярных движков на Lua

image
Мод для Minecraft на Lua

image
https://bitbucket.org/Izaron/luaforhabr/src
Исходный код
Проголосовать:
+16
Сохранить: