用光標移動函數gotoxy()實現方向鍵進行選擇


coolblog(個人博客):http://blog.coolight.cool/

0.效果圖

 

可以用方向鍵進行選擇,看起來高級點而且可以防止亂輸入。

 

1.引入:

這是我以前經常寫的選擇:

 

相信這應該也是很多人在控制台的時候會用的吧,

的確這個簡單容易寫。

 

但!是!

人要有理想,控制台也是

 

所以我開始想把它寫成一般游戲那種上下選擇的樣子:

所以就有了這篇博文

 

 

//------接下來讓我們進入正題-----

 

2.思路

 

注意:內容代碼中使用了我自定義的命名空間coolfun

如果必要請自行修改

 

我們首先把這部分cout出來

 

然后我們需要使用到控制台里的光標移動函數gotoxy()(頭文件:<windows.h>)

 

圖中最后的白塊就是光標,光標在哪,cout輸出的東西就會從那開始。(應該都是懂的吧...)

 

注意:gotoxy()並不是c++標准庫里的,windows.h里其實也沒有這玩意,

所以我們需要借助<windows.h>來自己“寫一個”。

void light_gotoxy(int x, int y)
{
    COORD pos = { (short)x,(short)y };
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOut, pos);
}

這里的X是橫坐標,Y 是縱坐標。

 

借助gotoxy()移動到選項前的空白

 

怎么移動呢??

 

直接看圖里,選項“自動模式”是在第3行,我們編程老傳統默認0為開始,所以這應該認為是第2行。

 

gotoxy(0,2);//把光標移動到第2(實際第3)行第0(實際第1)個。

 

然后在這里cout << ">>>" ;

 

就會有這樣的效果:

 

看起來已經有點感覺了!

 

然后我們只需要監控鍵盤的方向鍵,控制“>>>”的上下就可以了。

 

怎么樣,聽起來是不是簡簡單單!

 

但!是!

 

僅僅依靠這一個移動光標的函數是不夠的,

 

因為在讓選擇的這部分字打印之前經常是會有一些其他的提示,

 

抑或是之前已經有了一些輸出了,例如:

 

我們是要把它搞成一個可以經常用的函數

 

不可能每次需要就自己數數在哪一行開始輸出選項並移動

 

所以我們需要一個函數來獲取在這之前光標的位置。

 

light_getxy() :  獲取當前光標所在位置

void light_getxy(int& x, int& y)
{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hConsole, &csbi);
    x = csbi.dwCursorPosition.X;
    y = csbi.dwCursorPosition.Y;
}

 

把行數+1那就是下一行,然后開始cout我們我們的選項就可以了。

 

到這解決了輸出位置的問題了

 

具體的讓我們來看看實現代碼吧。

 

這就是我們這篇博文的主體函數:

 

    template<typename T>
    int switch_case(int case_num, const T* content_str)
    {
        int nowi = 1, nowx, nowy;//nowi 記錄選擇的選項編號
        coolfun::light_getxy(nowx, nowy);
        cout << "<--請用上下鍵選擇 | Enter鍵確定-->";
        coolfun::light_gotoxy(nowx, nowy + 1);
        const T* p = content_str;
        cout << ">>> " << *(p++);
        for (int i = 2; i <= case_num; ++i)  //先打印選項內容
        {
            coolfun::light_gotoxy(nowx, nowy + i);
            cout << "    " << *(p++);
        }
        coolfun::light_gotoxy(nowx, nowy + case_num + 1);
        coolfun::kbhit_remove();//清除殘留按鍵
        for (int getnum;;) //等待按鍵按下,getnum記錄按鍵asc
        {
            getnum = coolfun::kbhit_wait_getasc();
            coolfun::light_gotoxy(nowx, nowy + nowi); //移動到原來的編號選項前
            cout << "   ";  //覆蓋掉它的">>>"
            switch (getnum) //獲取按下的按鍵的ask2值
            {
            case 72: //
            {
                if (nowi > 1)   //在第一個再按上鍵會到最后一個
                    --nowi;
                else
                    nowi = case_num;
            }break;
            case 80: //
            {
                if (nowi < case_num)  //在最后一個按下鍵會到第一個
                    ++nowi;
                else
                    nowi = 1;
            }break;
            case 13:  //Enter鍵確定
            {
                coolfun::light_gotoxy(nowx, nowy + nowi);
                cout << "-->";
                coolfun::light_gotoxy(nowx, nowy + case_num + 1);
                return nowi;  //返回當前選項編號
            }break;
            }
            coolfun::light_gotoxy(nowx, nowy + nowi);//移動到修改后的位置
            cout << ">>>";
            coolfun::light_gotoxy(nowx, nowy + case_num + 1); //把光標移動回輸出的最后
        }
        return 0;
    }

 

