ПрограммированиеФорумОбщее

Проверка нажатия клавиши - задержка, а нужно мгновенно (WinApi) (2 стр)

Страницы: 1 2
#15
9:53, 29 ноя 2009

>>_Goshan_
>>if (msg.message == WM_KEYDOWN) {gKeys[(BYTE)msg.wParam] = true;
>>if (msg.message == WM_KEYUP) {gKeys[(BYTE)msg.wParam] = false;

это в оконную процедуру

LRESULT CALLBACK wndProc(HWND h, UINT m, WPARAM wp, LPARAM lp)
{

    switch (m)
    {
     case WM_KEYDOWN:
            {
                 gKeys[(BYTE)wp] = true;
                 return 0;
             } 

     case WM_KEYUP:   
            {
                  gKeys[(BYTE)wp] = false;
                  return 0;
            }
     case WM_CLOSE:
            {
                PostQuitMessage(0);
                return 0;
            }
      }

   return DEfWindowProc(h,m, wp, lp);
}


void Update(double deltaTime)
{
      if (gKeys[(BYTE)'W']) Kamera.Pos.z+=.01 * deltaTime;
      if (gKeys[VK_UP]) Kamera.Pos.z+=.01 * deltaTime;
      if (gKeys[VK_DOWN]) Kamera.Pos.z-=.01 * deltaTime;
}

....

while (!bQuit)
{
  while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
      {
         /* handle or dispatch messages */
         if (msg.message == WM_QUIT)
         {
            bQuit = TRUE;
         }
         else
         {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
         }
      }

   Update(Timer->getDelta());
   Render();
}


кое где псевдо код.. но думаю идея понятна

#16
22:52, 5 дек 2009

Ок!
Но возникает другая проблема - одновременное нажатие клавиш.
допустим W и A
Но получается, что W "доминирует" и не происходит одновременного нажатия двух клавиш. А это необходимо(игра же!)

#17
3:54, 6 дек 2009

_Goshan_
на основе собственных экспериментов могу порекомендовать примерно следующее :
создаем 2 потока (thread) - один читает юзерский ввод (т.е. обрабатывает виндовс-мессаги), а второй - делает все остальное

поток (thread), который создал вин-окно и принимает от него мессаги, не делает ничего вразумительного - его ЕДИНСТВЕННАЯ задача - прочитать ввод.
то, что он читает (нажатия\отпусканя кнопок, маусмувы, прочую хрень) он тихо (и быстро) складывает в очередь - откуда потом рабочий поток и будет все это читать. естественно, эта самая очередь должна быть thread-safe. фокус невелик :)

второй поток (рабочий) тупо читает мессаги (пока есть) из этой самой очереди и как-то их обрабатывает.  а когда кончатся - (к примеру) - рисует что получилось.

при правильной организации обработки - получается примерно следующее- 99.99% процессорного времени занимает отрисовка, при этом юзерский ввод не теряется. при ФПС >= 0.8 (условно - дальше я не выдержал) сохраняется адекватность управления - то есть тормоза жестокие, но делается именно то, что имелось в виду. ввод НЕ ТЕРЯЕТСЯ. можно мастерски отбить на клавиатуре "три шаги налево, две шаги направо, пять вперд и два назад" - и оно отработает. интервал прихода сообщений от мыши (я зануда, я в свои мессаги писал еще и время) - порядка 50-60 раз в секунду. это при ФПС < 1.

основные плюсы :
- просто (без фокусов)
- работает на ура, не теряя ввода
- система сделает за нас все (переключеня шрифтов\языков\....)
- ответная часть - кроссплатформеная получается

основные минусы :
- нужно не убояться, и попробовать
- нужно придумать (или содрать) свою систему эвентов- чтобы удовлетворяла потребностям

>> вдогонку - не надо париться о том, что случилось раньше - все однозначно определяется порядком в очереди.

#18
6:48, 6 дек 2009

Lion007
>поток (thread), который создал вин-окно и принимает от него мессаги, не делает ничего вразумительного - его ЕДИНСТВЕННАЯ задача - прочитать ввод.
>то, что он читает (нажатия\отпусканя кнопок, маусмувы, прочую хрень) он тихо (и быстро) складывает в очередь - откуда потом рабочий поток и будет все это >читать. естественно, эта самая очередь должна быть thread-safe. фокус невелик :)
>второй поток (рабочий) тупо читает мессаги (пока есть) из этой самой очереди и как-то их обрабатывает. а когда кончатся - (к примеру) - рисует что получилось.
честно говоря не понял на кой там поток, который читает ввод и складирует? Можно ведь сделать в самом втором потоке (рабочем), который читает мессаги и как-то их обрабатывает при получении событий ввода складывать их в очередь.

