Skip to main content

游戏教程

Summary

从零做一个终端迷宫小游戏的教程。目标是 10×10 的迷宫,玩家用键盘控制角色走到终点。先给出主循环骨架 init、print、check、move 四个函数,再依次实现:用二维数组存地图并约定 0 为空地、1 为墙,清屏后逐行打印;用坐标存玩家位置、设初始出生点、判断是否到达终点,并用 getch 读键无回车地移动。后半部分逐一讲解常用工具:system 命令、gotoxy 与隐藏光标等 Windows API、按键检测宏,以及 system、Windows API 与 ANSI 真彩色三种终端上色方式。

参考资料​

说明​

本教程将演示如何制作一个简单的终端迷宫游戏:

  • 迷宫地图为 10×1010\times 10,包含若干障碍物。
  • 玩家可以通过键盘控制角色到达终点。

流程​

代码的大致流程如下:

55 Bcpp
init();
print();
while(!check())
{
move();
print();
}
  • init():初始化
  • print():打印地图
  • check():判断结束
  • move():玩家移动

地图​

创建地图​

定义一个常量 N=10N=10,表示迷宫地图的大小。

再定义一个 N×NN\times N 的数组 aa 存储地图。

28 Bcpp
const int N=10;
int a[N][N];

此时我们可以随意编写地图,并赋予数字意义:

  • 00:空地(可以经过)。
  • 11:墙壁(无法经过)。
256 Bcpp
int a[N][N]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,1},
{1,1,1,0,1,0,1,1,1,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,1,1,1,1,0,1},
{1,0,1,0,0,0,0,0,0,1},
{1,0,1,0,1,0,1,1,0,1},
{1,0,0,0,1,0,1,1,0,1},
{1,0,1,0,1,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};

打印地图(print())​

打印地图,先清空屏幕,再逐行输出数组即可。

133 Bcpp
void print()
{
system("cls");
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}

玩家​

创建玩家​

定义两个变量 xx 和 yy,表示玩家当前的坐标为 (x,y)(x,y)。

8 Bcpp
int x,y;

初始化(init())​

将玩家位置设置为出生点 (1,1)(1,1)。

23 Bcpp
void init()
{
x=y=1;
}

事件​

判断结束​

返回玩家位置是否在目标点 (8,8)(8,8)。

36 Bcpp
bool check()
{
return x==8&&y==8;
}

玩家移动​

将 x,yx,y 备份为 x1,y1x_1,y_1。

然后使用 getch() 函数读取一个字符。

30 Bcpp
int x1=x,y1=y;
char f=getch();
tip

#include <conio.h> 头文件中的 getch() 函数相比直接输入,不需要按回车键,可以减少玩家操作复杂度。

分别判断 ff 是否为 w、a、s 和 d。

如果是某个字符,将 x1x_1 或 y1y_1 移动 11。

判断 ax1,y1a_{x_1,y_1} 是否等于 00。

如果是,就让 x,yx,y 等于 x1,y1x_1,y_1。

完整代码​

[TODO]

工具函数​

system 函数​

system() 函数是 C++ 标准库中的一个函数,用于执行系统命令。

调用操作系统的命令行或 shell 来执行指定的命令。

system("title XXX")​

  • 功能:修改终端窗口标题为 XXX。
21 Bcpp
system("title Game");

system("mode con cols=XX lines=YY")​

  • 功能:修改终端窗口大小为 YY 行 XX 列。
36 Bcpp
system("mode con cols=60 lines=30");

system("cls")​

  • 功能:清空整个终端。
14 Bcpp
system("cls");

Windows API​

Windows API 是微软提供的一套用于与 Windows 操作系统进行交互的编程接口,允许开发者通过 C++ 语言访问和操作操作系统的底层功能,如窗口管理、文件操作、网络通信等。

gotoxy(short x,short y)​

功能:设置控制台光标的位置为 y 行 x 列。

98 Bcpp
void gotoxy(short x,short y)
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),{x,y});
}

hideCursor()​

  • 功能:隐藏控制台中的光标。
130 Bcpp
void hideCursor()
{
CONSOLE_CURSOR_INFO cursor_info={1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

disableQuickEditMode()​

  • 功能:禁用快速编辑模式。
264 Bcpp
void disableQuickEditMode()
{
HANDLE hInput=GetStdHandle(STD_INPUT_HANDLE);
DWORD prevMode;
GetConsoleMode(hInput,&prevMode);
prevMode&=~ENABLE_QUICK_EDIT_MODE;
prevMode&=~ENABLE_INSERT_MODE;
prevMode&=~ENABLE_MOUSE_INPUT;
SetConsoleMode(hInput,prevMode);
}

windowPos()​

  • 功能:获取鼠标指针在控制台的相对位置。
105 Bcpp
POINT windowPos()
{
POINT pt;
GetCursorPos(&pt);
ScreenToClient(GetConsoleWindow(),&pt);
return pt;
}

moveConsoleWindow(int x,int y)​

功能:将控制台窗口移动 x 和 y 个单位。

240 Bcpp
void moveConsoleWindow(int x,int y)
{
HWND consoleWindow=GetConsoleWindow();
RECT rect;
GetWindowRect(consoleWindow,&rect);
int w=rect.right-rect.left,h=rect.bottom-rect.top;
MoveWindow(consoleWindow,rect.left+x,rect.top+y,w,h,TRUE);
}

centerConsoleWindow()​

  • 功能:将控制台窗口居中显示在屏幕上。
298 Bcpp
void centerConsoleWindow()
{
HWND consoleWindow=GetConsoleWindow();
RECT rect;
GetWindowRect(consoleWindow,&rect);
int w=rect.right-rect.left,h=rect.bottom-rect.top;
int x=GetSystemMetrics(SM_CXSCREEN)-w>>1;
int y=GetSystemMetrics(SM_CYSCREEN)-h>>1;
MoveWindow(consoleWindow,x,y,w,h,TRUE);
}

按键检测​

KEY_DOWN(VK_NONAME)​

  • 功能:检测某个按键是否被按下。
70 Bcpp
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME)&0x8000)?1:0)

颜色​

终端不只有黑白显示,还可以支持多种颜色。

system 函数​

通过 system("color XY"),可以设置终端文本的背景色和前景色。

其中,X 表示背景色,Y 表示前景色,它们都是十六进制数字(0 – 15),每个数字对应一种颜色。

数字颜色数值数字颜色数值
00黑色000000\textcolor{000000}{000000}88灰色808080\textcolor{808080}{808080}
11蓝色000080\textcolor{000080}{000080}99亮蓝色0000ff\textcolor{0000ff}{0000ff}
22绿色008000\textcolor{008000}{008000}1010亮绿色00ff00\textcolor{00ff00}{00ff00}
33青色008080\textcolor{008080}{008080}1111亮青色00ffff\textcolor{00ffff}{00ffff}
44红色800000\textcolor{800000}{800000}1212亮红色ff0000\textcolor{ff0000}{ff0000}
55紫色800080\textcolor{800080}{800080}1313亮紫色ff00ff\textcolor{ff00ff}{ff00ff}
66黄色808000\textcolor{808000}{808000}1414亮黄色ffff00\textcolor{ffff00}{ffff00}
77白色c0c0c0\textcolor{c0c0c0}{c0c0c0}1515亮白色ffffff\textcolor{ffffff}{ffffff}

system 函数的缺点是只能修改整个终端的颜色,无法仅修改特定部分的文字颜色。

Windows API​

如果你需要只修改某一段文字的颜色,可以使用 Windows API 来实现。

定义一个 color 函数,传入两个参数 a 和 b,表示背景色和前景色的颜色编号,对应的颜色与 system 函数相同。

97 Bcpp
void color(short a,short b)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a<<4|b);
}

Windows API 的缺点是颜色种类有限(只有 16 种)。

ANSI 转义序列​

ANSI 转义序列支持 24 位真彩色,也就是常见的 RGB 颜色格式。

但此功能默认未开启,需启用虚拟终端处理。

223 Bcpp
void enableVirtualTerminalProcessing()
{
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode=0;
GetConsoleMode(hConsole,&dwMode);
dwMode|=ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole,dwMode);
}

定义两个函数 foreColor 和 backColor,传入三个参数 r、g 和 b,表示 RGB 颜色值。

151 Bcpp
void foreColor(int r,int g,int b)
{
printf("\033[38;2;%d;%d;%dm",r,g,b);
}
void backColor(int r,int g,int b)
{
printf("\033[48;2;%d;%d;%dm",r,g,b);
}