- xutongwei 的博客
C++游戏指北(Windows)
- 2022-1-21 23:04:11 @
从洛谷账号 xutongwei 的博客文章 C++游戏指北(Windows) 复制。
: 最近更新
注:本文章适用于 C++ 语言,Windows 系统,虽然有些地方是通用的。
0. 前言
Q:做 C++ 游戏需要哪些基础?
A:会基本 C++ 语法。是不是很低。
Q:这是游戏教程吗?
A:不是。这只是当你想弄一些操作却不会时,提供一些帮助。
1. 目录
-
前言
-
目录
-
常用函数
-
文件操作(通用)
-
普通操作
-
复杂操作
-
-
输出优化
-
彩色输出
-
指定位置输出
-
闪烁优化(重点,通用)
-
附加隐藏光标
-
-
键盘操作
-
获取键盘按下之 getch(半重点,通用)
-
获取键盘按下之 KEY_DOWN(待更)
-
模拟键盘按下
-
-
鼠标操作(咕咕咕)
-
后置芝士
2. 常用函数
goto p1;
跳转到指定代码
#include <windows.h>
中:
Sleep(1000);
休息
system("cls");
清屏
system("pause");
按任意键继续
system("mode con lines=10 cols=20");
设定控制台大小
system("title 小游戏");
设定标题
system("start 小游戏.exe");
打开文件
如果还有疑问……
system("help");
查看帮助
3. 文件操作
1. 普通操作
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
freopen("CON", "r", stdin);
freopen("CON", "w", stdout);
不细讲了,肯定都会,不会的可以自行百度。
但是有一个BUG:用 freopen("CON", "w", stdout);
重新控制台输出之后,cout << endl;
和 cout << "\n";
都会出现问题,所以建议文件输出之后直接退出游戏(保存并退出),或者学习下一种。
优点:方便。
缺点:切换回控制台时会有输出问题。
2. 复杂操作
需要头文件 #include <fstream>
首先先定义两个输入输出指针:
ifstream fin;
ofstream fout;
其中 fin
与 fout
是作者习惯的名字,可以自己改。
然后打开文件:
fin.open("1.in");
输入:
fin >> n;
最后一定要记得加上关闭:
fin.close();
fin.clear();
就可以实现自由输入了。
输出也一样:
fout.open("1.out");
fout << n;
fout.close();
fout.clear();
现在就可以随便文件输入输出,不会与控制台混在一起啦!
具体例子就不举了。
据说 #include <fstream>
还有很多其他文件操作哦,请自行百度!
优点:功能强大
缺点:麻烦
4. 输出优化
1. 彩色输出
system("color 1a");
需要头文件 #include <windows.h>
其中 1a 表示背景颜色为 1(蓝色),字体颜色为 a(绿色),详细数字与颜色的对应表可输入 system("color /?");
查看。
优点:方便
缺点:只能实现全屏变色。
color(15);
需要头文件 #include <windows.h>
需要函数:
void color(int a){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a);
}
在输出前加上一句 color(15);
,接下来输出的字体就是 15 的颜色(白色)。
这里要将颜色 a ~ f 变为十进制数,背景颜色要乘 16,如 color(1 * 16 + 10);
就是背景颜色 1,字体颜色 a。
优点:可以实现对每个字符的颜色控制
2. 指定位置输出
block(10, 5);
需要函数:
void block(int x,int y){
HANDLE hCon;
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
COORD setps;
setps.X = x;
setps.Y = y;
SetConsoleCursorPosition(hCon,setps);
}
SetPos(10, 5);
需要函数:
void SetPos(COORD a){
HANDLE out=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(out, a);
}
void SetPos(int i, int j){
COORD pos={i, j};
SetPos(pos);
}
gotoxy(10, 5);
需要函数:
void gotoxy(int x, int y)
{
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
HANDLE hConsoleOut;
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
csbiInfo.dwCursorPosition.X = x;
csbiInfo.dwCursorPosition.Y = y;
SetConsoleCursorPosition(hConsoleOut, csbiInfo.dwCursorPosition);
return;
}
我才不会告诉你以上三个函数是同一个用法
在输出前加上 block(10, 5);
或 SetPos(10, 5);
或 gotoxy(10, 5);
,输入输出的光标会移动到第 5 行第 10 列,这样就可以指定位置输出了。
注:参数中前一个是列,后一个是行,不要弄混了!当然你自己换过来也可以
3. 闪烁优化
这可能是诸多游戏编写者的大难题,因为这个问题不仅需要以上“指定位置输出”的知识,还需要一点思路。
我们要明确一点事情:如果在已经有字的位置上再次输出,那么原来的字会被覆盖。
其实我们可以不用 system("cls");
,在需要去掉的字上输出空格覆盖,在需要输出的位置上直接输出。
但是,控制台的输出非常之慢,所以我们要尽可能输出的少:
把当前控制台上的字符 与 预想中输出后的字符 进行比较,不一样的定位输出,一样的略过不管。
在把思路具体一点:用一个二维数组 a 存 要输出的东西,用一个二维数组 a1 存 上一次输出完的东西,两个比较一下,把不同的地方输出到指定位置,最后把 a 里的所有东西赋值给 a1,完美!
具体代码实现:
原来的代码:
void output(){
system("cls");
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << a[i][j];
}
}
}
但是发现特别闪,于是就改进成了了下面这种:
void output(){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(a1[i][j] != a[i][j]){
block(j, i);
cout << a[i][j];
a1[i][j] = a[i][j];
}
}
}
}
只是一定要记得开局把 a1 初始化,游戏过程要谨慎使用 system("cls");
,就可以愉快地玩耍编游戏了!
注:当然,这么简单的迷宫游戏还有一个更简单的防闪屏方法(仔细找找有什么不同):
虽然这个方法只适用于一些非常简单的游戏。
还是学习第一种吧。
4. 附加隐藏光标
隐藏光标:HideCursor();
需要函数:
void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
只用在开头加一句 HideCursor();
即可。
5. 键盘操作
1.获取键盘按下之 getch
最简洁也常用的方法:getch()
需要头文件:#include <conio.h>
食用方法:把getch()
看作一个字符就可以了。
比如:
char z1 = getch();
就是把你按下的那个键的值赋值给 z1
更准确地说,这是不用显示的读入字符。
所以缺点很明显:只有等你输入了之后程序才会继续运行,并且如果之前多按了,就直接读入之前的值了。
还有一些特殊的键,比如方向键,用这就会更麻烦一些。
所以 getch()
主要是用于字母与数字。
另外,只打 getch();
是按任意键以继续,只是如果之前多按了,就会直接跳过。所以还是建议用 system("pause");
优点:方便,当没有输入时会等待。
缺点:如果前面多按了,就会直接读取前面的值。
当然啦,这些缺点是可以消除的。
- 补充:
kbhit()
需要头文件:#include <conio.h>
与如果读取到按键,那么返回 true ,否则返回 false。
常与 getch()
配合食用:
while(!kbhit()){
//没有按键时……
}
char z1 = getch();
//按键之后……
这样,没有按键时也可以进行操作了。
- 补充:方向键,
isascii()
读入时,方向键会被识别成两个字符,所以只要读入两次就行。
char z1 = getch();
z1 = getch();
if(z1 == 'H'){
//按上时……
}
else if(z1 == 'P'){
//按下时……
}
else if(z1 == 'K'){
//按左时……
}
else if(z1 == 'M'){
//按右时……
}
如果要方向键与数字字母等一起用,可以直接把方向键看做 HPKM 使用,这样按一下方向键相当于一个键按错了。
不过还有一种方法,就是食用isascii(z1)
。
如果是个 ASCII 码里的字符,返回 true ,否则是 false 。
这样可以粗略判断:如果是 true ,那就是字母数字,或方向键后一位;如果是 false ,那就是方向键前一位,下一位一定是方向键。这样就可以区分了。
- 补充:清空缓存
如果之前按多了怎么办?很简单,把之前多按的全部读空。
while(kbhit()){getch();}
char z1 = getch();
当然,缺点总是无法避免。前面说过,这可以理解为输入,所以只有在为“当前窗口”时才能使用。
优点:方便吗,适用于各种系统
缺点:必须为当前窗口
2.获取键盘按下之 KEY_DOWN
KEY_DOWN(VK_UP);
或 KEY_DOWN('a');
需要宏定义:
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
#define KEY_DOWM(vk_c) (GetAsyncKeyState(vk_c)&0x8000?1:0)
其中第一行是识别所有键,第二行是更方便识别有对应 ASCII 码的键,建议都带上。
如果按下键返回 true ,否则为 false。
这个与 getch();
就不同了,它是直接识别键有没有按下,就算不是“当前窗口”也可以照样工作。这就特别适合某些见不得人的程序。
(留坑)
3. 模拟键盘按下
keybd_event(65,0,0,0);//按下
keybd_event(65,0,KEYEVENTF_KEYUP,0);//释放
需要头文件:#include <windows.h>
不常用,就不多说了。这是游戏不是病毒啊
6. 鼠标操作(咕咕咕)
其实作者也不会,不如直接看链接吧
7. 后置芝士
其实控制台能实现的功能也不多。
如果真想编点好游戏……