热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

使用C++写控制台游戏/基础篇

此文本来想投洛谷日报结果被毙掉了,本文仅有纪念意义注:以下所有代码均以Windows下Dev-C++5.11运行为准,P党请跳过。用Dev-C++运行本文代码时,需要包含以下头文件

此文本来想投洛谷日报结果被毙掉了,本文仅有纪念意义

注:以下所有代码均以 WindowsDev-C++ 5.11 运行为准,P党请跳过。

Dev-C++ 运行本文代码时,需要包含以下头文件:

#include //众所周知的万能头
#include //使用 Windows 提供的函数
#include //使用 getch() 函数

阅读这篇文章,您只需要学完一本通中的 C++ 语言即可。



\[\large\texttt{前 言}

\]

很多 OIer 都想用 C++ 写一个游戏,但是仅仅凭 \(\texttt{NOI}\) 竞赛的内容是很难做到的,因为这需要掌握 C++ 更多函数的功能。

于是,蒟蒻写了三篇文章:基础篇、进阶篇和实践篇。这是基础篇。(后面两篇都没写好)

蒟蒻以前做过一些小游戏,那么在基础篇给出一些写游戏比较使用的语句,在以后的实践中可以经常用到。



\[\large\texttt{基础篇 —— 目录}

\]



  • \(\texttt{1}_{\texttt{st}} \to\;\) system() 函数



  • \(\texttt{2}_{\texttt{nd}} \to\;\) 光标基本操作 · 移动光标



  • \(\texttt{3}_{\texttt{rd}} \to\;\) 控制颜色



  • \(\texttt{4}_{\texttt{th}} \to\;\) 使用键盘



  • \(\texttt{5}_{\texttt{th}} \to\;\) 随机函数



  • \(\texttt{E}_{\texttt{ed}} \to\;\) 附录/后记






\(\texttt{1}\) 章 —— system() 函数

\(\large\text{1. 参数 "pause"}\)

在刚开始接触 C++ 编程时,肯定都见过这条语句:

system("pause"); //检测到任意键按下后继续

都了解,这个语句可以在屏幕上打印 请按任意键继续,接着等待你在键盘上按下一个键程序才会继续执行。这条语句当时是用于防止控制台运行完之后直接退出而看不到输出结果。

其实 system() 是一个标准函数,参数是一个字符串,效果等同于你 cmd 中输入这个字符串之后的结果。

运行结果:

\(\large\text{2. 参数 "shutdown"}\)

在你的朋友远离电脑时,你可能在 \(\texttt{TA}\) 的电脑上还用过如下语句:

system("shutdown -s -t 1145"); //1145秒之后关机

运行效果:

然后在关机前可以用如下语句取消关机:

system("shutdown -a"); //取消 windows 的注销指令

运行效果:

是的,使用参数 shutdown -s -t XX 可以让你的电脑在 XX 秒后关机,切记不要在别人的电脑上使用,否则你可能遭到物理攻击。

参数 shutdown -a 可以在关机前取消关机命令。

这就是 shutdown 指令两个常用的参数。

题外话:\(\texttt{Q: }\)这和游戏有什么关系?\(\texttt{A: }\) 可以在游戏中坑人,请不要乱用,容易被打

\(\large\text{3. 参数 "mode con"}\)

在很多 C++ 游戏制作中,需要调整窗口大小以便玩家游玩,那么怎么调整窗口的大小呢?system() 函数有一个参数是 mode con,可以做到这一点。

例如,这条语句可以把控制台窗口大小调成 \(114\times514\) 大小:

system("mode con lines=114 cols=514"); //调整窗口大小为 114*514

同时,你也可以把窗口调成 \(10\times20\) 大小:

system("mode con lines=10 cols=20"); //调整窗口大小为 10*20

要注意的是,这里的 \(10\times20\)的意义是高为 \(10\)换行符,宽为 \(20\)空格,一般换行符比空格要长一些。

两条语句的运行结果分别如下:

关于这种方式设置窗口大小,如果你设置的太大(例如 \(114514\times1919810\)),运行时会出问题,不妨自己试一试。

\(\large\text{4. 参数 "color"}\)

看这种参数的名字就知道,这种参数可以用来修改窗口的颜色(包括前景色和背景色),例如这条语句:

system("color b9"); //设置背景色为浅蓝,前景色为深蓝

运行结果如下:

那么为什么 color 后面接 b9 就是这种颜色呢?当你运行下面这条语句的时候,输出结果会告诉你答案:

system("color 1145141919810"); //不是必须这么写,color 后面的参数可以乱填,但是必须要大于两个字符

输出结果:

设置默认的控制台前景和背景颜色。
COLOR [attr]
attr 指定控制台输出的颜色属性。
颜色属性由两个十六进制数字指定 -- 第一个
对应于背景,第二个对应于前景。每个数字
可以为以下任何值:
0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色
如果没有给定任何参数,此命令会将颜色还原到 CMD.EXE 启动时
的颜色。这个值来自当前控制台
窗口、/T 命令行开关或 DefaultColor 注册表
值。
如果尝试使用相同的
前景和背景颜色来执行
COLOR 命令,COLOR 命令会将 ERRORLEVEL 设置为 1。
示例: "COLOR fc" 在亮白色上产生淡红色

这里尽管有些地方看不懂,但是中间的列表还是很清晰的,中间的列表表示了几种颜色的代号,还给了一个例子(最后一行),system() 参数中的所有字符均不区分大小写。

如果想要输出不同颜色的字怎么办呢?第三章会介绍。

\(\large\text{5. 参数 "cls"}\)

很简单,下面这条语句可以帮助我们清屏,这里不多讲:

system("cls");

\(\texttt{2}\) 章 —— 光标基本操作 · 移动光标

这里先引入一个程序事例:\(\texttt{Link}\)(同步发表于 \(\texttt{UKE}\) 神贴中)

其中有一个 gotoxy() 函数,可以帮助我们跳转光标在控制台的位置。

函数是这样的(需要自己定义):

void gotoxy(int x,int y){
COORD position;
position.X=x;
position.Y=y; //设置光标坐标
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position); //移动光标坐标
}

这个函数有点长,大家想记就记下来,不想记就把这个模板搬到您的代码中去。

可以这么用这个函数:

gotoxy(11,45); //把光标设置到坐标 (11,45) 处

注意,这个 \((11,45)\) 一格的定义和 system("mode con") 里的一样,行是换行符,列是空格。\((11,45)\) 代表的是 \((\)\(,\)\()\),是有序数对而不是数组下标,有序数对的坐标从 \(0\) 开始。

例如写如下代码:

gotoxy(0,20);
printf("(0,20):luogu");
gotoxy(20,0);
printf("(20,0):luogu");
gotoxy(20,20);
printf("(20,20):luogu");

运行效果:

这个 gotoxy() 函数还有一个特性,可以覆盖字体。

例如如下代码:

gotoxy(20,20);
printf("114514191981011451419198101145141919810");
gotoxy(30,20);
printf("LUOGU!");

运行效果:

这运行效果是怎么来的?首先在坐标 \((20,20)\) 为起始点打印 114514191981011451419198101145141919810,然后再跳转到坐标 \((30,20)\) 输出 LUOGU!,然后新输出的 LUOGU! 覆盖了原先位置上的 114514


\(\texttt{3}\) 章 —— 控制颜色

在上一章节的事例程序中还有一个函数 color(),用于控制输出颜色。

这里的 color() 函数和 system("color") 不同的是,后者是改变控制台窗口的颜色设置,而前者是改变输出字体的颜色。

函数是这样的(需要自己定义):

const int blue=0,yellow=1,red=2,green=3,purple=4,white=5;//颜色常量
void color(int c){
switch(c)
{
case red:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);break; //红色原色
case green:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);break; //绿色原色
case yellow:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);break; //黄色:红色和绿色相加
case blue:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);break; //蓝色原色
case white:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);break; //白色:三原色相加
case purple:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);break; //紫色:红色和蓝色相加
}
}

注意到,函数前面还有一行颜色常量的定义,这是为了避免 0,1,2,3,4,5 这样令人无法理解的常数出现。

可以这么使用它:

color(red); cout<<"red\n";
color(yellow); cout<<"yellow\n";
color(blue); cout<<"blue\n";
color(green); cout<<"green\n";
color(purple); cout<<"purple\n";
color(white); cout<<"white\n";

运行结果:

这里提供的 color() 函数的参数还不全面,大家可以自己尝试修改一下函数,发现更多的颜色(只要把颜色和颜色相加就可以得到新颜色)。


\(\texttt{4}\) 章 —— 使用键盘

\(\large\text{1. getch() 函数的使用}\)

游戏中常常需要人机交互,最简便的交互方式之一就是使用键盘。(当然鼠标也可以交互,鼠标的操作将在进阶篇讲述)

我们很早就学过,读取字符最快的方法就是

A了这道题 祝你们成功(滑稽

放错了,是下面这个(

char ch=getchar();

然而,这种读取方式在游戏中并不常见,毕竟,如果是完全没接触过编程的人作为玩家,TA 肯定不知道输入字符之后需要换行。

#include 中有一个函数 getch(),可以解决这个问题,用法和 getchar() 相同:

char ch=getch();

这种读取字符的方式有什么不同呢?



  • getchar() 需要换行之后才会结束读入,而 getch() 只需要按下某个键就可以结束读入。



  • getch() 函数读入字符时不会在窗口中显示你读入的字符,而 getchar() 会。



  • getch() 可以读到 Backspacetab,上下左右键等各种 getchar() 不能读到的字符。(别想了,getch() 在赛场上是不能用的)



大家可以尝试运行一下下面两个不同的语句:

while(true) cout<

while(true) cout<

会发现二者有很大的不同。

\(\large\text{2. 检测按键是否按下}\)

声明:此函数部分引用这篇博客的内容,因为我就是在这里面学到这个操作的。

在很多刺激性游戏中,总不能等待用户输入字符才做出反应吧?所以需要一个函数,检测某个按键是否被按下。

在写函数之前,需要准备一个宏:

#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)

(这个宏请写在头文件下方)

然后函数这么写:

bool check(char ch) //检测某个按键是否按下,按下返回 true,否则返回 false,参数:按键表示的字符
{
if(KEY_DOWN(ch)) return true;
else return false;
}

然后尝试运行这段代码:

while(true)
{
if(check(' ')) //检测到空格按下就输出1,否则输出 0
cout<<1;
else
cout<<0;
system("cls"); //清屏
}

这就是关于一些键盘操作的语句。


\(\texttt{5}\) 章 —— 随机函数

很多小游戏都会生成随机数(角色随机等),C++ 有一个函数可以实现:

cout<

这个函数就是 rand(),用于返回一个随机数。

这个随机数是杂乱无章的,比如说我们想要产生一个 \([2,10]\) 的随机数怎么办呢?很简单,把 rand() 模上 \(10-2+1\)\(+2\) 就行了:

cout<

也就是说,产生 \([l,r]\) 之间的随机数可以使用如下语句:

int num=rand()%(r-l+1)+l;

那么想产生负数怎么办呢?很简单,先产生一个 \([0,1]\) 之间的随机数,如果是 \(1\) 则为正,\(0\) 则为负:

int nev=rand()%2,num;
if(nev==1) num=rand();
if(nev==0) num=-rand();

想产生随机字符串怎么办呢?枚举 \(n\) 遍,每遍取二十六个字母之一即可:

for(int i=0; i{
cout<}

有时我们想产生一个特别大的数,而 rand() 只支持五位数,只要在程序开头加上这条语句即可:

srand(time(NULL)); //用时间作为随机种子值,有些编译器需要把 NULL 换成 0,

注意:对于某些编译器而言,每次产生随机数都需要 srand(time(NULL)),因为某些编译器 rand() 的值不是 \(42\) 就是 \(41\)


\(\texttt{6}\) 章 —— 附录 \(\texttt I\) / 基础篇后记

介绍如此多的操作,这里提供一个名字空间来组合它们,当你写游戏时可以直接复制它:

#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
namespace BASE {

//控制光标位置
void gotoxy(int x,int y)
{
HANDLE hout;
COORD coord;
coord.X = x;
coord.Y = y;
hout = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout, coord);
}

const int blue=0,yellow=1,red=2,green=3,purple=4,white=5;//颜色常量
void color(int c){ //设置颜色
switch(c)
{
case red:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);break;
case green:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);break;
case yellow:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN);break;
case blue:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);break;
case white:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE);break;
case purple:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_BLUE);break;
}
}

//产生一个 [l,r] 范围的随机数
int random(int l,int r)
{
srand(time(NULL));
return rand()%(r-l+1)+l;
}

//检测按键是否按下
bool check(char ch) //检测某个按键是否按下,按下返回 true,否则返回 false,参数:按键表示的字符
{
if(KEY_DOWN(ch)) return true;
else return false;
}

//清屏
void Cls()
{
system("cls");
}
};
using namespace BASE;

\(\large\text{后记}\)



  • 致谢:@Gorilla 提出改错意见。



  • 彩蛋C++ 狼人杀游戏,可能有 Bug,凑合着玩吧。



\[\huge\texttt{The end.}

\]



推荐阅读
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • windows便签快捷键_用了windows十几年,没想到竟然这么好用!隐藏的功能你知道吗?
    本文介绍了使用windows操作系统时的一些隐藏功能,包括便签快捷键、截图功能等。同时探讨了windows和macOS操作系统之间的优劣比较,以及人们对于这两个系统的不同看法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 最近学习反射机制的时候Properties.load(读取本地文件流的时候怎么也找不到加载文件后面发现IDEA的默认根目录是在它的Project工程下IDEA的文件目录和Ec ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
author-avatar
lily的思念
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有