#19
7:39, 6 дек 2009

gexogen
ну, есть у меня ощущение, что-таки понял. :)

идея примерно такая : тот, кто нам мешает - тот нам поможет. между нашей прогой и железом стоит ОСь. ну а раз стоит - пусть помогает.
так вот... задача первого потока (который читает ввод) - прочитать его МАКСИМАЛЬНО быстро(в моей персональной имплементации - к каждому евенту-мессаджу приклеивалось еще и время).
соответственно, я знаю, сколько времени у меня была нажата та или иная кнопка - и, что характерно, с достаточно высокой точностью - опять же, порядка 1/50 - 1/60 секунды.. все остальное - зависит от системы ввода в гриушке.. 

я уже не буду говорить о том, что нажать одновременно две кнопки - это нонсенс. можно нажать одну, удерживая другую... :)
а про локали... кто готов поддержать ввод на китайско\корейском? :)

#20
11:32, 6 дек 2009

Ничего не понял =)

Создал, значить я два потока:

DWORD WINAPI ThreadFunc_1(LPVOID lpParam)
{
   return 0;
}
DWORD WINAPI ThreadFunc_2(LPVOID lpParam)
{
   return 0;
}

.......

   DWORD dwThreadId_1, dwThrdParam_1 = 1;
   HANDLE hThread_1;
   hThread_1 = CreateThread(NULL,0,ThreadFunc_1,&dwThrdParam_1,0,&dwThreadId_1);

   DWORD dwThreadId_2, dwThrdParam_2 = 1;
   HANDLE hThread_2;
   hThread_2 = CreateThread(NULL,0,ThreadFunc_2,&dwThrdParam_2,0,&dwThreadId_2);
[code]

А вот что делать внутри этих функций я не знаю. Подскажите плиз!
#21
12:20, 6 дек 2009

2 потока делать совсем не не обязательно.

в посте 15 после(или перед) строки 'Render();' можно прописать:

  TKeyBoardState buttons;
  GetKeyBoardState(buttons);
  if(buttons[VK_UP]&128){
    кнопка UP нажата.
    }
  if(buttons[VK_DOWN]&128){
    кнопка DOWN нажата.
    }
#22
13:04, 6 дек 2009

_Goshan_
> Но осталась загвоздка с "зависанием" - долго нажимаю, потом отжимаю, но
> некоторое время двигается без нажатия.

Судя по симптомам у тебя принципиально неверно организована обработка клавиатуры.

Небольшой ликбез: клавиатура аппаратно вырабатывает один код нажатия непосредственно после нажатия, затем ждет некоторое время, затем начинает автоповтор символа. При этом интервал времени между двумя сигналами автоповтора существенно меньше интервала от первого нажатия до начала автоповтора. Кроме того, при автоповторе перед сигналом нажатия ОТСУТСТВУЕТ сигнал отпускания клавиши.

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

Правильно: персонаж (камера, etc.) должен двигаться не по сигналам с клавиатуры, а в соответствии с внутренним временем программы, исходя из СОСТОЯНИЯ клавиатуры. А состояние изменяется при нажатии либо отпускании: если состояние "не нажата" и приходит сигнал нажатия - переводим в "нажата", если при "нажата" приходит сигнал отпускания - переводим в "не нажата", если состояние "нажата" и приходит сигнал нажатия, ничего не меняем.

