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

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

Модератор: Apromix

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

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

Сообщение karagy » 23 дек 2013, 10:06

warchief писал(а):2) У меня такой ввод
Options.input_nonblocking выставил? Ато может получиться схематично:

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

do
  блокирующее чтение
while (неблокирующая проверка наличия)
Толково, чо уж.

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

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

Сообщение Cfyz » 23 дек 2013, 10:32

warchief писал(а):1) как узнать, какая клавиша была отжата?
3) В этом коде есть момент, через terminal_read_char() я получаю символ нажатой кнопки, а как теперь правильно узнать код этой кнопки? Звать terminal_read()()?
Боюсь чтение текста так не работает. Даже для простой латиницы 'a' -- это одна клавиша (и одно событие ввода), а вот 'A' -- уже две. Для того, чтобы набрать á на немецкой клавиатуре, надо отдельно сначала нажать т. н. мертвую клавишу [´], затем a. Причем это не единственный способ составлять символы, но главное что комбинации совершенно разнообразные. Если этого мало, есть и восточные языки (поддержки которых в терминале пока нет, но не будем дискриминировать).

Таким образом, нет возможности провести строгое сопоставление вводимого символа ни с физически нажатыми клавишами, ни тем более их комбинацией и порядком нажатия. Поэтому terminal_read_char работает по следующему принципу: если следующее в очереди ввода событие порождает печатный символ, то возвращается этот символ. Если нет (в очереди половина комбинации или просто посторонние клавиши), то возвращается TK_INPUT_CALL_AGAIN. Это означает, что прочитать текстовый символ пока невозможно. Паттерн использования следующий: на каждую операцию ввода вызовом terminal_read_char проверяем наличие текстового символа, если такого нет, читаем привычным terminal_read.
warchief писал(а):Как научить terminal_read_char() не ждать нажатия кнопки? А то из-за нее у меня не получается сделать реалтайм.
Помимо варианта с проверкой terminal_has_input есть еще неблокирующий ввод:

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

terminal_set("input.nonblocking=true");
Тогда в случае, если в очереди ввода нет ничего, terminal_read[_char] будут возвращать TK_INPUT_NONE. Следует заметить, что если в очереди все-таки что-то есть, просто это что-то не представляет из себя текстовый символ, то terminal_read_char вернет TK_CALL_AGAIN.

Ах да, karagy уже указал. Только не "Options.input_nonblocking", а "input.nonblocking", а то пока без справки еще запутаемся =)
warchief писал(а):Возникла другая проблема - мерцание текста, как лечить, надеюсь в движке есть двойной буфер? Собственно эта проблема у меня была и не в реалтайме.
В движке буфер даже более чем двойной =) мерцания быть не должно, возможно ты где-то делаешь лишний terminal_refresh?
Пытается раскуклиться

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

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

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

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

Вообщем пока общие впечатления - текущая BearLibTerminal, намного лучше двух предыдущих выложенных версий. Ну если не считать строковых конфигураций:)

Что я еще спрашивал, но не помню, получил ли ответ - масштабирование окна будет делаться? Ну то есть например создаю окно 80х25 и размером шрифта в 10. А у меня экран 1900х1200. Игру придется рассматривать под микроскопом. В идеале хотелось бы две возможности:
1) развернуть окно на весь экран так, чтобы количество символов само пересчитывалось в зависимости от размера
2) увеличивать окно на определенный коэффициент, то есть количество символов остается прежним, а вот размер шрифта увеличивается, понятно что здесь не получится сделать полный экран, но и не надо - будет олдскульный 80х25, но при этом окно будет занимать как можно больше размера (как в старые добрые времена мониторов в 640х480 когда стандартная консоль как раз почти весь экран и занимала). А так как в терминале TTF, то даже качество не пострадает
Конечно это можно сделать в ручную, но хотелось бы автоматики

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

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

Сообщение warchief » 23 дек 2013, 12:05

Паттерн использования следующий: на каждую операцию ввода вызовом terminal_read_char проверяем наличие текстового символа, если такого нет, читаем привычным terminal_read.
Можно примером показать? А то у меня так:

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

while (terminal_has_input())
{
    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;
    }		

    // нажатие кнопки
    for (auto it = m_inputlistener.begin(); it != m_inputlistener.end(); ++it)
    {
        if ((*it)->KeyPressed(KeyEvent(wc, key)) == true)
            break;
    }
}
То есть в метод KeyPressed, я передаю символ типа wchar_t(wc) и код типа int (key)

И сейчас приключилась беда:

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

