【C++】做一个飞机空战小游戏(五)——getch()控制两个飞机图标移动(控制光标位置)

  [导读]本系列博文内容链接如下:

【C++】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值

【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动
【C++】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动

【C++】做一个飞机空战小游戏(四)——给游戏添加背景音乐(多线程技巧应用)

【C++】做一个飞机空战小游戏(五)——getch()控制两个飞机图标移动(控制光标位置)

【C++】做一个飞机空战小游戏(六)——给两架飞机设置不同颜色(结构体的使用技巧)

目录

一、控制光标的位置

(二)为什么要控制光标位置

1、避免控制多个图标移动相互影响

2、消除光标的闪烁现象

(二)如何控制光标的输出位置

二、控制两个飞机图标移动程序

(一)头文件control_plane.h

1、更新键盘指令枚举内容

2、增加一个飞机的结构体类型坐标

3、更新飞机坐标计算函数声明

(二)控制函数库control_plane.cpp

1、增加光标控制函数

2、更新显示飞机图标函数

3、初始化函数

4、更新获取键盘指令函数

5、更新了飞机获取指令后位置计算函数的定义

(二)最终程序

1、主函数

2、头文件control_plane.h

3、库函数control_plane.cpp

三、运行效果


飞机空战游戏,一般都是两个飞机,今天介绍一下如何用getch()通过不同的方向键组控制两个飞机移动。

一、控制光标的位置

(二)为什么要控制光标位置

1、避免控制多个图标移动相互影响

本例中本节之前的代码对飞机位置的控制是通过cout函数输出空格和换行来确定飞机图标x和y坐标,这种方式对于游戏中只有一个被控制图标是可行的,但是要控制多个图标移动就行不通了,因为输出换行和空格时会对另外的图标产生影响。

2、消除光标的闪烁现象

输出换行和空格时,光标会不断移动,所以在移动飞机图标时会有光标闪烁的情况,游戏的体验感不好。

而通过控制光标把光标直接移动到要显示飞机图标的位置,就避免了频繁输出换行和空格的操作,不会对屏幕上其他的图标产生影响,也消除了光标闪烁的情况。

(二)如何控制光标的输出位置

对光标位置的控制,本文参考了以下博文

C++移动输出端的光标代码实现_c++光标_我行我素,向往自由的博客-CSDN博客

代码如下所示,需要引入头文件windows.h,并自定义一个gotoxy(x,y)函数,x,y就是光标输出的位置。

#include<windows.h>
void gotoxy(int x, int y) {COORD pos = { x,y };HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄SetConsoleCursorPosition(hOut, pos);//两个参数分别指定哪个窗口,具体位置
}

二、控制两个飞机图标移动程序

要想实现对两架飞机的控制,需要对代码进行部分更新,更新内容包括以下几方面。

(一)头文件control_plane.h

1、更新键盘指令枚举内容

增加一组控制飞机移动的指令,更新后的内容如下:

//定义移动方向命令枚举类型 
typedef  enum {up_cmd,down_cmd,left_cmd,right_cmd,up_cmd2,down_cmd2,left_cmd2,right_cmd2} direction_cmd;

"up_cmd2,down_cmd2,left_cmd2,right_cmd2"四项为新增加的内容。

2、增加一个飞机的结构体类型坐标

extern location plocation,plocation2;			//声明第二架飞机坐标 

3、更新飞机坐标计算函数声明

因为有两架飞机,需要计算两组坐标,所以在函数声明中增加了标识飞机序号的参量int n,具体代码如下:

//计算出接收指令后的飞机坐标 
location plane_locate(int n,location plct,direction_cmd dircmd);

(二)控制函数库control_plane.cpp

1、增加光标控制函数

void gotoxy(int x, int y) {COORD pos = { x,y };HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄SetConsoleCursorPosition(hOut, pos);//两个参数分别指定哪个窗口,具体位置
}

2、更新显示飞机图标函数

有了控制光标函数之后,就可以把显示飞机图标的函数进行调整了,具体代码如下所示。

//飞机图标刷新函数 
void show_plane(location plct)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{int x,y;int i,j;	int rows=sizeof(icon_plane)/sizeof(icon_plane[0]);x=plct.x;y=plct.y;for(i=0;i<rows;i++)				//图标每行前输出x个空格 {gotoxy(x,y+i);              //将光标移动到(x,y+i)位置cout<<icon_plane[i]<<endl;  //按行输出飞机图标}
}