其中還用到兩個按鍵相關的函數

頭文件:<conio.h>

//清空按鍵緩沖區,防止之前的殘留按鍵行為的影響
void coolfun::kbhit_remove()
{
    while (_kbhit())
        _getch();
}


//等待按鍵並讀取按下按鍵的 ASC碼
int coolfun::kbhit_wait_getasc()
{
    int num;
    do
    {
        num = _getch();
    } while (_kbhit());
    return num;
}

 

 

最后讓我們來找給簡單的例子測試一下。

 

3.測試代碼 

 

使用時只需要把coolfun整個復制過去並加上頭文件<windows.h>和<conio.h>

就可以調用switch_case()實現了

#include<conio.h>
#include<windows.h>


#include<iostream>

using namespace std;

namespace coolfun
{
    //清空按鍵緩沖區,防止之前的殘留按鍵行為的影響
    void kbhit_remove()
    {
        while (_kbhit())
            _getch();
    }

    //等待按鍵並讀取按下按鍵的 ASC碼
    int kbhit_wait_getasc()
    {
        int num;
        do
        {
            num = _getch();
        } while (_kbhit());
        return num;
    }
/*光標移動函數
* x為橫坐標,y為縱坐標
*/
    inline void light_gotoxy(int x, int y)
    {
        COORD pos = { (short)x,(short)y };
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorPosition(hOut, pos);
    }

    /*獲取光標位置
    * 同時獲取xy,需要傳入接受的xy變量引用
    * x為橫坐標,y為縱坐標
    */
    inline void light_getxy(int& x, int& y)
    {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        GetConsoleScreenBufferInfo(hConsole, &csbi);
        x = csbi.dwCursorPosition.X;
        y = csbi.dwCursorPosition.Y;
    }

    /*選擇函數
* 返回選擇的content_str[]的位置,默認1開始
* case_num:contentstr的數組大小;contentstr[]:選項內容
* >支持偏移<
*/
    template<typename T>
    int switch_case(int case_num, const T* content_str)
    {
        int nowi = 1, nowx, nowy;//nowi 記錄選擇的選項編號
        coolfun::light_getxy(nowx, nowy);
        cout << "<--請用上下鍵選擇 | Enter鍵確定-->";
        coolfun::light_gotoxy(nowx, nowy + 1);
        const T* p = content_str;
        cout << ">>> " << *(p++);
        for (int i = 2; i <= case_num; ++i)  //先打印選項內容
        {
            coolfun::light_gotoxy(nowx, nowy + i);
            cout << "    " << *(p++);
        }
        coolfun::light_gotoxy(nowx, nowy + case_num + 1);
        coolfun::kbhit_remove();//清除殘留按鍵
        for (int getnum;;) //等待按鍵按下,getnum記錄按鍵asc
        {
            getnum = coolfun::kbhit_wait_getasc();
            coolfun::light_gotoxy(nowx, nowy + nowi); //移動到原來的編號選項前
            cout << "   ";  //覆蓋掉它的">>>"
            switch (getnum) //獲取按下的按鍵的ask2值
            {
            case 72: //
            {
                if (nowi > 1)   //在第一個再按上鍵會到最后一個
                    --nowi;
                else
                    nowi = case_num;
            }break;
            case 80: //
            {
                if (nowi < case_num)  //在最后一個按下鍵會到第一個
                    ++nowi;
                else
                    nowi = 1;
            }break;
            case 13:  //Enter鍵確定
            {
                coolfun::light_gotoxy(nowx, nowy + nowi);
                cout << "-->";
                coolfun::light_gotoxy(nowx, nowy + case_num + 1);
                return nowi;  //返回當前選項編號
            }break;
            }
            coolfun::light_gotoxy(nowx, nowy + nowi);//移動到修改后的位置
            cout << ">>>";
            coolfun::light_gotoxy(nowx, nowy + case_num + 1); //把光標移動回輸出的最后
        }
        return 0;
    }

}


int main()
{
    cout << "歪比巴布占用了這一行" << endl;
    cout << "瑪卡巴卡占用了這一行" << endl;
    cout << "反沖斗士芭芭拉也需要一行" << endl;
    string str[] =
    {
       "1.自動模式",
       "2.手動模式",
    };

    cout << "<< 您希望以什么模式開始游戲?" << endl;

    cout << "\n<< 您選擇了選項" << coolfun::switch_case(2, str) << endl;

    return 0;
}

 

 

運行結果:

 

 

 

 

4.coolfun

 

coolfun:http://blog.coolight.cool/?p=313

 

利用獲取光標位置和移動光標的函數還能有其他一些不錯的功能,

例如一些顏文字的打印動畫,實現進度條等等,我們以后再聊聊。

 

coolight大字打印動畫:

 

進度條:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM