C++游戏指北(Windows)

xutongwei

2020-09-06 11:11:32

Algo. & Theory

Uptade$: 最近更新 $2021.9.14

注:本文章适用于 C++ 语言,Windows 系统,虽然有些地方是通用的。

咕咕咕

0. 前言

Q:做 C++ 游戏需要哪些基础?

A:会基本 C++ 语法。是不是很低。

Q:这是游戏教程吗?

A:不是。这只是当你想弄一些操作却不会时,提供一些帮助。

1. 目录

  1. 前言

  2. 目录

  3. 常用函数

  4. 文件操作(通用)

    1. 普通操作

    2. 复杂操作

  5. 输出优化

    1. 彩色输出

    2. 指定位置输出

    3. 闪烁优化(重点,通用)

    4. 附加隐藏光标

  6. 键盘操作

    1. 获取键盘按下之 getch(半重点,通用)

    2. 获取键盘按下之 KEY_DOWN(待更)

    3. 模拟键盘按下

  7. 鼠标操作(咕咕咕)

  8. 后置芝士

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;

其中 finfout 是作者习惯的名字,可以自己改。

然后打开文件:

fin.open("1.in");

输入:

fin >> n;

最后一定要记得加上关闭:

fin.close();

fin.clear();

就可以实现自由输入了。

输出也一样:

fout.open("1.out");

fout << n;

fout.close();

fout.clear();

现在就可以随便文件输入输出,不会与控制台混在一起啦!

具体例子就不举了。

据说 #include <fstream> 还有很多其他文件操作哦,请自行百度!

优点:功能强大

缺点:麻烦

4. 输出优化

1. 彩色输出

  1. system("color 1a");

需要头文件 #include <windows.h>

其中 1a 表示背景颜色为 1(蓝色),字体颜色为 a(绿色),详细数字与颜色的对应表可输入 system("color /?"); 查看。

优点:方便

缺点:只能实现全屏变色。

  1. 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. 指定位置输出

  1. 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);  
}
  1. 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);
}
  1. 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. 闪烁优化

\large\texttt{划 重 点}

这可能是诸多游戏编写者的大难题,因为这个问题不仅需要以上“指定位置输出”的知识,还需要一点思路。

我们要明确一点事情:如果在已经有字的位置上再次输出,那么原来的字会被覆盖。

其实我们可以不用 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"); ,就可以愉快地玩耍编游戏了!

示范代码1

注:当然,这么简单的迷宫游戏还有一个更简单的防闪屏方法(仔细找找有什么不同):

示范代码2

虽然这个方法只适用于一些非常简单的游戏。

还是学习第一种吧。

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");

优点:方便,当没有输入时会等待。

缺点:如果前面多按了,就会直接读取前面的值。

当然啦,这些缺点是可以消除的。

  1. 补充:kbhit()

需要头文件:#include <conio.h>

与如果读取到按键,那么返回 true ,否则返回 false。

常与 getch() 配合食用:

while(!kbhit()){
    //没有按键时……
}
char z1 = getch();
//按键之后……

这样,没有按键时也可以进行操作了。

  1. 补充:方向键,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 ,那就是方向键前一位,下一位一定是方向键。这样就可以区分了。

  1. 补充:清空缓存

如果之前按多了怎么办?很简单,把之前多按的全部读空。

while(kbhit()){getch();}
char z1 = getch();

示范代码3

当然,缺点总是无法避免。前面说过,这可以理解为输入,所以只有在为“当前窗口”时才能使用。

优点:方便,适用于各种系统

缺点:必须为当前窗口

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. 后置芝士

模拟鼠标操作

模拟键盘按下

键盘操作

完整键位码(百度百科)

其实控制台能实现的功能也不多。

如果真想编点好游戏……

Qt教程

EGE(含教程)

(还没完结先不撒花)