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

    Привет, Хабрахабр!

    Этот небольшой пост родился после того, как я решил узнать, как можно запускать скрипты 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
    Исходный код
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 15
    • 0
      При многократном исполнении lua-скриптов идет JIT-компиляция, или они интерпретируются каждый раз?
      • 0
        В lua нет JIT, вообще. Для JIT вам нужно ответвление под названием LuaJIT, которое, кстати, чаще всего не требует даже перекомпиляции программы для перехода на него. Другое дело что в LuaJIT скрипт при повторном запуске все равно перекомпилируется.
        • 0
          Спасибо. Буду рыть.
        • 0
          Судя по тому, что допущенная ошибка в скрипте бросает исключение только в тот момент, когда действие скрипта дойдет собственно до места ошибки, и сравнивая это дело с определением Just-in-time, то да, JIT есть :)
          • +1
            Вы неправильно понимаете JIT-компиляцию. А перехват исключения в месте ошибки исполнения в скрипте никак не связан с тем интерпретируется скрипт или работает уже по скомпилированному коду.
        • 0
          bitbucket.org/Izaron/luaforhabr/src
          Access denied
          Use the links at the top to get back.
          • 0
            Извините, исправил :)
            • 0
              Коммит сурцов в зипе в репозиторий — это слишком сурово. Для таких целей и гуглодрайв бы подошёл.
          • +1
            А почему именно luainterface? Эта либа отсюда (https://code.google.com/p/luainterface/)?
            Там написано же:
            Project Update: 30th April 2013
            Over the last few years I've found very little time to work on LuaInterface. Certain platforms such as Mono/Xamarin and WinRT aren't supported well (if at all) and there are still quite a few bugs open.

            I would like to suggest that the community look towards Vinicius Jarina's project on Github called NLua moving forward.

            NLua is a fork of this project and I expect it will see more frequent updates over there than I am able to achieve here on Google Code.

            — Craig Presti

            Почему не использовать более свежий форк (https://github.com/NLua/NLua), когда сам автор исходной либы рекомендует его.
            И странный способ распроcтранения либы, зип в репозитории. Можно же более культурно через NuGet www.nuget.org/packages/NLua/
            • +3
              А почему не делать скриптинг в приложении прямо на самом C#? Мы как-то так делали.
              • 0
                Наш архитектор так и говорил — зачем нам скрипты мы и так пишем на скриптах. C# можно компилировать на лету как текст так и байткод, полная интроинспекция (рефлексия), динамическая загрузка кода и все это без потери безопасности.
                • 0
                  Там есть проблемы с выгрукой кода. Решали вопрос организацие отдельного AppDomain и его разрушением при перезагрузке скрипта. Для работы в разных доменах пришлось сделать маршалинг во всех необходимых классах. Заодно это еще сильнее добавило безопастности
                • 0
                  Ну или на F# в котором есть хорошая out-of-the-box поддержка скриптинга?
              • 0
                народ!, кто сталкивался, подскажите пожалуйста: как получить данные из функции скрипта LUA в C#?

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.