Привет!
Этот небольшой пост родился после того, как я решил узнать, как можно запускать скрипты Lua совместно с игрой на C# (либо на другом .NET-языке). с использованием библиотеки LuaInterface. Я был впечатлен легкостью этого интерфейса по сравнению с lua.h на C++
Что нужно знать
C# на приличном уровне, иметь понятие об основах программирования, а также о подключении ссылок в проекте на Visual Studio
Начало
Исходники (со всеми dll, конечно) выложены в конце поста
Первое, что нужно сделать — подключить к нашему проекту LuaInterface.dll. Просто добавляем ссылку на файл .dll. Если вы еще не в курсе, как это делается, то можете найти мануалы в интернете. Также для подключения требуется luanet.dll
Небольшой ликбез
LuaInterface — библиотека для удобной интеграции между Lua и CLR
Lua — очень легкий скриптовый язык программирования. Вот его разбор
Для чего нужны скрипты — выдержка из другого моего поста
В игре создается больше оружия, больше диалогов, больше меню, больше etc.
Одна из самых главных проблем, возникающих в связи с нововведениями — поддерживать бессчетное множество оружия и бейджиков довольно сложное занятие.
В ситуации, когда просьба друга/босса/напарника изменить диалог или добавить новый вид оружия занимает слишком много времени, приходится прибегать к каким-то мерам — например, записи всей этой фигни в отдельные текстовые файлы.
Почти каждый геймдевелопер когда-нибудь делал карту уровней или диалоги в отдельном текстовом файле и потом их считывал. Взять хотя бы простейший вариант — олимпиадные задачи по информатике с файлом ввода
Но есть способ, на голову выше — использование скриптов.
Решение проблемы
«Окей, для таких дел хватает обычного файла с описанием характеристиков игрока. Но что делать, если в бурно развивающемся проекте почти каждый день приходится немножко изменять логику главного игрока, и, следовательно, много раз компилировать проект?»
Хороший вопрос. В этом случае нам на помощь приходят скрипты, держащие именно логику игрока со всеми характеристиками либо какой-либо другой части игры.
Естественно, удобнее всего держать, логику игрока в виде кода какого-нибудь языка программирования.
Первая мысль — написать свой интерпретатор своего скриптового языка, выкидывается из
К счастью, есть специальные библиотеки скриптовых языков для С++, которые принимают на вход текстовый файл и выполняют его.
Об одном таком скриптовом языке 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);
Символика Lua
Love2D — один из самых популярных движков на Lua
Мод для Minecraft на Lua
https://bitbucket.org/Izaron/luaforhabr/src
Исходный код
Автор: Izaron