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大字打印动画:
进度条: