BearLibTerminal - псевдоконсольное окно для рогалика

Форум библиотеки BeaRLib

Модератор: Apromix

Аватара пользователя
Феникc
Сообщения: 679
Зарегистрирован: 27 ноя 2010, 15:01
Откуда: Челябинск

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Феникc » 03 окт 2013, 10:08

Кстати, в выложеной версии clear_area поломан.
Реквестирую ман по подключению терминала к С#
Всё вышесказанное - ИМХО, если не указано обратное.

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Cfyz » 03 окт 2013, 11:38

Maelstraz писал(а):Возможность указывать не только координаты знакоместа для тайла, но и величину попиксельного смещения?
Именно так. Можно будет задать dx, dy для каждого тайла (которых, напоминаю, в одной ячейке может быть несколько). Цена реализации копеечная, а разных интересных плюшек можно сделать море. Это будет отдельная функция terminal_put_ext, сигнатуры terminal_put/terminal_print не изменятся; не хотите -- не пользуйтесь.
Феникс писал(а):Кстати, в выложеной версии clear_area поломан.
Упс, нигде в "демо" (которую я использую в роли теста) это не используется, проглядел.
Феникс писал(а):Реквестирую ман по подключению терминала к С#
Для C# нужен враппер, примерно как юнит для Паскаля (но C# я по крайней мере знаю, так что точно сделаю). Однако дилемма, можно оформить в виде сборки, которая подойдет всем .NET-языкам, но добавит еще одну .dll в папку проекта. А можно сделать в виде файла на C#, который надо будет включать в проект и работать будет очевидным образом только с C#, но зато лишних .dll не будет.
Пытается раскуклиться

Аватара пользователя
Феникc
Сообщения: 679
Зарегистрирован: 27 ноя 2010, 15:01
Откуда: Челябинск

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Феникc » 03 окт 2013, 12:06

Оба два сразу? Как вариант.
Всё вышесказанное - ИМХО, если не указано обратное.

Аватара пользователя
Maelstraz
Сообщения: 46
Зарегистрирован: 27 янв 2011, 22:41
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Maelstraz » 03 окт 2013, 18:30

Cfyz писал(а):Это будет отдельная функция terminal_put_ext, сигнатуры terminal_put/terminal_print не изменятся
Ну, теперь заживём.

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Cfyz » 05 окт 2013, 21:47

Махонький апдейт, так сказать 0.8.1: фикс terminal_clear_area + враппер для C#.

Враппер выполнен в виде файла на C# и должен включаться в проект (универсальную .dll придется сделать позже). В API внесены минимальные изменения для большего соответствия языку:
1. Не terminal_printf(0, 10, "HP: %d", hp); а Terminal.Print(0, 10, "HP: {0}", hp);
2. Не terminal_color(0xFFFF0000); а Terminal.Color(Color.Red); // тот самый Color, который из System.Drawing
3. Не terminal_get() != VK_CLOSE, а Terminal.Get() != Terminal.Keys.Close
4. ?????
5. PROFIT

Итого, программа-минимум:
Скрытый текст: ПОКАЗАТЬ

Код: Выделить всё

using System;
using BearLib;

namespace HelloWorld
{
    static class Program
    {
        static void Main()
        {
            Terminal.Open();

            // Выводим текст
            Terminal.Print(1, 1, "Hello, world!");
            Terminal.Refresh();

            // Ждем, пока пользователь не закроет окно
            while (Terminal.Get() != Terminal.Keys.Close);

            Terminal.Close();
        }
    }
}
Вложения
BearLibTerminal.cs.zip
(2.67 КБ) 82 скачивания
BearLibTerminal.dll.zip
(434.79 КБ) 94 скачивания
Пытается раскуклиться

Аватара пользователя
Apromix
Мастер
Сообщения: 1236
Зарегистрирован: 04 июл 2011, 10:44
Откуда: Украина, Черновцы
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Apromix » 22 окт 2013, 08:12

Вот и на шарпеге можно затерминаллить рогуль :lol: Зачет!

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Cfyz » 22 дек 2013, 00:18

С упорством, достойным, быть может, лучшего применения, продолжается разработка библиотечки BearLibTerminal. Вышло весьма существенное, предположительно последнее столь существенное обновление. Начавшиеся с привнесения чуть большей гибкости в вывод отдельных глифов, дальнейшие события могут быть достаточно точно охарактеризованы фразой “и тут Остапа понесло”. Но обо всем по порядку.

Изменения коснулись практически всего, кроме принципов работы. Программный интерфейс предыдущих версий формировался весьма спонтанно, внутренняя структура вообще началась с прототипа. Поэтому после недолго анализа этого бардака нестыковки и очевидные косяки были вычесаны из интерфейса. Так как отдельных мелких изменений выходит очень много, я буду описывать как будто бы с самого начала.

Библиотечка изначально и по сей день призвана решать проблему вывода текста и псевдографики на терминал в свете требований рогаликоподобных игр. Вывод в предоставляемую ОС консоль имеет кучу ограничений, обходные пути вокруг которых, если вообще есть, неуклюжи и постоянно забываются. Можно сделать окошко и набить все шишки самому, можно взять какой-нибудь движочек и долго его конфигурировать. А можно взять libtcod, ну или BearLibTerminal.

Обновленный API библиотеки состоит из 18 вызовов: terminal_open, close, set, refresh, clear, clear_area, layer, color, bkcolor, composition, put, put_ext, print, has_input, read, read_char, read_str, color_from_name. Те вызовы, что принимают строку, существуют в нескольких экземплярах в зависимости от битности символа, но этот момент скрыт биндингом (хедером) к языку. Хедер C/C++ прозрачно предоставляет однобайтные (terminal_set, terminal_print) и юникодные (terminal_wset, terminal_wprint) вызовы. Также, эти строковые вызовы имеют версии с форматированием (terminal_[w]setf, terminal_[w]printf). В случае C++, char* и wchar_t* функции перегружены и префикс "w" может быть опущен.

Вообще, с помощью этого не самого крупного API можно сделать очень дофига. Однако, совершенно необязательно пользоваться всем и сразу. При проектировании я старался придерживаться идеи, когда дополнительные фичи не маячат постоянно перед глазами, и, пока они явно не нужны, их как бы и нет.


Базовый вывод

В самом минимуме достаточно полудюжины вызовов. Вызов terminal_open инициализирует окошко псевдотерминала с классическими настройками: размером 80x25 сиволов, шрифт Fixedsys. Фактически, если все, что нужно — это быстрая замена cmd.exe, то настройка вызовом terminal_open и заканчивается. Следует отметить, что вложенный в библиотеку шрифт-по-умолчанию имеет набор символов в виде чуть расширенного WGL4. Это покрывает большое количество (по большей части европейских) языков и некоторую полезную пунктуацию, а также символы рисования рамочек.
Скрытый текст: ПОКАЗАТЬ
demo01.png
demo01.png (24.3 КБ) 3693 просмотра
demo02.png
demo02.png (38.56 КБ) 3693 просмотра
Помимо банальностей типа задания цвета символа и фона, первым, что следует рассказывать про BearLibTerminal, является возможность тривиальной смены шрифта на практически какой угодно. Во-первых, как и много где еще, можно использовать “тайловые” шрифты. Те, которые растеризованы в картинку. Например:

Код: Выделить всё

terminal_set("font: Bisasam_16x16_437.png");
Скрытый текст: ПОКАЗАТЬ
BitmapFont.png
BitmapFont.png (10.3 КБ) 3693 просмотра
Во-вторых, и это типа киллер-фича, можно поставить шрифтом сразу векторный .ttf.

Код: Выделить всё

terminal_set("font: VeraMono.ttf, size=12");
Скрытый текст: ПОКАЗАТЬ
TrueTypeFont.png
TrueTypeFont.png (12.19 КБ) 3693 просмотра
Да, установка параметров через строчку никуда не делась. Более того (тут warchief не будет рад), роль такой настройки только увеличилась. terminal_options была переименована в terminal_set, так как смысл ее сместился от настройки окна один раз когда-то до установки всех тяжелых рычагов внутри библиотеки, в том числе прямо в процессе работы. По приведенным выше примерам видно, что сменился формат указания опций. Хотя допустимо указывать параметры и так, как раньше, формат был расширен до

Код: Выделить всё

group: [name0=]value0, name1=value1;
который то же самое, что и

Код: Выделить всё

group.name0=value0, group.name1=value1;
только компактнее и проще для восприятия.


Работа с тайлсетами

В расширении возможностей терминала вообще и terminal_set в частности кроется причина переименования options в set. Если ранее шрифт это было огого, ШРИФТ, это ж полтерминала просто, то теперь основной шрифт это лишь один из потенциально доступных к использованию тайлсетов. Посудите сами, если терминал Юникодный и, значит, оперирует примерно 65 тысячами слотов под буквы, а в используемом шрифте лишь сотня-другая символов, то почему-бы не использовать оставшиеся примерно, хм, 65 тысяч слотов под другие тайлы? Например, вот так:

Код: Выделить всё

terminal_set("0xE000: tile.png");
Это самый простой пример: мы взяли, и set произвольный тайл в конкретный слот. Можно и похитрее, наверняка многие догадались, что основной тайловый шрифт — это просто тайлсет, установленный в ячейки-слоты 0x0000 и далее. А значит таким же образом можно подгружать и целые наборы тайлов:

Код: Выделить всё

terminal_set("0xE100: tileset.png, size=8x16");
Следующим существенным изменением является логичное снятие ограничения загруженного тайла размерами ячейки-знакоместа. Ранее был костыль, допускающий загрузку в слот только одиночных изображений произвольного размера посредством отдельного вызова со своим поведением. Теперь все тайлы могут быть какого угодно размера, по умолчанию они будут выводиться центрированно в знакоместе (но это можно изменить). Это позволяет иметь как набор букв размером, скажем 8x16, так и набор именно тайлов, скажем 32x32. Причем я больше вам скажу, тайлсеты можно творить как привычным образом, из картинок, так и из TrueType-шрифтов:
Скрытый текст: ПОКАЗАТЬ
demo03.png
demo03.png (33.58 КБ) 3693 просмотра
В этом примере в пункте 4 продемонстрировано, что "тайлы" можно расцвечивать (terminal_color) так же, как и символы, а еще их так же можно стакать в одной клетке. Что неудивительно, ведь что тайл из тайлсета, что символ шрифта для терминала все едино.

В пункте 5 использованы векторные шрифты FontAwesome и Zodiac-S, размер подогнан под 32x32.
Возможности загрузки отдельных “спрайтов” (самостоятельных картинок) тоже были расширены. Вообще деление на спрайты и тайлы сугубо логическое, просто некоторые операции имеют больше смысла для одной группы и меньше для другой. Спрайты, предположительно, должны использоваться для вывода относительно больших, одинарных картинок: фон, портрет монстра, арт какой-нибудь. Имеющаяся картинка может иметь несколько отличные от желаемыех размеры и обрезать ее то так, то эдак в процессе прототипирования откровенно не с руки. Или быть может остальной интерфейс весь на векторных шрифтах и может менять размеры. На этот случай добавлена возможность при загрузке переразмерить спрайт:
Скрытый текст: ПОКАЗАТЬ
Изображение

В этом примере три картинки внизу переразмерены при загрузки с применением различных фильтров: ближайшая точка, билинейный, бикубический.

Третья картинка иллюстрирует возможность загрузки тайла/спрайта не только из файла (.bmp, .png или .jpg), но и прямо из памяти. Об этом несколько позже.
Так как было снято ограничение на строгое соответствие размера тайла размеру знакоместа, показалось логичным сделать то же самое и в обратную сторону: отвязать размер знакоместа от размера символа основного шрифта (именно так вычислялся размер знакоместа в предыдущих версиях, строго bbox шрифта). С помощью параметра window.cellsize можно как задать произвольное значение, так и вернуться потом к поведению по умолчанию:
Скрытый текст: ПОКАЗАТЬ
Изображение
По этому же поводу следует отметить, что несмотря на центрирование возможно больших или меньших символов в ячейках произвольно установленного размера, символы рисования рамок всегда подходящего размера (ибо генерируются на лету). Т. е. если этими рамочками рисовать уровень или интерфейс, то все равно можно поиграть размерами шрифта или ячейки для лучшего вида, ничего не разъедется:
Скрытый текст: ПОКАЗАТЬ
Изображение
Следующим моментом, на который следует обратить внимание является то, что комбинируя вышеописанные возможности (несколько тайлсетов, тайлсеты из TrueType, произвольный размер, центрирование), можно выводить текст аж несколькими шрифтами сразу:
Скрытый текст: ПОКАЗАТЬ
Изображение
Наглядное пояснение как это работает потребует дополнительных иллюстраций, что перегрузит и без того огромный пост, так что позже. Пока просто отмечу, что это пусть и не столь интуитивно, как вывод одним основным шрифтом, но все-таки совсем не сложно.


Слои

Следующим новым моментом является такая невзрачная, но очень местами полезная сущность как слои. Слой — это просто еще один набор ячеек, такой же как всегда был в распоряжении в терминале. Возможность положить в ячейку несколько тайлов никак к слоям не относится, можно сто тайлов в ячейку первого слоя и еще сто — в ячейку второго. Нюансами работы со слоями является то, что фон ячейки есть только у самого первого, нижнего слоя, и то, что clear_area работает с текущим слоем.

Польза слоев заключается в двух вещах. Во-первых, строгий порядок (да вообще любой порядок) вывода тайлов могут помешать желаемому выводу, так как тайлы, выводящиеся позже обязательно будут наезжать на тайлы выведенные ранее. Со слоями можно сгруппировать вывод в несколько “частей” с желаемым порядком вывода. Во-вторых, иногда можно немного упростить себе жизнь модифицируя только один слой, а остальные будут отрисовываться сами по себе.
Скрытый текст: ПОКАЗАТЬ
Изображение
Расширенный вывод

Далее еще вкуснятина. В предыдущих версиях можно было положить тайл в ячейку лишь ровно-ровно. Неоднократно высказывалось мнение, что было бы прикольно иметь возможность положить туда тайл с некоторым смещением, добиваясь этим различных эффектов. Теперь можно. Кроме смещения я также запилил еще одну фичу (ну чтобы два раза не вставать), а именно — указание индивидуального цвета для каждого угла тайла. И все это, разумеется, можно комбинировать со всеми предыдущими возможностями (наложение, тайлы, центрирование тайлов):
Скрытый текст: ПОКАЗАТЬ
Изображение
Комбинируя слои и возможность попиксельного смещения тайлов, можно добиться алгоритмически тривиальной реализации некоторых простых, но интересных эффектов. Например, количество урона, визуализируемое как отлетающая от противника цифра. Т. е. таких old-school вида спецэффектов, когда весь мир замирает, давая возможность полюбоваться высплывающим сообщением, привлекающим внимание коротким миганием, взрывом, цепочкой молний и т. д.
Скрытый текст: ПОКАЗАТЬ
Изображение
Суть в том, что весь “спецэффект” реализуется строго в пределах одной функции, без необходимости трогать-обновлять все остальное на экране в процессе анимации.
Скрытый текст: ПОКАЗАТЬ
Пример с вкраплениями всевдокода:

Код: Выделить всё

void AnimateDamage(int x, int y, char* text)
{
	terminal_layer(1);

	for (int i=0; i<N && !terminal_has_input(); i++)
	{
		terminal_clear_area(0, 0, 80, 25);
		int dx = Fx(i), dy = Fy(i);
		terminal_printf(x, y, "[offset=%dx%d]%s", dx, dy, text);
		terminal_refresh();
		delay(1000/fps);
	}

	terminal_clear_area(0, 0, 80, 25);
	terminal_layer(0);
}
Действо состоит из трех частей. Во-первых, переходим на отдельный слой. Во-вторых, в этом слое независимо происходит анимация чего-либо, причем вся остальная сцена отрисовывается терминалом автоматически. В-третьих, возвращаем все на место.
Можно сделать еще один шажок и пойти еще дальше от собственно терминала в сторону тайлового движка. Так как в одну ячейку можно положить неограниченное количество тайлов, смещение задается у каждого индивидуально, а точку отсчета тайла возможно (естесственно) установить в его угол, появляется возможность немного абстрагироваться от ячеек и рисовать тайлы по пиксельным координатам просто передавая их двумя аргументами правее: terminal_put_ext(0, 0, x, y, code, 0);
Скрытый текст: ПОКАЗАТЬ
Изображение

Низкий FPS, разумеется, только для уменьшения размера гифки.
Выше я упоминал, что загрузить тайл можно из TrueType шрифт, из растровой картинки-файла или картинки в памяти. Действительно, если сделать так:

Код: Выделить всё

color_t* pixels[32*32];
terminal_setf(“0xE000: %#p, size=32x32”, pixels);
то получится тайл из указанной области памяти. Для того, чтобы это сработало, “имя” тайлсета должно быть строго в форме адреса памяти, в шестнадцатиричном виде и с префиксом 0x.
Скрытый текст: ПОКАЗАТЬ
Изображение

На этой картинке иллюстрируется отрисовка миникарты через создание ее в памяти и загрузки в качестве спрайта.
Ввод

Ввода изменения коснулись в меньшей степени.

Во-первых, Terminal_get был переименован в terminal_read, так как имя _set весьма удачно подошло совершенно иному по смыслу вызову (и если уж _get и появится, то это будет именно чтение настроек и переменных).

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

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


Где?

Код переехал в паблик: https://bitbucket.org/cfyzium/bearlibterminal

32-битная сборка под Windows: ссылка, остальные разрядности и Linux чуть позже.

Биндинг пока только для C/C++. С точки зрения привязки библиотеки, изменения невелики, так что Pascal и C# скоро будут.

Документацию придется всю переписывать =_=.
Последний раз редактировалось Cfyz 22 дек 2013, 21:59, всего редактировалось 2 раза.
Пытается раскуклиться

Аватара пользователя
karagy
Сообщения: 1271
Зарегистрирован: 10 янв 2007, 14:13

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение karagy » 22 дек 2013, 07:07

Не собираешься сделать анонс на RogueBasin?

оффтоп: чем ты делаешь animated gif? (например demo11.gif)

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Cfyz » 22 дек 2013, 08:58

karagy писал(а):Не собираешься сделать анонс на RogueBasin?
Конечно собираюсь. Вон весь текст в примерах на английском =) Но чуть позже, надо сначала сделать документацию: если здесь народ хоть как-то в теме, то там увидят вообще впервые.
karagy писал(а):чем ты делаешь animated gif?
Ручками. Fraps записывает .avi, ffmpeg бьет на отдельные фреймы, нужные импортирую пачкой в Gimp как слои, а тот уже умеет сохранять .gif. Вероятно, есть путь попроще, сразу .avi -> .gif, но с Gimp можно подредактировать картинку.
Пытается раскуклиться

Аватара пользователя
Jolly Roger
Сообщения: 2973
Зарегистрирован: 27 ноя 2009, 09:10
Откуда: Minsk, Belarus

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Jolly Roger » 23 дек 2013, 05:57

Я кончил, товарищи!
Теперь будущее за медведем! =D>
Есть у меня наработки для одного сайд проекта под медведь. Перееду на новую версию!
Писать диздок спустя несколько лет разработки и множества изменений концепции - исконная русская девелоперская традиция.

Аватара пользователя
Apromix
Мастер
Сообщения: 1236
Зарегистрирован: 04 июл 2011, 10:44
Откуда: Украина, Черновцы
Контактная информация:

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Apromix » 23 дек 2013, 06:31

Понравилось :) Очень =D> =D> =D>

Аватара пользователя
warchief
Сообщения: 300
Зарегистрирован: 11 янв 2008, 09:55
Откуда: Озеро снов

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение warchief » 23 дек 2013, 06:33

Вопросы:
1) как узнать, какая клавиша была отжата?

2) У меня такой ввод
bool Engine::input()
{
do
{
int key = terminal_read_char();
wchar wc = L' ';
if (key == TK_INPUT_CALL_AGAIN)
{
key = terminal_read();
if (key == TK_CLOSE)
return false;
}
else if (key > 0)
{
wc = key;
}

if (m_inputlistener.empty())
return true;

// нажатие кнопки
for (auto it = m_inputlistener.begin(); it != m_inputlistener.end(); ++it)
{
if ((*it)->KeyPressed(KeyEvent(wc, key)) == true)
break;
}

} while (terminal_has_input());

return true;
}
Правильно написал? А то куча команд. хотя по идее нужно было оставить только terminal_read() и функцию, которая по ее возвращаемому коду возвращает символ.

3) В этом коде есть момент, через terminal_read_char() я получаю символ нажатой кнопки, а как теперь правильно узнать код этой кнопки? Звать terminal_read()()?

Аватара пользователя
Frolik
Сообщения: 624
Зарегистрирован: 08 мар 2011, 17:21

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение Frolik » 23 дек 2013, 06:38

Jolly Roger писал(а):Я кончил, товарищи!
Теперь будущее за медведем! =D>
Аналогично. Пока остальные активно обсуждали астрал, Саня сотворил настоящую магию.
Прошу предоставить хедеры для делфей.

Аватара пользователя
warchief
Сообщения: 300
Зарегистрирован: 11 янв 2008, 09:55
Откуда: Озеро снов

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение warchief » 23 дек 2013, 08:38

Как научить terminal_read_char() не ждать нажатия кнопки? А то из-за нее у меня не получается сделать реалтайм.

То есть я хочу так - игра выполняется в реалтайме, пользователь нажимает кнопку, система определяет код нажатой кнопки и ее символ. То есть не ждет, когда игрок нажмет кнопку, а опрашивает текущее состояние. Пользователь отжимает кнопку, теперь система определяет код отжатой кнопки и ее символ

Аватара пользователя
warchief
Сообщения: 300
Зарегистрирован: 11 янв 2008, 09:55
Откуда: Озеро снов

Re: BearLibTerminal - псевдоконсольное окно для рогалика

Сообщение warchief » 23 дек 2013, 10:01

Вообщем разобрался почему у меня не реалтайм. Вместо do...while, надо было просто
while (terminal_has_input()){}

Возникла другая проблема - мерцание текста, как лечить, надеюсь в движке есть двойной буфер? Собственно эта проблема у меня была и не в реалтайме.

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 22 гостя