bool HeroCreateModule::KeyPressed(const KeyEvent &arg)
{
    if (arg.code == TK_R)
        newgentate = true;

    return false;
}
Событие возникает только тогда, когда я ввожу 'R' зажимая шифт или с влкюченным капслоком. То есть проверка успешна только когда я ввожу большую букву R. А мне надо чтобы и с маленькой работало, а то бред получается:) И здесь конечно же мне надо знать именно код

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

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

Сообщение warchief » 23 дек 2013, 12:14

Вообщем тестовым путем выяснил что мой код при нажатии маленькой 'r' возвращает код 114, а при нажатии большой - 82.

Вообще хотелось бы вот так чтобы было
int key = terminal_read(); // Получаем код нажатой кнопки, при этом пофиг какая раскладка, и какие вспомогательные клавиши нажаты
wchar_t с = terminal_getkey(key);// по полученному коду получаем символ
А то сейчас terminal_read_char() возвращает для большой буквы один код, для маленькой другой, в другой раскладке третий - и как вообще тогда делать? Зафигачить везде terminal_read? А если мне надо чтобы игрок что-то вводил на экран на родном русском языке? делать отдельный костыль?

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

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

Сообщение Феникc » 23 дек 2013, 12:41

warchief жжот.
read_char возвращает введённый символ, а не кнопку. Если тебе нужно получить нажатую клавишу, используй terminal_read.
Вполне логично что у большой и маленькой буквы разные коды.

А русские буквы, судя по описанию, тоже вводятся с помощью read_key.
Всё вышесказанное - ИМХО, если не указано обратное.

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

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

Сообщение Cfyz » 23 дек 2013, 12:55

warchief писал(а):Можно примером показать? А то у меня так:
Не вижу ничего плохого. Думаю для разных ситуаций будет весьма разный код. terminal_read_char -- это достаточно узкоспециализированная штука для конкретного применения.
warchief писал(а):Событие возникает только тогда, когда я ввожу 'R' зажимая шифт или с влкюченным капслоком. То есть проверка успешна только когда я ввожу большую букву R. А мне надо чтобы и с маленькой работало, а то бред получается:) И здесь конечно же мне надо знать именно код
warchief писал(а):Вообщем тестовым путем выяснил что мой код при нажатии маленькой 'r' возвращает код 114, а при нажатии большой - 82.
warchief писал(а):А то сейчас terminal_read_char() возвращает для большой буквы один код, для маленькой другой, в другой раскладке третий - и как вообще тогда делать? Зафигачить везде terminal_read? А если мне надо чтобы игрок что-то вводил на экран на родном русском языке? делать отдельный костыль?
Нажатие определенной клавиши и ввод некоторого символа -- это две большие разницы. Давайте определимся:
1. Если тебе надо отслеживать именно нажатие клавиши (скажем r, R, к или К -- четвертая клавиша в верхнем ряду), то необходимо использовать terminal_read. Я думаю тут все понятно.
2. Если тебе надо отслеживать визуально (или наоборот, по смыслу) схожие буквы, то придется использовать какие-либо средства интернационализации. Задача проверить, является ли один символ строчной версией другого, нетривиальна и универсального решения не имеет. Подобный хай-левел анализ текста в задачу терминала не входит, см. в сторону поддержки локалей языком и библиотек наподобие ICU.

Самым простым выходом из ситуации, когда надо, чтобы игрок что-то вводил на родном русском, будет просто разделить ввод на:
1. Интерфейс управления: навигация по меню, передвижение, внутриигровые действия. Он строится на основе виртуальных кодов клавиш (terminal_read).
2. Работу с текстом: ввод имени, сохранение заметок. Текст читается как юникод посредством terminal_read_char и никак не анализируется.
Пытается раскуклиться

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

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

Сообщение warchief » 23 дек 2013, 13:12

warchief жжот.
read_char возвращает введённый символ, а не кнопку. Если тебе нужно получить нажатую клавишу, используй terminal_read.
Вполне логично что у большой и маленькой буквы разные коды.
Ты не понял. Я нажал кнопку, и хочу получить с нее И КОД И СИМВОЛ одновременно.
Самым простым выходом из ситуации, когда надо, чтобы игрок что-то вводил на родном русском, будет просто разделить ввод на:
Я хочу отделить терминал от логики игры. У меня есть функция OnKeyDown(), и теперь мне придется еще писать OnChar()? Вообще опять костыли. Проблема в том, что я не жду факта нажатия клавиши, я проверяю текущее нажатие. Поэтому сложно - игрок нажал клавишу, а как понять коду что сейчас надо - символ или код? Придется где-то писать отдельный стейт, в котором сообщать что вот сейчас игрок будет писать текст, а вот сейчас он играет - криво
Задача проверить, является ли один символ строчной версией другого, нетривиальна и универсального решения не имеет
На WinAPI:

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

eKeyCodes KeyIndex;
wchar_t buffer[1];
BYTE lpKeyState[256];
KeyIndex = static_cast<eKeyCodes>(wParam);
GetKeyboardState(lpKeyState);
ToUnicode(wParam, HIWORD(lParam)&0xFF, lpKeyState, buffer, 1, 0);
И получается именно то, что мне нужно - в KeyIndex хранится код символа в buffer[0] - сам символ. Юникод, никаких проблем. ОС для того и нужна чтобы знать, какая локаль у пользователя и как правильно интерпретировать

Обрати внимание, ToUnicode() как раз возвращает символ по коду нажатой клавиши, о чем я выше и писал, как я хотел бы это видеть

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

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

Сообщение warchief » 23 дек 2013, 13:31

Распишу как я делаю ввод. Для этого я использую паттерн Слушатель (Listener) и реализую простую событийную модель. Идея в следующем - движок получает событие и оповещает всех своих слушателей об этом событии. А вот они там уже сами решают что с этим событием делать. То есть движок НЕ ЗНАЕТ О ТОМ, ДЛЯ ЧЕГО ИГРОК НАЖАЛ КНОПКУ - ДЛЯ ИГРЫ, ИЛИ ТЕКСТ ПИШЕТ.
Вот оповещение о том, что нажата кнопка

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

for (auto it = m_inputlistener.begin(); it != m_inputlistener.end(); ++it)
{
   if ((*it)->KeyPressed(KeyEvent(wc, key)) == true) break;
}
А вот один из слушателей (это окно интро, там для каждого окна свой слушатель)

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

bool Title::DoInit()
{	
    m_eng->InitImg(IMAGE_0, "media/menu_title.png", true);	
    return true;
}

bool Title::DoRun()
{	
    m_eng->DrawImg(0, 0, IMAGE_0);
    return true;
}

void Title::DoClose()
{
    m_eng->CloseImg(IMAGE_0);
}

bool Title::KeyPressed(const KeyEvent &arg)
{
    // переходим в главное меню
    if (arg.code == TK_ESCAPE)
        m_mgr->SetActiveModule(MainMenuScene);
    return false;
}
Ввод выполняется в методе KeyPressed(). Слушатели сами там по хитрому переключаются между собой, поэтому движок просто физически не может знать - где вообще сейчас игрок и что он делает. И это как раз правильно, ибо абстракция, каждый класс должен делать свою работу и не лезть в чужую.
И вот если не будет преемлемого решения когда известен и код и символ, то придется всю эту красоту ломать костылями

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

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

Сообщение Cfyz » 23 дек 2013, 14:05

warchief писал(а):Я нажал кнопку, и хочу получить с нее И КОД И СИМВОЛ одновременно.
warchief писал(а):Поэтому сложно - игрок нажал клавишу, а как понять коду что сейчас надо - символ или код? Придется где-то писать отдельный стейт, в котором сообщать что вот сейчас игрок будет писать текст, а вот сейчас он играет - криво
Боюсь тебе в любом случае придется как-то дать коду понять, ввод текста сейчас или иная обработка. Потому что это больно разные ситуации, одни и те же клавиши могут иметь совершенно разное значение. По-моему, если сделать чтение, но не делать обработку прочитанного (так как нет отдельного стейта), а делать обработку уровнем выше, то получится просто лишний враппер из одной очереди ввода в другую. Ок, в следующем сообщении ты приводишь контраргумент.

Наверное, все-таки не помешало бы иметь способ выцепить и код, и символ одновременно. Хм, надо подумать.
warchief писал(а):Обрати внимание, ToUnicode() как раз возвращает символ по коду нажатой клавиши, о чем я выше и писал, как я хотел бы это видеть
Неа, он возвращает по виртуальному коду клавиши (первый аргумент), по ее аппаратному коду (второй) и полному состоянию клавиатуры (третий). И то, это не гарантирует верной трансляции, не зря WM_UNICHAR придумали. Восстановить же символ строго по коду или код строго по символу невозможно. Впрочем, ранее я немного иное имел в виду, не пары код-символ, а пары символ-символ ('a' и 'A' -- это формы одной буквы?).
Пытается раскуклиться

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

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

Сообщение Cfyz » 23 дек 2013, 14:15

warchief писал(а):получить с нее И КОД И СИМВОЛ одновременно
Навскидку (я не утверждаю, что это лучшее решение) должного эффекта возможно добиться переделав функцию terminal_read_char в terminal_peek_char, которая возвращает символ не вычитывая его клавишу из очереди. Ну или не возвращает, если нет символа:

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

while (terminal_has_input())
{
    wchar_t symbol = terminal_peek_char(); // Unicode или нейтральный 0.
    int code = terminal_read();
    if (code == TK_CLOSE) std::terminate();
    ReportInput(code, symbol);
}
Насколько я вижу, поведение практически не меняется. Все равно terminal_read всегда, обязательно должен идти в паре к terminal_xxx_char, так как ввод когда-то надо прекращать, а это можно отследить только вычитывая виртуальные коды. Таким образом символы все равно будут потихоньку выбираться из очереди.
Пытается раскуклиться

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

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

Сообщение Cfyz » 23 дек 2013, 16:02

Пропустил как-то
warchief писал(а):Что я еще спрашивал, но не помню, получил ли ответ - масштабирование окна будет делаться? Ну то есть например создаю окно 80х25 и размером шрифта в 10. А у меня экран 1900х1200. Игру придется рассматривать под микроскопом. В идеале хотелось бы две возможности:
1) развернуть окно на весь экран так, чтобы количество символов само пересчитывалось в зависимости от размера
2) увеличивать окно на определенный коэффициент, то есть количество символов остается прежним, а вот размер шрифта увеличивается, понятно что здесь не получится сделать полный экран, но и не надо - будет олдскульный 80х25, но при этом окно будет занимать как можно больше размера (как в старые добрые времена мониторов в 640х480 когда стандартная консоль как раз почти весь экран и занимала). А так как в терминале TTF, то даже качество не пострадает
Конечно это можно сделать в ручную, но хотелось бы автоматики
1. Если развернуть экран так, чтобы количество знакомест само пересчитывалось, то все равно придется рассматривать под микроскопом =) Это на самом деле можно сделать, я разве что не очень понимаю что делать с краями экрана, где останется немного пустого места. Сетка ячеек скорее всего ровно все экранное пространство не заполнит.
2. Основная проблема в том, что масштабирование на произвольный коэффициент даст плохое качество картинки, особенно текста. А сделать так, чтобы TrueType-тайлы масштабировались векторно, в то время как остальные -- растрово, я вижу весьма трудоемкой задачей. Можно сделать простое 2x увеличение, наверное.

В масштабировании, возможно, сможет помочь не упомянутый мной выше момент (забыл =_=). У TrueType-шрифта размер можно задавать не только в пунктах, но и bbox. Т. е. если, скажем, сделать так:

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

terminal_set("font: smth.ttf, size=10x16");
то размер шрифта будет выбран таким, чтобы символы влезали в указанные размеры. Количество свободного места в ячейке будет немного "гулять" (пропорции символов никуда не денутся), но так можно более точно подстраивать размеры окна.
Пытается раскуклиться

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

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

Сообщение warchief » 24 дек 2013, 01:22

в terminal_peek_char, которая возвращает символ не вычитывая его клавишу из очереди. Ну или не возвращает, если нет символа:
О, а я как раз о таком тоже подумал, то есть сначала опознать символ, но не извлекать его из очереди, а распознать код и с этим извлечь... Так значит можно? Ну вообщем-то тогда тоже приемлемое решение
где останется немного пустого места.
Делать бордюр, я когда-то так делал
1. Если развернуть экран так, чтобы количество знакомест само пересчитывалось, то все равно придется рассматривать под микроскопом =)
Их конечно можно увеличить, это просто нужно чтобы занять все окно. dwarf fortress так делает - я увеличиваю окно, игра увеличивает игровую область.
Вот классический размер:
Скрытый текст: ПОКАЗАТЬ
Изображение
А вот, растянутое на весь мой экран (1600х900):
Скрытый текст: ПОКАЗАТЬ
Изображение
И там как раз бордюр дорисовывается, вот я его отметил красным (снизу и справа черная линия возле границы окна):
Скрытый текст: ПОКАЗАТЬ
Изображение
То есть в разных размерах разный бордюр, а в некоторых его вообще нет (при классическом консольном бордюра нет).

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

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

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

Сообщение karagy » 24 дек 2013, 08:14

warchief писал(а):Вот такую вещь я хочу и у себя
/Пристально смотрит/ готовый виджет захотел?

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

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

Сообщение Apromix » 24 дек 2013, 08:42

Мельком проглядел тему и показалось, что warchief новый dwarf fortress делает :D :D :D

Ответить

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

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