Быстрый движок на C# в Fluent-стиле

Чисто ради любопытства за пару часов накидал рыбу парсерного (можно и менюшного) движка, позволяющего писать игры на C# в Fluent-стиле (это последовательность вызовов методов, смотрите пример кода ниже).
Выглядит самый простенький пример так:
Игра.Новая()
	.Имя("Пробная игра")
	.Локации()
		.Локация("Пещера", "Холодная и мрачная пещера")
		.Локация("Поляна", "Светлая и приветливая полянка")
	.КонецЛокаций()
	.Настройки()
		.НачатьВЛокации("Пещера")
	.КонецНастроек()
.ЗавершитьСоздание()
.ЗапуститьИгру();


Полный код программы выглядит так (без упоминания namespace и usings):
Код
class Program
{
	static void Main(string[] args)
	{
		try
		{
			Игра.Новая()
				.Имя("Пробная игра")
				.Локации()
					.Локация("Пещера", "Холодная и мрачная пещера")
					.Локация("Поляна", "Светлая и приветливая полянка")
				.КонецЛокаций()
				.Настройки()
					.НачатьВЛокации("Пещера")
				.КонецНастроек()
			.ЗавершитьСоздание()
			.ЗапуститьИгру();
		}
		catch (ОшибкаОписания ошибкаОписания)
		{
			Игра.Вывести(ошибкаОписания);
			Игра.ОжидатьКлавишу("Нажмите любую клавишу для выхода.");
		}
	}
}


А при запуске выводит текст в консольном окне:
Запускается игра "Пробная игра"...
Локации игры: "Пещера", "Поляна".
Игрок начинает в локации "Пещера".
Игра запущена.
Нажмите любую клавишу для выхода.

Если вы ошибаетесь с ссылками, например, на локацию, в которой начинает игрок (пишете «ПещерО» вместо «Пещера»), то запуск обрабатывает ошибку так:
При загрузке игры произошла ошибка:
        Настройка "НачатьВЛокации" ссылается на несуществующую локацию "ПещерО".
Нажмите любую клавишу для выхода.

Это то, что получилось навскидку. Вообще, можно, например, создание локаций вынести в отдельную конструкцию, чтобы потом было просто на них ссылаться, а не писать текстом, в котором можно ошибиться. Попробую, наверное, сделать такой вариант позже.

Как тогда выглядит разработка? Думаю, примерно так: качаете и ставите бесплатную Visual Studio Community Edition (или лёгкую кроссплатформенную Visual Studio Code), качаете один файл библиотеки с движком, добавляете его в Cтудии в проект как библиотеку и пишете игру.
А можно пойти чуть дальше и сделать так: писать код с проверками и подсказками в Visual Studio Code, а вставлять его в специальный уже собранный движок, который будет его сам компилировать на лету и запускать или делать что-то ещё.
У меня сходу компилируется exe-файл для запуска под Windows, но, по идее, современный .NET Core позволяет собирать и под *nix.
Кроме того, можно не запускать игру методом ЗапуститьИгру(), а, например, выгрузить всю игру в какой-то файл исходного кода, например, в XML для ЯРИЛа или urq-файл (если делать менюшку).

Что думаете о таком способе разработки? Насколько сложно? Насколько удобно? Плюс кода на C# в том, что Студия контролирует вводимый код и не даст писать несовместимые инструкции, выдаёт кучу подсказок.

UPD. Проект на GitHub.
UPD2. Вынес создание локаций для упрощения ссылок.
Локация пещера = Локация("Пещера", "Холодная и мрачная пещера");

НоваяИгра()
	.Имя("Пробная игра")
	.Локации()
		.Локация(пещера)
		.Локация("Поляна", "Светлая и приветливая полянка")
	.КонецЛокаций()
	.Настройки()
		.НачатьВЛокации(пещера)
	.КонецНастроек()
.ЗавершитьСоздание()
.ЗапуститьИгру();

UPD3. Доработал движок для менюшек.
Теперь код выглядит так:
Локация пещера = Локация("Пещера", "Холодная и мрачная пещера.");
Локация полянка = Локация("Поляна", "Светлая и приветливая полянка. Рядом чернеет вход в пещеру.");

НоваяИгра()
	.Имя("Пробная игра")
	.Локации()
		.Локация(пещера)
			.Кнопка("Выйти из пещеры", полянка)
			.КонецЛокации()
		.Локация(полянка)
			.Кнопка("Войти в пещеру", пещера)
			.КонецЛокации()
		.КонецЛокаций()
	.Настройки()
		.НачатьВЛокации(пещера)
		.КонецНастроек()