Если программируем под Windows, то сигнал - это сообщение, приходящее в WinProc, если под DOS - аппаратное прерывание, приходящее на переопределенный нами обработчик прерываний клавиатуры.

#23
13:18, 6 дек 2009

_Goshan_
> Но возникает другая проблема - одновременное нажатие клавиш.
> допустим W и A
> Но получается, что W "доминирует" и не происходит одновременного нажатия двух
> клавиш. А это необходимо(игра же!)

Нету такого.
С точки зрения программы все кнопки обрабатываются независимо и никак друг на друга е влияют. Если это не так - нужно исправлять.
Есть, правда, еще один неприятный момент - аппаратные проблемы клавиатуры.
Обычно разработчики экономят на диодах развязки, в результате чего НЕ ВСЕГДА можно распознать одновременно нажатые клавиши. Но это проблема контроллера, находящегося внутри клавиатуры - он не может распознать и посылает компьютеру НЕВЕРНУЮ информацию о нажатии. И сделать с этим ничего нельзя.
Почти.
Можно только одно - грамотно распределить команды по кнопкам, чтобы в нашей программе НЕ МОГЛО встретиться ситуаций, когда контроллер клавиатуры мог бы ошибиться.
А зависимость здесь такая: контроллер клавиатуры должен гарантированно разпознавать
- нажатие двух любых кнопок,
- нажатие любой комбинации из Alt, Ctrl, Shift + одна любая кнопка из остальных.
Правильное распознавание комбинаций, не подходящих под один из описанных выше двух вариантов, не гарантируется. Хуже того, на одном компьютере это может работать, а на другом - нет. Т.е. возможна ситуация, когда программа, исправно работает на компьютере разработчике, но не работает на компьютере пользователя.

#24
13:35, 6 дек 2009

_Goshan_
предыдущий оратор (тов. andriano) - в принципе прав. да и пред-предыдущий (тов  Try) тоже прав. отчасти.

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

#25
15:20, 6 дек 2009

Люди! Прошу у всех прощения.
Виноват я.
Неправильно я операторы перегрузил..(Kamera.Pos+=tmp2) Вот и не выходило одновременного нажатия клавиш.
Спасибо всем за дельные советы! =)

#26
10:09, 10 дек 2009

_Goshan_
GetAsyncKeyState на будущее

Прошло более 1 года
#27
16:59, 5 июля 2011

Lion007
а можно как то сделать на WinApi что бы поток для обработки пользовательского ввода никогда не терял фокус ?

#28
19:23, 5 июля 2011

_Goshan_
Если хочешь, попробуй мою обёртку над Win32 API для работы с клавиатурой. Её можно скачать тут. Работать с ней просто. Подключаешь:

#include "DKeyboard.h"

Объявляешь:

DKeyboard* key=0;

// ...

key=DKeyboard::GetInstance();

if(!key)
{
    // Плохо
}

В функцию обработки сообщений от операционной системы указываешь:

// ...

if(key)
{
    if(key->Message(_window,_message,_wParam,_lParam)) return 0;
}

// ...

В игровом цикле делаешь так:

while(isExit)
{
    // ...

    if(key) key->Update();

    // ...

    if(key)
    {
        if(key->GetHit(VK_NUMPAD1))
        {
            // Если нажали цифру 1 на дополнительной клавиатуре
        }

         if(key->GetDown(VK_LEFT))
        {
            // Если нажата стрелка влево
        }

         if(key->GetUp(VK_ESCAPE))
        {
            // Если отжали клавишу Escape
        }
    }

    // ...
}

Если нужно очистить состояние клавиш, то делаешь так:

if(key) key->Clear();

И после завершения работы:

if(key) key->Release();

Приятного программирования!

Страницы: 1 2
ПрограммированиеФорумОбщее

Тема в архиве.