[导读]本系列博文内容链接如下:
【C++】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值
【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动
【C++】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动【C++】做一个飞机空战小游戏(四)——给游戏添加背景音乐(多线程技巧应用)
【C++】做一个飞机空战小游戏(五)——getch()控制两个飞机图标移动(控制光标位置)
【C++】做一个飞机空战小游戏(六)——给两架飞机设置不同颜色(cout输出彩色字符、结构体使用技巧)
【C++】做一个飞机空战小游戏(七)——两组按键同时检测平滑移动(GetAsyncKeyState()函数应用)
目录
飞机空战游戏,一般都是两个飞机,今天介绍一下如何用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减1
if(y<t_b) //限定y值最小值为0
{
y=t_b;
}
break;
case down_cmd:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd:
x--; //字符左移一列,列值x减1
if(x<l_b)
{
x=l_b; //限定x最小值为0;
}
break;
case right_cmd:
x++; //字符右移一列,列值x加1
if(x>r_b)
{
x=r_b; //限定x宽度
}
break;
}
}
else if(n==2)
{
switch(dircmd)
{
case up_cmd2:
y--; //字符上移一行,行值y减1
if(y<t_b) //限定y值最小值为0
{
y=t_b;
}
break;
case down_cmd2:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd2:
x--; //字符左移一列,列值x减1
if(x<l_b)
{
x=l_b; //限定x最小值为0;
}
break;
case right_cmd2:
x++; //字符右移一列,列值x加1
if(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减1
if(y<t_b) //限定y值最小值为0
{
y=t_b;
}
break;
case down_cmd:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd:
x--; //字符左移一列,列值x减1
if(x<l_b)
{
x=l_b; //限定x最小值为0;
}
break;
case right_cmd:
x++; //字符右移一列,列值x加1
if(x>r_b)
{
x=r_b; //限定x宽度
}
break;
}
}
else if(n==2)
{
switch(dircmd)
{
case up_cmd2:
y--; //字符上移一行,行值y减1
if(y<t_b) //限定y值最小值为0
{
y=t_b;
}
break;
case down_cmd2:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd2:
x--; //字符左移一列,列值x减1
if(x<l_b)
{
x=l_b; //限定x最小值为0;
}
break;
case right_cmd2:
x++; //字符右移一列,列值x加1
if(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减1
if(y<t_b) //限定y值最小值为0
{
y=t_b;
}
break;
case down_cmd2:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd2:
x--; //字符左移一列,列值x减1
if(x<l_b)
{
x=l_b; //限定x最小值为0;
}
break;
case right_cmd2:
x++; //字符右移一列,列值x加1
if(x>r_b)
{
x=r_b; //限定x宽度
}
break;
}
plct.x=x;
plct.y=y;
return plct;
}
三、运行效果
(未完待续)