.ЗавершитьСоздание()
.ЗапуститьИгру();
А игра так:
Скриншот IFluent для меню

Ветвь проекта с этими правками: github.com/realsonic/IFluent/tree/feature/menu

Как вам такой подход?

18 комментариев

k12th
Много вопросов:) Как указываются связи между локациями? как добавить в локацию неписей и предметы? как добавить цвета? где собственно скачать либу?

Мне всегда казалось, что fluent-стиль красив на бумаге, а на практике это неудобно дебажить, рефачить и поддерживать code-style потому что у всех свое представление, где нужен отступ, а где нет.
Но c# это круто!
realsonic
Нет пока ответов на эти вопросы. Это скорее прототип прототипа, а не движок. -) Я его накидал для проверки удобности и реализуемости fluent-стиля кодирования игр. Можно расширить. Проще сделать движок для менюшки, чем для для парсерки. Посмотрю, может, накидаю. :)
По идее, при написании игры уже не нужно дебажить код движка, а для дебага кода игры можно вставить специальные методы логирования и проч. С code-style да, есть проблема — студия их может переформатировать.
k12th
Мне кажется, смысл использовать C# для IF только в поддержке инструментами (студия + решарпер, куча готовых либ и рекомендованных подходов, ну и кроссплатформенность). И писать игры императивно (ну примерно как мы это делаем в Unity3D). Для этого-то и нужен дебаг.
А делать из шарпа декларативный DSL при наличии в мире XML, JSON (ну json не фонтан, пусть будет json5) и YAML как-то странно, имхо. Можно же написать XSD или json-схему и писать в любимом редакторе с автокомплитом и подсказками, и перегонять в тот же xml или urq-файл какими-то скриптами.
realsonic
Ну, XML и JSON для неспециалистов ужасен, никто не хочет на нём писать, даже я. :)
Есть ещё мысль приспособить JetBrains MPS для этих целей, но это прорва работы.
k12th
и то и другое с нормальными подсказками ничем не отличается от любого другого ЯП.
realsonic
Отличается лишними символами, что и отпугивает многих.
k12th
Так-то я всецело за идею писать игры на явно типизированных языках. Просто DSL из них получается не очень, этот факт нужно принять и извлекать из него пользу, а не бодаться с ним:)
realsonic
Опять же, я попробовал для того, чтобы такой код был максимально понятен не программистам. Программисты и сами смогут всё написать. )
k12th
Почему вы думаете, что fluent-стиль не-программистам будет понятнее? Есть какие-то исследования? Или только потому что он использовался всякими идиотами в jquery?) Так-то, полагаю, неспециалистам одинаково непонятно — и там какие-то скобочки и крючочки и тут.

Программистам тоже хочется писать удобно и без лишнего мусора, кстати говоря.
Комментарий отредактирован: 25 июля 2017, 16:48
techniX
В JQuery, кстати, fluent-стиль (он же method chaining) использовался с вполне конкретной целью — чтобы лаконично записать вызов нескольких методов у одного и того же объекта. Иногда это действительно удобно. Конечно, когда в такую цепочку выстраивается 10-15 вызовов, отлаживать и разбирать такой код становится сложновато :(
k12th
Программисты и сами смогут всё написать. )

Программисты обычно хотят написать игру, а не изобретать очередной движок.
realsonic
Тогда достаточно сделать движок в виде библиотеки, которую можно подключить и сразу использовать. Но что-то мало таких движков. А Inform и TADS ещё изучать надо. Мне вот, например, не очень хочется.
Oreolek
Ну, тот же Ink на C# работает именно как встраиваемая библиотека.
realsonic
Русских авторов на нём немного?
На западе с движками и писателями всё получше.
realsonic
Вот есть ещё пример адаптации языка под декларативный стиль — на груви. Но он уже не проверяет синтаксис, т.к. использует позднее связывание.
realsonic
Доработал для менюшек — см. под спойлером UPD3. Работает, можно играть. ))
techniX
Мне кажется, такой подход был бы удобен в первую очередь для тестирования прототипов игр. Набросал быстро логическую структуру игры — локации, предметы, паззлы — а потом гоняешь тесты или карту зависимостей генерируешь.
Комментарий отредактирован: 25 июля 2017, 14:42
realsonic
Да, можно, в принципе, дописать как угодно — и для тестов, и выгрузки исходников других платформ, и зависимости.