3、初始化函数

初始化函数中增加另外一架飞机的初始位置,具体代码如下所示。

//初始化函数 
void init(void)
{plocation.x=2*r_b/3;				//初始化飞机1图标的x坐标为屏幕横轴最大值的2/3 plocation.y=b_b;					//初始化飞机1图标的y坐标为屏幕纵轴最大值plocation2.x=r_b/3;					//初始化飞机2图标的x坐标为屏幕横轴最大值的1/3 plocation2.y=b_b;					//初始化飞机2图标的y坐标为屏幕纵轴最大值
}

4、更新获取键盘指令函数

因为两架飞机,需要有两组控制飞机移动方向的控制键组合,第一组为双码按键“↑、↓、←、→”四个键,第二组为单码键'w'、's'、'a'、'd'四个键。所以函数中增加了第二组键的返回值。

//获取键盘指令函数 
direction_cmd key(void)
{int key_value1,key_value2;			//声明两个变量,存放键值 key_value1=getch();					//先获取第一个码值	if(key_value1==224)					//如果第一个码值为224,则进行第二个码值的判断 {key_value2=getch();			//先获取第二个码值switch(key_value2){case 72:				//向上方向键 return up_cmd;case 80:				//向下方向键return down_cmd;case 75:				//向左方向键return left_cmd;case 77:				//向右方向键return right_cmd;}}else{switch(key_value1){case 119:				//向上方向键 return up_cmd2;case 115:				//向下方向键return down_cmd2;case 97:				//向左方向键return left_cmd2;case 100:				//向右方向键return right_cmd2;}}		
}

5、更新了飞机获取指令后位置计算函数的定义

参量n代表飞机的序号,不同的飞机响应不同的指令dircmd,然后再根据不同的指令分别计算两架飞机位置。

