NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
Avatar
Jan Kotěhulka:3.4.2017 15:43

Zdravím :)

Mám takový problém - máme například smyčku, která slouží k pohybu objektu po konzoli. A aby byl pohyb vidět, je ve smyčce sleep např. na 100 ms.

Ovšem, pokud klávesu držíme, tak asi naplníme buffer a dané příkazy se vykonávají i když klávesu pustíme.
Zkoušel jsem vyprázdnit buffer pomocí toho krátkého kódu, který fungoval ale pohyb rozbil zase jinak.

while (Console.KeyAvailable)
    Console.ReadKey(true);

Příklad kde kód dělá problémy:

while (true)
{
    System.Threading.Thread.Sleep(100);

    switch (Console.ReadKey(true).Key)
    {
        case ConsoleKey.RightArrow:
            Console.WriteLine("Zmacknul jsi doprava");
            break;
        case ConsoleKey.LeftArrow:
            Console.WriteLine("Zmacknul jsi doleva");
            break;
        case ConsoleKey.UpArrow:
            Console.WriteLine("Zmacknul jsi nahoru");
            break;
        case ConsoleKey.DownArrow:
            Console.WriteLine("Zmacknul jsi dolu");
            break;
    }
}

Díky :)

 
Odpovědět
3.4.2017 15:43
Avatar
Petr Šťastný
Tvůrce
Avatar
Odpovídá na Jan Kotěhulka
Petr Šťastný:3.4.2017 16:49

Kam jsi vlozil ten cyklus na vymazani bufferu? Zkus ho vlozit tesne pod ten switch blok. Melo by to prvni klavesu precist a zbytek zahodit.

 
Nahoru Odpovědět
3.4.2017 16:49
Avatar
Jan Kotěhulka:3.4.2017 17:08

Ano, ovšem toto řešení není úplně ideální. Když poté držím klávesu a zmáčknu druhou zatímco první ještě držím, tak tu nastane minimálně 0,5 s pomlka než se začne provádět příkaz jaký má.

Proto hledám nějaké lepší řešení (jestli teda existuje).

 
Nahoru Odpovědět
3.4.2017 17:08
Avatar
termostat
Člen
Avatar
termostat:5.4.2017 12:58

Ta pomlka je způsobena běžným Keyboard Repeat Delay http://www.dummies.com/…repeat-rate/
Bohužel v konzolové aplikaci neexistuje rozumný způsob jak to obejít.
Musel bys to udělat alespoň ve WinForms, kde pak můžeš pomocí Timeru zjišťovat stav klávesy.

 
Nahoru Odpovědět
5.4.2017 12:58
Avatar
Odpovídá na termostat
Jan Kotěhulka:5.4.2017 19:26

Díky za odpověď, ano máš pravdu. Ale možná by šlo tento problém vyřešit jinak.

Když to úplně zobecním, tak problém nastává tehdy, když je vlákno ve sleepu, tak pořád zaznamenává stisk kláves. Přemýšlel jsem jestli třeba nejde to vlákno plně zamčít na určitou dobu (Takový lepší sleep).

Console.Write("Wait");
System.Threading.Thread.Sleep(5000);
Console.Read();

Pokud zde v tech 5s budeme busit do klavesnice, tak se stejne klavesy budou zaznamenavat a vypisi se po sleepu.

 
Nahoru Odpovědět
5.4.2017 19:26
Avatar
termostat
Člen
Avatar
termostat:5.4.2017 21:23

Tomu repeat delay se v klasické konzoli nevyhneš. Ještě to můžeš obejít přes platform invoke třeba takto :) :

class Program
{
        private const int KEY_PRESSED = 0x8000;

        [DllImport("USER32.dll")]
        static extern short GetKeyState(ConsoleKey key);

        private static bool IsKeyDown(ConsoleKey key)
        {
                return (GetKeyState(key) & KEY_PRESSED) > 0;
        }

        static void Main(string[] args)
        {
                int x = Console.WindowWidth / 2;
                int y = Console.WindowHeight / 2;

                while (true)
                {
                        System.Threading.Thread.Sleep(100);

                        if (IsKeyDown(ConsoleKey.LeftArrow))
                                x = Math.Max(0, x - 1);
                        if (IsKeyDown(ConsoleKey.RightArrow))
                                x = Math.Min(Console.WindowWidth - 1, x + 1);
                        if (IsKeyDown(ConsoleKey.UpArrow))
                                y = Math.Max(0, y - 1);
                        if (IsKeyDown(ConsoleKey.DownArrow))
                                y = Math.Min(Console.WindowHeight - 1, y + 1);

                        Console.SetCursorPosition(x, y);
                        Console.Write('*');
                }
        }
}
Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
5.4.2017 21:23
Avatar
Odpovídá na termostat
Jan Kotěhulka:6.4.2017 17:08

Tvoje řešení mi strašně pomohlo, díky moc! Funguje jak má!
Díky

PS. Trošku tam nechápu ten bitovej AND u

return (GetKeyState(key) & KEY_PRESSED) > 0;

ale to snad přeziju :)

 
Nahoru Odpovědět
6.4.2017 17:08
Avatar
termostat
Člen
Avatar
termostat:6.4.2017 19:58

To jsem rád. Jde o to, že výsledkem WinAPI funkce GetKeyState je hodnota typu short.
Nás zajímá jen jestli je klávesa zmáčknutá a podle dokumentace ( https://msdn.microsoft.com/…=vs.85).aspx ) to poznáme tak, že nejvyšší bit vrácené hodnoty je nastaven na 1.
Zavoláním "GetKeyState(key) & 0x8000" si vymaskujeme tento nejvyšší bit v shortu a zjistíme jestli je nastaven. 0x8000 totiž odpovídá bitům 1000000000000000.
Když zkusíš něco podobného jako toto, zjistíš jak vypadají bity ve vráceném shortu při stisku levé šipky:

while (true)
{
        Console.SetCursorPosition(0, 0);
        Console.WriteLine(Convert.ToString(GetKeyState(ConsoleKey.LeftArrow), 2).PadLeft(16, '0'));
}

Děláš dobře že to chceš pochopit a ne jen zkopírovat :)

 
Nahoru Odpovědět
6.4.2017 19:58
Avatar
Odpovídá na termostat
Jan Kotěhulka:6.4.2017 20:45

Jasný, chápu. Za toto vysvětlení ještě větší dík než za ten zdroják, protože to vysvětlení dá člověku víc. :)

Je zajímavý jak to funguje, a je dobré vědět, že ten poslední bit může sloužit (on slouží) jako přepínač.

Díky :)

Editováno 6.4.2017 20:46
 
Nahoru Odpovědět
6.4.2017 20:45
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 9 zpráv z 9.