//计算获得移动指令后飞机的坐标 
location plane_locate(int n,location plct,direction_cmd dircmd)
{int x,y;x=plct.x;y=plct.y;if(n==1){switch(dircmd){case up_cmd:y--;				//字符上移一行,行值y减1if(y<t_b)			//限定y值最小值为0{y=t_b;}break;case down_cmd:y++;				//字符下移一行,行值y加1if(y>b_b)			//限定y高度 {y=b_b;}break;case left_cmd:x--;				//字符左移一列,列值x减1if(x<l_b){x=l_b;			//限定x最小值为0; }break;case right_cmd:x++;				//字符右移一列,列值x加1if(x>r_b){x=r_b;			//限定x宽度}break;}}else if(n==2){switch(dircmd){case up_cmd2:y--;				//字符上移一行,行值y减1if(y<t_b)			//限定y值最小值为0{y=t_b;}break;case down_cmd2:y++;				//字符下移一行,行值y加1if(y>b_b)			//限定y高度 {y=b_b;}break;case left_cmd2:x--;				//字符左移一列,列值x减1if(x<l_b){x=l_b;			//限定x最小值为0; }break;case right_cmd2:x++;				//字符右移一列,列值x加1if(x>r_b){x=r_b;			//限定x宽度}break;}}plct.x=x;plct.y=y;return plct;}

(二)最终程序

1、主函数

#include "control_plane.h"
using namespace std;location plocation;
location plocation2;int main(int argc, char** argv) {	init();	//初始化					 bgmusic();//播放背景音乐while(1)					//循环等待键盘指令 {system("cls");			//清屏show_plane(plocation);	//刷新飞机1图标show_plane(plocation2);	//刷新飞机2图标 		dir_cmd=key();			//获取按键指令 //计算收到键盘指令后的两架飞机坐标 plocation=plane_locate(1,plocation,dir_cmd);plocation2=plane_locate(2,plocation2,dir_cmd);	}return 0; 	
}

2、头文件control_plane.h

#ifndef CONTROL_PLANE_H
#define CONTROL_PLANE
#include <iostream>
#include <string>
#include<stdlib.h>
#include<windows.h>
#include <pthread.h>//导入线程头文件库
#include <mmsystem.h> //导入声音头文件库
#pragma comment(lib,"winmm.lib")//导入声音的链接库
#define _CRT_SECURE_NO_WARNINGS 
using namespace std;#define t_b 0  	//图形显示区域上侧边界 
#define l_b 0	//图形显示区域左侧边界
#define r_b 100	//图形显示区域右侧边界
#define b_b 20	//图形显示区域下侧边界//定义飞机造型 
const string icon_plane[]={"    ■   ","■  ■  ■","■■■■■","■  ■  ■","    ■   ","  ■■■ "};//定义图标坐标结构体 
typedef struct{int x;int y;
} location;//定义移动方向命令枚举类型 
typedef  enum {up_cmd,down_cmd,left_cmd,right_cmd,up_cmd2,down_cmd2,left_cmd2,right_cmd2} direction_cmd;extern location plocation,plocation2;			//声明飞机坐标 
static direction_cmd dir_cmd; 		//声明存放按键码值的两个变量//声明刷新飞机位置函数 
void show_plane(location plct);//获取键盘指令 
direction_cmd key(void);//计算出接收指令后的飞机坐标 
location plane_locate(int n,location plct,direction_cmd dircmd);//计算出接收指令后的飞机坐标 
location plane_locate2(location plct,direction_cmd dircmd);void init(void);void* thread_bgmusic(void* arg);
void play_bgmusic();
void bgmusic();#endif

3、库函数control_plane.cpp

#include <iostream>
#include "conio.h"
#include <string>
#include "control_plane.h"
#include<windows.h>
using namespace std;//初始化函数 
void init(void)
{plocation.x=2*r_b/3;				//初始化飞机图标的x坐标为屏幕横轴最大值的一半 plocation.y=b_b;					//初始化飞机图标的y坐标为屏幕纵轴最大值plocation2.x=r_b/3;					//初始化飞机图标的x坐标为屏幕横轴最大值的一半 plocation2.y=b_b;					//初始化飞机图标的y坐标为屏幕纵轴最大值
}//********************************************************************************//以下三个函数为播放背景音乐功能 
//********************************************************************************//播放一遍背景音乐 void play_bgmusic() {  mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);mciSendString(TEXT("play s1"),NULL,0,NULL);Sleep(153*1000);//153*1000意思是153秒,是整首音乐的时长 mciSendString(TEXT("close S1"),NULL,0,NULL); }//循环播放音乐线程函数 
void* thread_bgmusic(void* arg) //
{ while(1){  	play_bgmusic();}
} //创建音乐播放线程,开始循环播放音乐 
void bgmusic()
{pthread_t tid; pthread_create(&tid, NULL, thread_bgmusic, NULL);
}void gotoxy(int x, int y) {COORD pos = { x,y };HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄SetConsoleCursorPosition(hOut, pos);//两个参数分别指定哪个窗口,具体位置
}//飞机图标刷新函数 
void show_plane(location plct)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{int x,y;int i,j;	int rows=sizeof(icon_plane)/sizeof(icon_plane[0]);x=plct.x;y=plct.y;for(i=0;i<rows;i++)				//图标每行前输出x个空格 {gotoxy(x,y+i);cout<<icon_plane[i]<<endl;}
}//获取键盘指令函数 
direction_cmd key(void)
{int key_value1,key_value2;			//声明两个变量,存放键值 key_value1=getch();					//先获取第一个码值 if(key_value1==224)					//如果第一个码值为224,则进行第二个码值的判断 {key_value2=getch();			//先获取第二个码值switch(key_value2){case 72:				//向上方向键 return up_cmd;case 80:				//向下方向键return down_cmd;case 75:				//向左方向键return left_cmd;case 77:				//向右方向键return right_cmd;}}else{switch(key_value1){case 119:				//向上方向键 return up_cmd2;case 115:				//向下方向键return down_cmd2;case 97:				//向左方向键return left_cmd2;case 100:				//向右方向键return right_cmd2;}}		
}//计算获得移动指令后飞机的坐标 
location plane_locate(int n,location plct,direction_cmd dircmd)
{int x,y;x=plct.x;y=plct.y;if(n==1){switch(dircmd){case up_cmd:y--;				//字符上移一行,行值y减1if(y<t_b)			//限定y值最小值为0{y=t_b;}break;case down_cmd:y++;				//字符下移一行,行值y加1if(y>b_b)			//限定y高度 {y=b_b;}break;case left_cmd:x--;				//字符左移一列,列值x减1if(x<l_b){x=l_b;			//限定x最小值为0; }break;case right_cmd:x++;				//字符右移一列,列值x加1if(x>r_b){x=r_b;			//限定x宽度}break;}}else if(n==2){switch(dircmd){case up_cmd2:y--;				//字符上移一行,行值y减1if(y<t_b)			//限定y值最小值为0{y=t_b;}break;case down_cmd2:y++;				//字符下移一行,行值y加1if(y>b_b)			//限定y高度 {y=b_b;}break;case left_cmd2:x--;				//字符左移一列,列值x减1if(x<l_b){x=l_b;			//限定x最小值为0; }break;case right_cmd2:x++;				//字符右移一列,列值x加1if(x>r_b){x=r_b;			//限定x宽度}break;}}plct.x=x;plct.y=y;return plct;}//计算获得移动指令后飞机的坐标 
location plane_locate2(location plct,direction_cmd dircmd)
{int x,y;x=plct.x;y=plct.y;switch(dircmd){case up_cmd2:y--;				//字符上移一行,行值y减1if(y<t_b)			//限定y值最小值为0{y=t_b;}break;case down_cmd2:y++;				//字符下移一行,行值y加1if(y>b_b)			//限定y高度 {y=b_b;}break;case left_cmd2:x--;				//字符左移一列,列值x减1if(x<l_b){x=l_b;			//限定x最小值为0; }break;case right_cmd2:x++;				//字符右移一列,列值x加1if(x>r_b){x=r_b;			//限定x宽度}break;}plct.x=x;plct.y=y;return plct;}

三、运行效果

(未完待续) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/28750.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二、 MySQL 内部技术架构

二、 MySQL 内部技术架构 047 Mysql内部支持缓存查询吗&#xff1f; 当MySQL接收到客户端的查询SQL之后&#xff0c;仅仅只需要对其进行相应的权限验证之后&#xff0c;就会通过Query Cache来查找结果&#xff0c;甚至都不需要经过Optimizer模块进行执行计划的分析优化&…

腾讯云国际轻量应用服务器怎么使用呢?

腾讯云国际轻量应用服务器怎么使用呢&#xff1f;下面一起来了解一下&#xff1a; 1. 熟悉轻量应用服务器基础知识 ①什么是轻量应用服务器 TencentCloud Lighthouse&#xff1f; ②轻量应用服务器与云服务器 CVM 的区别是什么&#xff1f; ③为什么选择轻量应用服务器&#xf…

杀手锏SwissTable

杀手锏SwissTable 0.导语 最近在研究HashJoin的性能&#xff0c;发现SwissTable的性能真牛逼&#xff0c;对于原生的哈希表采用STL的unordered_multimap&#xff0c;其性能一般&#xff0c;为了加速这个查找&#xff0c;Arrow提供了SwissJoin&#xff0c;其实现原理为SwissTabl…

Java GUI——网页浏览器开发

Java GUI——网页浏览器开发 前言&#xff1a;为了做java课设&#xff0c;学了一手Java GUI。感觉蛮有意思的&#xff0c;写写文章&#xff0c;做个视频记录一下。欢迎大家友善指出我的不足 网页浏览器开发录制视频&#xff0c;从头敲到尾 任务需求 界面需求 菜单栏 文件 【…

接口测试之文件上传

在日常工作中&#xff0c;经常有上传文件功能的测试场景&#xff0c;因此&#xff0c;本文介绍两种主流编写上传文件接口测试脚本的方法。 首先&#xff0c;要知道文件上传的一般原理&#xff1a;客户端根据文件路径读取文件内容&#xff0c;将文件内容转换成二进制文件流的格式…

vue3中使用vue-simple-uploader

vue-simple-uploader本身是基于vue2的&#xff0c;直接npm i vue-simple-uploader -S下载下来版本的是0.7.6。在vue3中无法使用会报错。 解决&#xff1a;使用next安装接下来要发布的版本就会下载1.0.1版本&#xff0c;即可使用vue3 npm i vue-simple-uploadernext -S 注意&…

【linux】ssh 和adb connect区别

问&#xff1a;ssh 与ping的区别 答&#xff1a;SSH&#xff08;Secure Shell&#xff09;和Ping是两种完全不同的网络工具。 SSH是一种加密的网络协议&#xff0c;用于安全地远程管理或访问远程计算机。它提供了一种安全的通信方式&#xff0c;可以在不安全的网络上进行远程登…

SpringMVC的架构有什么优势?——视图与模型(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

【go】GIN参数重复绑定报错EOF问题

文章目录 1 问题描述2 解决&#xff1a;替换为ShouldBindBodyWith 1 问题描述 在 Gin 框架中&#xff0c;当多次调用 ShouldBind() 或 ShouldBindJSON() 方法时&#xff0c;会导致请求体的数据流被读取多次&#xff0c;从而出现 “EOF” 错误。 例如在api层绑定了参数&#x…

【数据结构】‘双向链表’冲冲冲

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Prometheus技术文档-概念

Prometheus是一个开源的项目连接如下&#xff1a; Prometheus首页、文档和下载 - 服务监控系统 - OSCHINA - 中文开源技术交流社区 基本概念&#xff1a; Prometheus是一个开源的系统监控和告警系统&#xff0c;由Google的BorgMon监控系统发展而来。它主要用于监控和度量各种…

《UNIX 传奇:历史与回忆》读后感

《UNIX 传奇&#xff1a;历史与回忆》 是 bwk&#xff08;Brian W. Kernighan&#xff09;2019 年的新作&#xff0c;回忆了 UNIX 在大半个世纪的风雨历程&#xff0c;是一本引人入胜的书籍。通过对 UNIX 操作系统的历史和发展进行详细的叙述和回顾&#xff0c;让我对这个操作系…

微信-公众号服务器配置,小程序token认证

参考微信文档 这部分代码试用于微信公众号的服务器token校验,且适用于小程序的token校验 接入概述 | 微信开放文档 @RequestMapping(value = "/message/verify")public @ResponseBody String verify(HttpServletRequest request, HttpServletResponse respons…

redis String类型命令

Redis的String类型是一种简单的键值对数据结构&#xff0c;常用的String类型命令有&#xff1a; SET key value&#xff1a;设置指定key的值为value。GET key&#xff1a;获取指定key的值。DEL key&#xff1a;删除指定key及其对应的值。INCR key&#xff1a;将指定key的值加1…

【eNSP】静态路由

【eNSP】静态路由 原理网关路由表 实验根据图片连接模块配置路由器设备R1R2R3R4 配置PC的IP地址、掩码、网关PC1PC2PC3 配置静态路由查看路由表R1R2R3R4测试能否通信 原理 网关 网关与路由器地址相同&#xff0c;一般路由地址为.1或.254。 网关是当电脑发送的数据的目标IP不在…

CSS基础介绍笔记1

官方文档 CSS指的是层叠样式&#xff08;Cascading Style Sheets&#xff09;地址&#xff1a;CSS 教程离线文档&#xff1a;放大放小&#xff1a;ctrl鼠标滚动为什么需要css&#xff1a;简化修改HTML元素的样式&#xff1b;将html页面的内容与样式分离提高web开发的工作效率&…

ROS实现自定义信息以及使用

常见的消息包 消息包定义一般如下&#x1f447; &#xff08;1&#xff09;创建包和依赖项 &#xff08;2&#xff09;在新建的qq_msgs的包新建msgs的文件夹&#xff0c;在该文件夹里面新建Carry.msg类型的文件。 其实&#xff0c;Carry.msg就是你自己定义的消息类型&am…

C++入门(小白篇1—编译器安装-代码注释等)

前言&#xff1a; 最近想学一下一下C看了一些博客内容写的倒是很充实&#xff0c;但是&#xff0c;细节不到位&#xff0c;我是有Python基础的&#xff0c;所以学习来蛮快的&#xff0c;但是对于小白的话&#xff0c;有好多小细节大多数博客还是不够详细&#xff0c;由此我想写…

【机器学习】在 MLOps构建项目 ( MLOps2)

My MLOps tutorials: Tutorial 1: A Beginner-Friendly Introduction to MLOps教程 2&#xff1a;使用 MLOps 构建机器学习项目 一、说明 如果你希望将机器学习项目提升到一个新的水平&#xff0c;MLOps 是该过程的重要组成部分。在本文中&#xff0c;我们将以经典手写数字分类…

新型高速 JavaScript 运行时 Bun 0.7 发布

导读近日&#xff0c;新型 JavaScript 运行时 Bun 正式发布了 0.7 版本&#xff0c;带来了重大的升级。据悉&#xff0c;Bun 是一个配套齐全的 JavaScript 解决方案&#xff0c;集运行时、打包器、转译器和包管理器于一体&#xff0c;追求极致的运行速度。此次更新主要集中在与…