【C++】做一个飞机空战小游戏(十一)——游戏过关、通关、结束的设置

news2024/12/23 4:27:01

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

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

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

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

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

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

【C++】做一个飞机空战小游戏(七)——两组按键同时检测平滑移动(GetAsyncKeyState()函数应用)

【C++】做一个飞机空战小游戏(八)——生成敌方炮弹(rand()函数应用)

【C++】做一个飞机空战小游戏(九)——发射子弹的编程技巧

【C++】做一个飞机空战小游戏(十)——子弹击落炮弹、炮弹与飞机相撞

【C++】做一个飞机空战小游戏(十一)——游戏过关、通关、结束的设置

本节介绍游戏的过关、通关和游戏结束(game over)的相关设置。过关的依据是敌方死亡炮弹的数量,当达到设定数目时,即可通过这一关,当通过最后一关时,即完成通关,如果还未通关时,所有飞机的命已经没了,那游戏就结束了。每一关敌方每波炮弹数量、每关总死亡炮弹数量、炮弹移动速度、炮弹血量、炮弹伤害值都是不一样的,是随着关数的增加而递增的,也就是游戏过关的难度越来越高。本节还实现了游戏信息显示、爆炸音效、飞机开火音效、控制台窗口大小设置等功能。

目录

一、游戏过关、通关、结束

(一)过关

1、game结构体新增属性

2、过关的判断条件

3、过关后的游戏属性变化

(二)通关

1、game结构体新增属性

2、判断条件

3、通关后的游戏属性变化

(1)主函数、炮弹更新线程、按键更新线程、子弹更新线程

(2)show_game_complete()函数

(三)游戏结束

1、game结构体新增属性

2、判断条件

3、游戏结束后的游戏属性变化

(1)主函数、炮弹更新线程、按键更新线程、子弹更新线程

(2)show_game_over()函数

二、游戏音效设置

(一)炸弹爆炸音效

1、爆炸音效线程

2、触发条件

(1)炮弹与飞机相撞

(2)子弹与炮弹相撞

(二)飞机开火音效

1、开火音效线程

2、触发条件

三、游戏信息显示

(一)需要显示的信息

1、当前关数

2、剩余炮弹

3、飞机杀敌数目

4、飞机剩余命数

5、飞机复活保护时间

(二)头文件相关信息

(三)显示函数

(四)飞机复活保护程序

1、倒计时线程

2、保护条件

四、漏洞修补

五、控制台窗口设置

(一)大小设置

(二)取消最大化最小化

1、函数调用

2、函数定义

六、最终程序

(一)主函数

(二)头文件control_plane.h

(三)库函数control_plane.cpp

七、运行效果

八、不足之处


一、游戏过关、通关、结束

(一)过关

1、game结构体新增属性

int bombs_round_increase; 	//下一关敌方每波发射炮弹数量增加值
int bombs_stage_increase; 	//下一关总计发射炮弹数量增加值
int bomb_interval_reduce    //下一关炮弹更新间隔减少值
int last_stage;             //游戏一共多少关

2、过关的判断条件

当前已发射并死亡的炮弹数量game.cur_num_bomb>=当前关总计发射并死亡的炮弹数量game.bombs_stage,并且当前关不是最后一关,如果当前关是最后一关,就是通关。

3、过关后的游戏属性变化

敌方每波炮弹数量、每关总死亡炮弹数量、炮弹移动速度、炮弹血量、炮弹伤害值都相应增加。代码如下:

if(game.cur_num_bomb>=game.bombs_stage)	
{
	if(game.cur_stage<game.last_stage)
	{
		game.clear=true;
		game.cur_num_bomb=0;
		game.bombs_stage+=game.bombs_stage_increase;
		game.bombs_round+=game.bombs_round_increase;
		game.bomb_interval-=game.bomb_interval_reduce;
		game.cur_stage+=1;
		init_bombs();
		init_bullets();							
	}
	else
	{
		game.complete=true;
	}												
}					

炮弹血量、伤害值的变化在init_bombs()函数中

//单个炮弹初始化函数 
Bomb init_bomb(Bomb bomb,int stage)
{
	bomb.location.x=rand()%r_b;	//x坐标随机 
	bomb.location.y=b_b+6;		//y显示区域底部下一行 
	bomb.icon=icon_bomb;		//设置炮弹图标 
	bomb.color=6;				//设置炮弹颜色为6,黄色 
	bomb.dam=1+stage/3;			//炮弹伤害值每过3关增加1点 
	bomb.hp=stage;				//炮弹血量每过1关增加1点 
	bomb.alive=false;			//炮弹设置为存活状态 
	bomb.rt=rand()%(eq_rt+1)+1;	//炮弹复活时间随机生成 
	return bomb;			
}

//所有炮弹初始化函数 
void init_bombs(void)
{
	game.bomb_move=false;		
	
	for(int i=0;i<game.bombs_round;i++)
	{
		bomb[i]=init_bomb(bomb[i],game.cur_stage);	
	}
}

(二)通关

1、game结构体新增属性

bool show_complete;			//是否显示游戏通关信息 

2、判断条件

判定条件见过关的判定条件。

3、通关后的游戏属性变化

通关后game.complete=true,然后要显示通关信息。

(1)主函数、炮弹更新线程、按键更新线程、子弹更新线程

while(game.complete)
{
	show_game_complete();
}

(2)show_game_complete()函数

//显示游戏通关信息 
void show_game_complete(void)
{
	if(!game.show_complete)
	{
		game.show_complete=true;
		Sleep(100);  
		system("cls");
		show_info();
		gotoxy((l_b+r_b)/2-4,(t_b+b_b)/2);				
		ColorCout(game_complete_text,game.info_color);		
	}
}

(三)游戏结束

1、game结构体新增属性

bool show_gameover;			//是否显示游戏结束信息

2、判断条件

判断条件就是两架飞机命都为0,具体代码在void* thread_bomb(void* arg)线程中

//以下一段程序作用:判断两架飞机都没有命的情况,即为游戏结束 
if(!(plane[0].life>0||plane[1].life>0))
{
	game.gameover=true;
}

3、游戏结束后的游戏属性变化

游戏结束后,要显示游戏结束的信息。

(1)主函数、炮弹更新线程、按键更新线程、子弹更新线程

while(game.gameover)		//等待游戏结束 
{
	show_game_over();
}

(2)show_game_over()函数

//显示游戏结束信息 		
void show_game_over(void)
{	
	if(!game.show_gameover)
	{
		game.show_gameover=true;
		Sleep(100); 
		system("cls");
		show_info();
		gotoxy((l_b+r_b)/2-4,(t_b+b_b)/2);				
		ColorCout(game_over_text,game.info_color);		
	}
}

二、游戏音效设置

(一)炸弹爆炸音效

1、爆炸音效线程

//********************************************************************************
 //以下三个函数为播放爆炸音效功能 
//********************************************************************************
//循环播放爆炸音乐线程函数 
void* thread_bomb_music(void* arg) //
{ 
  while(1)
  {  	
  	if(game.bomb)
  	{
  		play_bomb_music();
		game.bomb=false;	
	}	  
  }
} 
 
//创建爆炸音效播放线程,开始循环播放音效 
void bomb_music()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bomb_music, NULL);
}

 //播放一遍爆炸音效 
 void play_bomb_music() {  
 
	mciSendString(TEXT("open bomb.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(2*1000);//2*1000意思是2秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL);   
}

2、触发条件

(1)炮弹与飞机相撞

代码在thread_bomb(void* arg)线程中:

if(collide(plane[j],bomb[i])) 	//判断飞机是否与炮弹相撞 
{
	game.bomb=true;
    ......
}

(2)子弹与炮弹相撞

代码在thread_bullet(void* arg)线程中:

if(shoot(bullet[i][j],bomb[k]))		//如果被击中 
{
	game.bomb=true;					//爆炸声音启动

    ...... 
							
}

(二)飞机开火音效

1、开火音效线程

//********************************************************************************
 //以下三个函数为播放机开火音效功能 
//********************************************************************************
//循环播放飞机开火音效线程函数 
void* thread_fire_music(void* arg) //
{ 
  while(1)
  {  	
  	if(game.fire)
  	{
  		play_fire_music();
		game.fire=false;	
	}	  
  }
} 
 
//创建飞机开火音效播放线程,开始循环播放音乐 
void fire_music()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_fire_music, NULL);
}

 //播放一遍飞机开火音效 
 void play_fire_music() {  
 
	mciSendString(TEXT("open fire.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(1500);//153*1000意思是153秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL);   
}

2、触发条件

飞机开火键按下,代码在开火函数中:

//发射子弹 
Bullet fire(Plane plane,Bullet bullet)
{	
	game.fire=true;	
    ......		
}

三、游戏信息显示

(一)需要显示的信息

1、当前关数

当前所在关的关数game.cur_stage。

2、剩余炮弹

剩余炮弹=该关设定总死亡炮弹数量-当前已死亡炮弹数量。

game.bombs_stage-game.cur_num_bomb。

3、飞机杀敌数目

飞机杀敌数目=该飞机子弹击落炮弹数量+飞机撞落炮弹数量。

4、飞机剩余命数

飞机剩余命数plane.life

5、飞机复活保护时间

防止飞机刚复活时,其初始化位置刚好有炮弹而将飞机撞落,所以设定了复活保护时间为10s。在此期间,炮弹与飞机相撞,飞机和炮弹都不会掉血。

(二)头文件相关信息

//以下为游戏当中要显示的信息文本 
#define cur_stage_text "当前关次:"
#define remain_bombs_text "剩余炮弹:"
#define play0_kill_text "飞机0杀敌:"
#define play1_kill_text "飞机1杀敌:"
#define play0_life_text "飞机0命数:"
#define play1_life_text "飞机1命数:"
#define play0_rt_text "飞机0保护时间:"
#define play1_rt_text "飞机1保护时间:" 
#define game_complete_text "恭喜,游戏通关!"
#define game_over_text "遗憾,游戏结束!"

extern Location loc_info;

(三)显示函数

//显示游戏信息 
void show_info(void)
{
	gotoxy(loc_info.x,loc_info.y);
	ColorCout(cur_stage_text,game.info_color);
	ColorCout(game.cur_stage,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+2);
	ColorCout(remain_bombs_text,game.info_color);
	ColorCout(game.bombs_stage-game.cur_num_bomb,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+10);
	ColorCout(play0_kill_text,plane[0].color);
	ColorCout(plane[0].kill,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+12);
	ColorCout(play1_kill_text,plane[1].color);
	ColorCout(plane[1].kill,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+20);
	ColorCout(play0_life_text,plane[0].color);
	ColorCout(plane[0].life,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+22);
	ColorCout(play1_life_text,plane[1].color);
	ColorCout(plane[1].life,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+30);
	ColorCout(play0_rt_text,plane[0].color);
	ColorCout(plane[0].rt,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+32);
	ColorCout(play1_rt_text,plane[1].color);
	ColorCout(plane[1].rt,game.info_color);	
}

(四)飞机复活保护程序

1、倒计时线程

//********************************************************************************
 //以下两个函数为飞机复活时间计时功能 
//********************************************************************************
//飞机复活保护时间线程 
void* thread_plane_rt(void* arg) //
{ 
  while(1)
  {  	
	Sleep(1000); 
	for(int j=0;j<game.num_plane;j++)
	{
		if(plane[j].rt>0)
		{
			plane[j].rt-=1;
		}
	}
		
  }
} 
 
//创建飞机复活保护时间线程 
void plane_rt()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_plane_rt, NULL);
}

2、保护条件

在炮弹位置更新线程中,判断飞机与炮弹相撞之前,先要判断飞机是否存活并且不在保护期内。

if(plane[j].alive&&!plane[j].rt)	//判断的必要条件为飞机存活并且已经出了死亡恢复保护期 
{
    if(collide(plane[j],bomb[i])) 	//判断飞机是否与炮弹相撞 
	{
        ......
    }
}

四、漏洞修补

游戏增加了如果两架飞机至少有一架飞机还有命的情况下,飞机在死亡状态必须复活。因为游戏在两架飞机在有命而不复活的情况下还继续进行,而每关设定的炮弹死亡也包括冲出底部边界情况(此种情况称为坠落),那样就可以不复活而让炮弹只通过坠落就可以实现通关了。

//以下一段程序作用:防止两架飞机都死亡之后,且至少有一架飞机还有命的情况下,不按复活键,让游戏在无飞机的情况下过关 
if(!plane[(j+1)%2].alive)		//(j+1)%2即为除j之外的一架飞机,j=0,(j+1)%2=1;j=1,(j+1)%2=0 
{
	if(plane[(j+1)%2].life>0)	//判断另一架飞机是否还有命,如果有,则将其复活,如果没有,则将自己复活 
	{
		plane[(j+1)%2]=init_plane(plane[(j+1)%2]);
		show_plane(plane[(j+1)%2]);	
	}
	else
	{
		if(plane[j].life>0)
		{
			plane[j]=init_plane(plane[j]);
			show_plane(plane[j]);	
		}
	}
									
}

五、控制台窗口设置

对控制台窗口的设置都在初始化函数void init(void)中。

(一)大小设置

system("mode con cols=175 lines=46");	//设置控制台屏幕的大小

(二)取消最大化最小化

1、函数调用

SizeGoAway();

2、函数定义

//取消最大化,最小化
void SizeGoAway() {
	SetWindowLongPtrA(
		GetConsoleWindow(),
		GWL_STYLE,
		GetWindowLongPtrA(GetConsoleWindow(), GWL_STYLE) & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
}

六、最终程序

(一)主函数

#include "control_plane.h"
#include "quenue.h"
#include "graphics.h"
#include <sstream>
//#include "acllib.h"
using namespace std; 
 
Plane plane[eq_plane];
Game game;
Bomb bomb[eq_bombs_round];
Bullet bullet[eq_plane][eq_bullets_round];
Location plocation[]={{2*r_b/3,b_b},{r_b/3,b_b}};

Location loc_info={r_b+15,t_b+5};

int main(int argc, char** argv) {	

	init();	//初始化						 
 
	bgmusic();//播放背景音乐
	bomb_music(); 
	fire_music();
	getmykey();//循环等待键盘指令
	bomb_location_update();	
	bullet_location_update();
	plane_rt();
	
	while(1)					 
	{
		while(game.pause)
		{
			;
		}
		
		while(game.complete)
		{
			show_game_complete();
		}
		
		while(game.gameover)
		{
			show_game_over();
		}
		
		if(plane[0].keycmd!=none_cmd ||plane[1].keycmd!=none_cmd ||game.bomb_move ||game.bullet_move)
		{
			game.bomb_move=false;
			game.bullet_move=false;
			system("cls");			
			show_info();
			for(int i=0;i<game.num_plane;i++)
			{
				if(plane[i].alive)
				{
					show_plane(plane[i]);//刷新飞机图标
				}
				
			}
			
			for(int i=0;i<eq_plane;i++)
			{
				for(int j=0;j<eq_bullets_round;j++)
				{				
					if(bullet[i][j].alive)
					{
						show_bullet(bullet[i][j]);
					}
				}	
			}	
			
			for(int i=0;i<game.bombs_round;i++)
			{
				if(bomb[i].alive)
				{
					show_bomb(bomb[i]);
				}
				
			}
			
			
		}	
	}
	return 0; 	
}

(二)头文件control_plane.h

#ifndef CONTROL_PLANE_H
#define CONTROL_PLANE
#include <iostream>
#include <ctime>
#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 140			//图形显示区域右侧边界
#define b_b 40			//图形显示区域下侧边界
#define plane_width 9	//飞机宽度 
#define plane_height 6	//飞机高度 

#define eq_plane 2			//飞机架数
#define eq_bombs_round 32	//eq=end quantity最后一关每波炮弹数量 
#define eq_rt 5				//炮弹复活最大时间
#define eq_bullets_round 40	//eq=end quantity飞机每波发射子弹最大数量 


//以下为游戏当中要显示的信息文本 
#define cur_stage_text "当前关次:"
#define remain_bombs_text "剩余炮弹:"
#define play0_kill_text "飞机0杀敌:"
#define play1_kill_text "飞机1杀敌:"
#define play0_life_text "飞机0命数:"
#define play1_life_text "飞机1命数:"
#define play0_rt_text "飞机0保护时间:"
#define play1_rt_text "飞机1保护时间:" 
#define game_complete_text "恭喜,游戏通关!"
#define game_over_text "遗憾,游戏结束!"

 
//定义飞机造型 
const string icon_plane1[]={"    ■","■  ■  ■","■■■■■","■  ■  ■","    ■","  ■■■"};
const string icon_plane2[]={"    ■","■  ■  ■","■■■■■","    ■","  ■■■","■■■■■"};

//定义炮弹造型
const string icon_bomb="■";

//定义子弹造型
const string icon_bullet="■";
 
//定义坐标结构体 
typedef struct{
	int x;
	int y;
} Location;
 
 
//定义移动方向命令枚举类型 
typedef  enum {none_cmd,up_cmd,down_cmd,left_cmd,right_cmd} direction_cmd; 


//定义游戏结构体 
typedef struct{
	int cur_stage;				//游戏当前关 	
	int bombs_round;			//当前关敌方每波发射炮弹数量
	int bombs_round_increase; 	//下一关敌方每波发射炮弹数量增加值 
	int bombs_stage;			//当前关总计发射炮弹数量
	int bombs_stage_increase; 	//下一关总计发射炮弹数量增加值
	
	
	bool clear;					//游戏过关 
	bool complete;				//游戏通关 
	bool gameover;				//游戏结束
	int num_plane;				//飞机数量 
	int cur_num_bomb;			//当前已发射炮弹数量 
	int bomb_interval; 			//炮弹位置更新间隔
	int bomb_interval_reduce;	//下一关炮弹位置更新间隔减少值 
	
	int bullet_interval; 		//子弹位置更新间隔 
	bool bomb_move;				//炮弹是否移动
	bool bullet_move;			//子弹是否移动
	bool pause;					//游戏是否暂停
	int last_stage;				//游戏最后一关数目 
	int info_color;				//游戏信息文本颜色 
	bool bomb;					//是否调用爆炸音效 
	bool fire;					//是否调用开火音效 
	bool show_gameover;			//是否显示游戏结束信息 
	bool show_complete;			//是否显示游戏通关信息 
}Game;
 
//定义飞机结构体 
typedef struct{
	Location location;		//飞机坐标 
	int color;				//飞机颜色 
	int icon;				//飞机图标编号 
	direction_cmd keycmd;	//飞机移动方向命令 
	bool fire;				//飞机是否开火 
	int cnt_bullets; 		//按一次开火键加1,然后初始化bullet[cnt_bullets],子弹死亡一个减1
	int hp;					//飞机血量 
	bool alive;				//飞机存活标志 
	int No;					//飞机序号 
	int life;				//飞机命数
	int kill;				//飞机击落炮弹数量
	int rt;					//rt=respawn time复活时间,在此期间炮弹对飞机无伤害 
}Plane;

//定义敌方炮弹结构体 
typedef struct{
	Location location;	//炮弹位置 
	bool alive;			//炮弹是否存活 
	int color;			//炮弹颜色
	string icon;		//炮弹图标
	int rt;				//rt=respawn time复活时间 
	int hp;				//hp=hit point 生命值,此值<=0时,敌方炮弹死亡,敌方炮弹被飞机子弹击中hp会减少,坠地或与飞机相撞hp直接降为0 
	int dam;			//dam=damage 伤害值
	int type;			//炮弹类型 
}Bomb;

//定义子弹结构体 
typedef struct{
	Location location;	//子弹位置 
	bool alive;			//子弹是否存活 
	int color;			//子弹颜色
	string icon;		//子弹图标	
	int hp;				//hp=hit point 生命值,子弹击中炮弹或冲出屏幕上方hp直接降为0,子弹死亡 
	int dam;			//dam=damage 伤害值
	int type;			//子弹类型 
}Bullet;
 
extern Plane plane[eq_plane];
extern Game game;
extern Bomb bomb[eq_bombs_round];
extern Bullet bullet[eq_plane][eq_bullets_round];

extern Location plocation[];
extern Location loc_info;
 
//声明刷新飞机位置函数
void show_plane(Plane plane);
 
//获取键盘指令 
void key(void);
 
//更新所有飞机坐标
void plane_location_update(void);
 
//初始化函数 
void init(void);
 
//播放背景音乐线程 
void* thread_bgmusic(void* arg);
void play_bgmusic();
void bgmusic();
 
//获取按键指令线程
void* thread_key(void* arg);
void getmykey();
 
//输出彩色字符函数
template<typename T>	//T表示任何可以被cout输出的类型 
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0);

void gotoxy(int x, int y);

void init_bombs(void);
Bomb init_(Bomb bomb,int stage);
void* thread_bomb(void* arg);
void bomb_location_update();
void show_bomb(Bomb bomb);


void bullet_location_update();			//子弹位置更新 
void* thread_bullet(void* arg);			//子弹线程函数 
Bullet init_bullet(Bullet bullet);		//单个子弹初始化
void init_bullets(void);				//所有子弹初始化
Bullet fire(Plane plane,Bullet bullet);	//飞机开火函数,确定子弹出现的起始位置 
void show_bullet(Bullet bullet);		//显示子弹图标 


bool collide(Plane plane,Bomb bomb);
bool shoot(Bullet bullet,Bomb bomb);

Plane init_plane(Plane plane);
void init_planes(void);

void* thread_bomb_music(void* arg);
void bomb_music();
void play_bomb_music();

void* thread_fire_music(void* arg);
void fire_music();
void play_fire_music();

void show_info(void);
void show_game_complete(void);
void show_game_over(void);

void plane_rt();
void* thread_plane_rt(void* arg);

void SizeGoAway();

#endif

(三)库函数control_plane.cpp

#include <iostream>
#include "conio.h"
#include <string>
#include "control_plane.h"
#include<windows.h>
using namespace std;
 
 
//彩色输出函数
template<typename T>	//T表示任何可以被cout输出的类型
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0)
{
	//	0 = 黑色	1 = 蓝色	 2 = 绿色	 3 = 浅绿色		 4 = 红色	 5 = 紫色	 6 = 黄色	 7 = 白色
	//	8 = 灰色	9 = 淡蓝色	10 = 淡绿色	11 = 淡浅绿色	12 = 淡红色	13 = 淡紫色	14 = 淡黄色	15 = 亮白色
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), ForeColor + BackColor * 0x10);
	cout << t;
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
 
 
//隐藏光标函数
HANDLE han = GetStdHandle(-11);
void hide(){
	CONSOLE_CURSOR_INFO cursor;
	cursor.bVisible = 0;
	cursor.dwSize = 1;
	SetConsoleCursorInfo(han,&cursor);
}

//光标移动位置 
void gotoxy(int x, int y) {
	COORD pos = { x,y };
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄
	SetConsoleCursorPosition(hOut, pos);//两个参数分别指定哪个窗口,具体位置
}
 
//初始化函数 
void init(void)
{
	plane[0].No=0;				//飞机0序号为0 
	plane[1].No=1;				//飞机1序号为1
	
	plane[0].life=3;			//飞机0命数为3		
	plane[1].life=3;			//飞机1命数为3
	
	plane[0].kill=0;			//飞机0初始杀敌数为0		
	plane[1].kill=0;			//飞机1初始杀敌数为0 
	
	srand(time(NULL));			//设置随机函数种子 
	
	game.num_plane=2;			//飞机数量 
	game.bombs_round=5;			//第一关炮弹数量置5 
	game.bomb_move=false;		//炮弹是否移动标志置否 
	game.bullet_move=false;		//子弹是否移动标志置否 
	game.bomb_interval=1000;	//炮弹初始移动间隔1秒钟 
	game.cur_stage=1;			//游戏初始关设置为1 
	game.bombs_stage=10;		//游戏初始关炮弹总数设置为10 
	game.pause=false;			//游戏暂停设置为否 
	game.info_color=5;			//游戏信息颜色设置为5,紫色 
	game.cur_num_bomb=0;		//游戏当前死亡炮弹数量设置为0	
	game.bullet_interval=500;	//子弹移动间隔设置为1秒钟
	game.bomb=false;			//爆炸标志设置为否,此标志为爆炸声音播放标志 
	game.fire=false;			//飞机开火标志,此标志为飞机开火声音播放标志 
	game.gameover=false;		//游戏结束标志设置为否 
	game.complete=false;		//游戏通关标志设置为否
	game.last_stage=10;			//游戏总关数设置为10
	
	game.bombs_round_increase=3;//每过一关敌方每波发射炮弹数量增加3个 
	game.bombs_stage_increase=10;//每过一关敌方该关总发射炮弹数量增加10个
	game.bomb_interval_reduce=100; 
	
	game.show_gameover=false;	//显示游戏结束信息标志设置为否
	game.show_complete=false;	//显示游戏通关信息标志设置为否 
	
	system("mode con cols=175 lines=46");	//设置控制台屏幕的大小
	SizeGoAway();							//取消控制台屏幕的最大化最小化 
	
	
	init_bombs();				//初始化所有炸弹 
	init_bullets();				//初始化所有子弹 
	init_planes();				//初始化所有飞机 
	
	system("cls");
	for(int i=0;i<game.num_plane;i++)//刷新飞机图标
	{			
		show_plane(plane[i]);
	}		
	
	
	
	hide();//隐藏光标
}
 
 
 
//********************************************************************************
 //以下三个函数为获得按键指令线程函数 
//******************************************************************************** 
void* thread_key(void* arg)
{
	while(1)
	{
		Sleep(60); 		//获取指令延时一定时间,起滤波作用,延缓获取指令的响应速度 
		key();			//获取按键指令
		plane_location_update() ;//获取完指令马上更新飞机坐标 
	}
}

//按键指令线程启动函数
void getmykey()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_key, NULL);
}
 
//获取键盘指令函数
void key(void)
{
	if (GetAsyncKeyState(VK_SPACE) & 0x8000)
	{
		Sleep(50);
		game.pause=!game.pause;					
	}
	
	while(game.complete)
	{
		show_game_complete();
	}
	
	while(game.gameover)
	{
		show_game_over();
	}
	
	if(!game.pause)
	{
		if(plane[0].alive)
		{
			direction_cmd c=none_cmd;
			if (GetAsyncKeyState(VK_UP) & 0x8000)		c = up_cmd;
			if (GetAsyncKeyState(VK_DOWN) & 0x8000)		c = down_cmd;
			if (GetAsyncKeyState(VK_LEFT) & 0x8000)		c = left_cmd;
			if (GetAsyncKeyState(VK_RIGHT) & 0x8000)	c = right_cmd;
			plane[0].keycmd=c;												//刷新飞机方向指令											 
			if (GetAsyncKeyState('P') & 0x8000)		plane[0].fire = true;	//飞机开火指令
		}
		else
		{
			if(plane[0].life>0)
			{
				if (GetAsyncKeyState('O') & 0x8000)							//飞机复活指令
				{
					plane[0]=init_plane(plane[0]);
					show_plane(plane[0]);
				}
			}
			
		}
		
		if(plane[1].alive)
		{
			direction_cmd d=none_cmd;
			if (GetAsyncKeyState('W') & 0x8000)	d = up_cmd;
			if (GetAsyncKeyState('S') & 0x8000)	d = down_cmd;
			if (GetAsyncKeyState('A') & 0x8000)	d = left_cmd;
			if (GetAsyncKeyState('D') & 0x8000)	d = right_cmd;
			plane[1].keycmd=d;//刷新飞机图标
			if (GetAsyncKeyState('F') & 0x8000)			plane[1].fire = true;
		}
		else
		{
			if(plane[1].life>0)
			{
				if (GetAsyncKeyState('Q') & 0x8000)
				{
					plane[1]=init_plane(plane[1]);
					show_plane(plane[1]);
				}
			}					
		}	
	}	
}
 
 
//飞机图标刷新函数 
void show_plane(Plane plane)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{
	int x,y;
	int i,j;
	int rows;
	x=plane.location.x;
	y=plane.location.y;
	
	switch(plane.icon)
	{
		case 1://第一种造型 
			rows=sizeof(icon_plane1)/sizeof(icon_plane1[0]);
			for(i=0;i<rows;i++)				 
			{
				gotoxy(x,y+i);				
				ColorCout(icon_plane1[i],plane.color);
			}
			break;
		case 2://第二种造型 
			rows=sizeof(icon_plane2)/sizeof(icon_plane2[0]);
			for(i=0;i<rows;i++)				
			{
				gotoxy(x,y+i);				
				ColorCout(icon_plane2[i],plane.color);
			}
			break;				
	}
}
 
//更新两个飞机的坐标 
void plane_location_update(void)
{ 	
	for(int i=0;i<2;i++)
	{
		if(plane[i].keycmd!=none_cmd) 
		{
			int x,y;
 			x=plane[i].location.x;
 			y=plane[i].location.y;
 			switch(plane[i].keycmd)
			{
				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;
				
			}
			plane[i].location.x=x;
 			plane[i].location.y=y;
 			plane[i].keycmd=none_cmd;	
		}
					
	} 		
}

//单个炮弹初始化函数 
Bomb init_bomb(Bomb bomb,int stage)
{
	bomb.location.x=rand()%r_b;	//x坐标随机 
	bomb.location.y=b_b+6;		//y显示区域底部下一行 
	bomb.icon=icon_bomb;		//设置炮弹图标 
	bomb.color=6;				//设置炮弹颜色为6,黄色 
	bomb.dam=1+stage/3;			//炮弹伤害值每过3关增加1点 
	bomb.hp=stage;				//炮弹血量每过1关增加1点 
	bomb.alive=false;			//炮弹设置为存活状态 
	bomb.rt=rand()%(eq_rt+1)+1;	//炮弹复活时间随机生成 
	return bomb;			
}

//所有炮弹初始化函数 
void init_bombs(void)
{
	game.bomb_move=false;		
	
	for(int i=0;i<game.bombs_round;i++)
	{
		bomb[i]=init_bomb(bomb[i],game.cur_stage);	
	}
}

//炮弹位置更新 线程 
void* thread_bomb(void* arg)
{
	while(1)
	{
		Sleep(game.bomb_interval);	//更新间隔		
		game.bomb_move=true;		//更新一次炮弹移动一次 
		
		while(game.pause)			//等待游戏暂停 
		{
			;
		}
		
		while(game.complete)		//等待游戏通关 
		{
			show_game_complete();
		}
		
		while(game.gameover)		//等待游戏结束 
		{
			show_game_over();
		}
		
		//依次查询炮弹状态、更新位置,查询炮弹是否死亡,记录炮弹死亡数目,计算是否过关,是否通关。
		//还查询炮弹是否与飞机相撞,计算飞机血量,死亡状态,飞机命数,游戏是否gameover。		
		for(int i=0;i<game.bombs_round;i++)
		{			
			if(bomb[i].alive)					//如果炮弹存活,则进行移动,如果死亡则进行复活前的计时准备复活 
			{
				bomb[i].location.y++;			//炮弹移动一步 
				if(bomb[i].location.y>b_b+5)	//查询炮弹是否冲出底部边界 
				{
					bomb[i].hp=0;									
				}
			
			
				//此循环用于查询炮弹是否与飞机相撞 
				for(int j=0;j<game.num_plane;j++)
				{
					if(plane[j].alive&&!plane[j].rt)	//判断的必要条件为飞机存活并且已经出了死亡恢复保护期 
					{
						if(collide(plane[j],bomb[i])) 	//判断飞机是否与炮弹相撞 
						{
							game.bomb=true;						//爆炸声音启动 
							bomb[i].hp=0;						//炸弹血量置0 
							plane[j].hp-=bomb[i].dam;			//飞机血量减少值为炮弹伤害值 
							plane[j].kill+=1;					//飞机杀敌数加1 
														
							if(plane[j].hp<=0)					//判断飞机血量是否降到0以下 
							{
								plane[j].alive=false;			//飞机死亡 
								plane[j].life-=1;				//飞机命数减1 
								plane[j].keycmd=none_cmd;		//飞机移动指令置空 
								plane[j].location.x=0;			//飞机x坐标置0 
								plane[j].location.y=b_b+8;		//飞机y坐标置底部边界下3行 
								
								//以下一段程序作用:防止两架飞机都死亡之后,且至少有一架飞机还有命的情况下,不按复活键,让游戏在无飞机的情况下过关 
								if(!plane[(j+1)%2].alive)		//(j+1)%2即为除j之外的一架飞机,j=0,(j+1)%2=1;j=1,(j+1)%2=0 
								{
									if(plane[(j+1)%2].life>0)	//判断另一架飞机是否还有命,如果有,则将其复活,如果没有,则将自己复活 
									{
										plane[(j+1)%2]=init_plane(plane[(j+1)%2]);
										show_plane(plane[(j+1)%2]);	
									}
									else
									{
										if(plane[j].life>0)
										{
											plane[j]=init_plane(plane[j]);
											show_plane(plane[j]);	
										}
									}
									
								}
																 
								//以下一段程序作用:判断两架飞机都没有命的情况,即为游戏结束 
								if(!(plane[0].life>0||plane[1].life>0))
								{
									game.gameover=true;
								}									
							}	
						}
					}
					
				}				
				
							
				if(bomb[i].hp<=0)
				{
					bomb[i]=init_bomb(bomb[i],game.cur_stage);
					game.cur_num_bomb++;
					if(game.cur_num_bomb>=game.bombs_stage)	
					{
						if(game.cur_stage<game.last_stage)
						{
							game.clear=true;
							game.cur_num_bomb=0;
							game.bombs_stage+=game.bombs_stage_increase;
							game.bombs_round+=game.bombs_round_increase;
							game.bomb_interval-=game.bomb_interval_reduce;
							game.cur_stage+=1;
							init_bombs();
							init_bullets();
							
						}
						else
						{
							game.complete=true;
						}												
					}			
				}			
			}
			else							//复活前的倒计时 
			{
				bomb[i].rt--;
				if(bomb[i].rt<=0)			//倒计时结束 
				{
					bomb[i].alive=true;		//复活 
					bomb[i].location.y=t_b;	//将炮弹移动到界面顶部 
				}			
			}
						
		}		
		
	}
}

//启动炮弹位置更新线程 
void bomb_location_update()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bomb, NULL);
}


炮弹图标刷新函数
void show_bomb(Bomb bomb)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{
	int x,y;	
	x=bomb.location.x;
	y=bomb.location.y;
	hide();//隐藏光标
	gotoxy(x,y);
	ColorCout(bomb.icon,bomb.color);
		
} 

//发射子弹 
Bullet fire(Plane plane,Bullet bullet)
{
	game.bullet_move=true;
	game.fire=true;
	bullet.location.x=plane.location.x+plane_width/2;	
	bullet.location.y=plane.location.y-1;
	bullet.alive=true;		
	return bullet;			
}

//单个子弹初始化函数
Bullet init_bullet(Bullet bullet)
{
	bullet.icon=icon_bullet;
	bullet.location.x=0;
	bullet.location.y=b_b+7;
	bullet.alive=false;
	bullet.color=4;
	bullet.dam=1;
	bullet.hp=1;	
	return bullet;			
}

//所有子弹初始化函数 
void init_bullets(void)
{
	game.bullet_move=false;	
	for(int i=0;i<eq_plane;i++)
	{
		for(int j=0;j<eq_bullets_round;j++)
		{
			bullet[i][j]=init_bullet(bullet[i][j]);	
		}	
	}
}

//单个飞机初始化函数
Plane init_plane(Plane plane)
{
	plane.alive=true;	
	plane.hp=3;	
	plane.keycmd=none_cmd;
	plane.location=plocation[plane.No];
	plane.color=plane.No+1;
	plane.icon=plane.No+1;
	plane.rt=10;		
	return plane;
}



//所有飞机初始化函数 
void init_planes(void)
{
	for(int i=0;i<game.num_plane;i++)//刷新飞机图标
	{			
		plane[i]=init_plane(plane[i]);
	}
}


//子弹位置更新 线程 
void* thread_bullet(void* arg)
{
	while(1)
	{
		while(game.pause)			//等待游戏暂停指令 
		{
			;
		}
		
		while(game.complete)		//等待游戏通关 
		{
			show_game_complete();
		}
		
		while(game.gameover)		//等待游戏结束
		{
			show_game_over();
		}
		
		Sleep(game.bullet_interval);//子弹位置更新间隔 
		
		for(int i=0;i<eq_plane;i++)
		{
			if(plane[i].fire)			//如果飞机开火,要搜寻一个子弹处于死亡状态的位置激活子弹 
				{					
					game.bullet_move=true;	
					for(int j=0;j<eq_bullets_round;j++)
					{
						if(!bullet[i][j].alive)
						{
							bullet[i][j]=fire(plane[i],bullet[i][j]);
							break;
						}	
					}
					
					
				}
			plane[i].fire=false;		//飞机开火完成后,开火指令置0				
		}
		
		
		for(int i=0;i<eq_plane;i++)					//判断是哪架飞机发出的子弹与炮弹相撞 
		{
			for(int j=0;j<eq_bullets_round;j++)		//判断是哪个位置的子弹
			{				
				if(bullet[i][j].alive)				//判断条件为子弹处于激活状态 
				{
					bullet[i][j].location.y--;		//子弹向上移动一行 
					if(bullet[i][j].location.y<t_b)	//子弹冲出顶部边界 
					{
						bullet[i][j].hp=0;									
					}
					
					
					//此循环用于查询炮弹是否被子弹击中
					for(int k=0;k<game.bombs_round;k++) 
					{			
						if(shoot(bullet[i][j],bomb[k]))		//如果被击中 
						{
							game.bomb=true;					//爆炸声音启动 
							bomb[k].hp-=bullet[i][j].dam;	//炮弹血量减少值为子弹伤害值 
							bullet[i][j].hp-=bomb[k].dam;	//子弹血量减少值为炮弹伤害值 
							
							if(bomb[k].hp<=0)				//判断炮弹是否被击落 
							{
								plane[i].kill+=1;			//记录击落炮弹的飞机,该飞机杀敌数加1 
							}
						}
					}
					
					//如果子弹血量值降到0以下,则子弹初始化,等待下次被激活 
					if(bullet[i][j].hp<=0)					
					{
						bullet[i][j]=init_bullet(bullet[i][j]);									
					}				
					
								
				}
			}	
		}	
		
	}
}

//子弹位置更新 
void bullet_location_update()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bullet, NULL);
}


子弹图标刷新函数
void show_bullet(Bullet bullet)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{
	int x,y;	
	x=bullet.location.x;
	y=bullet.location.y;
	hide();//隐藏光标
	gotoxy(x,y);
	ColorCout(bullet.icon,bullet.color);		
} 

//********************************************************************************
 //以下函数功能:判断炮弹是否被飞机撞击 
//********************************************************************************
 bool collide(Plane plane,Bomb bomb)
{
	bool cpb,cx0,cx1,cy0,cy1;
	cx0=bomb.location.x>=plane.location.x-1;
	cx1=bomb.location.x<=plane.location.x+plane_width;
	cy0=bomb.location.y>=plane.location.y;
	cy1=bomb.location.y<=plane.location.y+plane_height; 
	
	cpb=cx0 && cx1 && cy0 && cy1;
	return cpb;
}

//********************************************************************************
 //以下函数功能:判断炮弹是否被子弹击中 
//********************************************************************************
 bool shoot(Bullet bullet,Bomb bomb)
{
	bool sbb,sx0,sx1,sy0,sy1;
	sx0=(bomb.location.x>=bullet.location.x-1);
	sx1=(bomb.location.x<=bullet.location.x+1);	
	sy0=(bomb.location.y>=bullet.location.y-1);
	sy1=(bomb.location.y<=bullet.location.y+1);	
	sbb=sx0 &&sx1&&sy0&&sy1;
	return sbb;
}

//********************************************************************************
 //以下三个函数为播放背景音乐功能 
//********************************************************************************
 
 //播放一遍背景音乐 
 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* thread_bomb_music(void* arg) //
{ 
  while(1)
  {  	
  	if(game.bomb)
  	{
  		play_bomb_music();
		game.bomb=false;	
	}	  
  }
} 
 
//创建爆炸音效播放线程,开始循环播放音效 
void bomb_music()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bomb_music, NULL);
}

 //播放一遍爆炸音效 
 void play_bomb_music() {  
 
	mciSendString(TEXT("open bomb.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(2*1000);//2*1000意思是2秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL);   
}


//********************************************************************************
 //以下三个函数为播放机开火音效功能 
//********************************************************************************
//循环播放飞机开火音效线程函数 
void* thread_fire_music(void* arg) //
{ 
  while(1)
  {  	
  	if(game.fire)
  	{
  		play_fire_music();
		game.fire=false;	
	}	  
  }
} 
 
//创建飞机开火音效播放线程,开始循环播放音乐 
void fire_music()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_fire_music, NULL);
}

 //播放一遍飞机开火音效 
 void play_fire_music() {  
 
	mciSendString(TEXT("open fire.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(1500);//153*1000意思是153秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL);   
}


//显示游戏信息 
void show_info(void)
{
	gotoxy(loc_info.x,loc_info.y);
	ColorCout(cur_stage_text,game.info_color);
	ColorCout(game.cur_stage,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+2);
	ColorCout(remain_bombs_text,game.info_color);
	ColorCout(game.bombs_stage-game.cur_num_bomb,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+10);
	ColorCout(play0_kill_text,plane[0].color);
	ColorCout(plane[0].kill,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+12);
	ColorCout(play1_kill_text,plane[1].color);
	ColorCout(plane[1].kill,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+20);
	ColorCout(play0_life_text,plane[0].color);
	ColorCout(plane[0].life,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+22);
	ColorCout(play1_life_text,plane[1].color);
	ColorCout(plane[1].life,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+30);
	ColorCout(play0_rt_text,plane[0].color);
	ColorCout(plane[0].rt,game.info_color);
	
	gotoxy(loc_info.x,loc_info.y+32);
	ColorCout(play1_rt_text,plane[1].color);
	ColorCout(plane[1].rt,game.info_color);	
}

//显示游戏通关信息 
void show_game_complete(void)
{
	if(!game.show_complete)
	{
		game.show_complete=true;
		Sleep(100);  
		system("cls");
		show_info();
		gotoxy((l_b+r_b)/2-4,(t_b+b_b)/2);				
		ColorCout(game_complete_text,game.info_color);		
	}
}

//显示游戏结束信息 		
void show_game_over(void)
{	
	if(!game.show_gameover)
	{
		game.show_gameover=true;
		Sleep(100); 
		system("cls");
		show_info();
		gotoxy((l_b+r_b)/2-4,(t_b+b_b)/2);				
		ColorCout(game_over_text,game.info_color);		
	}
}


//********************************************************************************
 //以下两个函数为飞机复活时间计时功能 
//********************************************************************************
//飞机复活保护时间线程 
void* thread_plane_rt(void* arg) //
{ 
  while(1)
  {  	
	Sleep(1000); 
	for(int j=0;j<game.num_plane;j++)
	{
		if(plane[j].rt>0)
		{
			plane[j].rt-=1;
		}
	}
		
  }
} 
 
//创建飞机复活保护时间线程 
void plane_rt()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_plane_rt, NULL);
}

//取消最大化,最小化
void SizeGoAway() {
	SetWindowLongPtrA(
		GetConsoleWindow(),
		GWL_STYLE,
		GetWindowLongPtrA(GetConsoleWindow(), GWL_STYLE) & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
}

七、运行效果

 

 

 

八、不足之处

本系列博文到此告一段落。本程序对于专业做游戏的高手来说,都是雕虫小技,本人只是初学c++,随手做了一个小游戏,权当练习了。本游戏中,定有好多不足之处,欢迎给位大神随时指正。本程序最大的不足之处就是由于屏幕刷新引起的闪烁情况。经查阅资料,屏幕刷新引起的闪烁问题可以通过双缓存方式予以解决,本人已经测试过,这种方法只适用于字符是系统默认的白色,如果是彩色字符则无能为力。所以后续还需要继续探索其他方法,如有大神知道解决方法,也敬请赐教。

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

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

相关文章

数据结构(Java实现)-集合与时间和空间复杂度

什么是集合框架 Java 集合框架 Java Collection Framework &#xff0c;又被称为容器 container &#xff0c;是定义在 java.util 包下的一组接口 interfaces 和其实现类 classes 。 什么是数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之…

第6章 性能分析相关的CPU特性

性能分析的终极目标是找到性能瓶颈&#xff0c;并定位到与之相关的代码段。 性能剖析可以快速让人了解应用程序热点。有时&#xff0c;性能剖析是开发者解决性能问题的唯一手段&#xff0c;尤其是针对较高层次性能问题。然而&#xff0c;即使解决了所有的主要性能问题&#xf…

Spring练习---环境搭建步骤分析27,这里要找老师的素材

1、Spring现在各层都有解决方案了&#xff0c;web层有springMVC,Dao层有jdbcTemplate 1.1 纯静态的还得转换一下 2、老师给的素材在文件里了 3、导入的坐标导入到文件里 4、包结构给你讲一下&#xff0c;这个包是controller层&#xff1a; 4.1 Serive业务层&#xff0c;Dao数…

使用 Node.js 生成优化的图像格式

使用 Node.js 生成优化的图像格式 图像是任何 Web 应用程序的重要组成部分&#xff0c;但如果优化不当&#xff0c;它们也可能成为性能问题的主要根源。在本文中&#xff0c;我们将介绍如何使用 Node.js 自动生成优化的图像格式&#xff0c;并以最适合用户浏览器的格式显示它们…

QT 基本对话框

包括&#xff1a; 1.标准文件对话框 dialog.h #ifndef DIALOG_H #define DIALOG_H#include <QDialog> #include <QTextCodec> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QGridLayout> #include <QFr…

Vscode详细安装教程

Vscode官网下载 官网地址&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 通过链接可以直接跳转到下面的页面当中&#xff0c;支持的版本有Windows、Linux、Mac&#xff0c;可以选择适配自己电脑的版本&#xff0c;一般来说应该是Windows x64的。不要直接点W…

C++图形界面编程-MFC

C控制台程序是命令行黑框&#xff0c;如果要写一个图形界面&#xff0c;VS也提供了图形界面编程MFC。建项目的时候选如下选项&#xff1a; 类似于QT。 问&#xff1a;那么MFC项目的运行入口main()或WinMain()在哪里呢&#xff1f; 答&#xff1a;其实&#xff0c;在MFC应用程…

Kubernetes 使用 Rancher 管理

K8S集群管理工具 只能管理单个K8S集群 kubectl命令行管理工具 dashboard&#xff08;K8S官方的UI界面图形化管理工具&#xff09; &#xff08;管理多集群很麻烦&#xff0c;切换不同集群每次需要更改kube-config文件[kubectl配置文件]&#xff0c;如果kubeadm部署每次都需…

字符设备驱动实例(PWM和RTC)

目录 五、PWM 六、RTC 五、PWM PWM(Pulse Width Modulation&#xff0c;脉宽调制器)&#xff0c;顾名思义就是一个输出脉冲宽度可以调整的硬件器件&#xff0c;其实它不仅脉冲宽度可调&#xff0c;频率也可以调整。它的核心部件是一个硬件定时器&#xff0c;其工作原理可以用…

15.配置资源管理

文章目录 配置资源管理Secret陈述式创建声明式创建存储卷挂载变量引用创建tls类型创建dockerconfigjson类型安装docker&#xff0c;创建 harbor仓库pod节点设置 ConfigMap创建挂载目录挂载文件以环境变量引用通过打补丁的方式修改配置 总结 配置资源管理 Secret Secret 是用来…

项目实战 — 博客系统③ {功能实现}

目录 一、编写注册功能 &#x1f345; 1、使用ajax构造请求&#xff08;前端&#xff09; &#x1f345; 2、统一处理 &#x1f384; 统一对象处理 &#x1f384; 保底统一返回处理 &#x1f384; 统一异常处理 &#x1f345; 3、处理请求 二、编写登录功能 &#x1f345; …

Android 10.0 SystemServer进程读写sdcard权限的修改

1.前言 在10.0的系统开发中,在一些系统进程中,也就是在SystemServer的进程中,其中系统服务中会要求读写Sdcard的一些功能,然后 默认是没有读取sdcard权限的,而在app中可以申请sdcard读写权限在系统服务中就不能申请权限,接下来看怎么授权实现sdcard授权 如图: 2.Sy…

计算机网络(9) --- 数据链路层与MAC帧

计算机网络&#xff08;8&#xff09; --- IP与IP协议_哈里沃克的博客-CSDN博客IP与IP协议https://blog.csdn.net/m0_63488627/article/details/132155460?spm1001.2014.3001.5502 目录 1.MAC帧 1.MAC地址 2.MAC帧报头 3.资源碰撞 4.MTU 1.对IP协议的影响 2.对UDP协议…

【docker】基于dockerfile编写LNMP

目录 一、基础环境准备 二、部署nginx&#xff08;容器IP为172.18.0.10&#xff09; 1、整个Dockerfile文件内容 2、配置nginx.conf文件 3、构建镜像 ​编辑 三、部署mysql 1、整个Docker文件内容 2、准备my.conf文件 3、生成镜像 4、启动镜像容器 5、验证mysql 四、PH…

视频局部区域移动检测, 删除相似帧

视频局部区域移动检测, 删除相似帧 完整方案在本文最后, 不想听故事的直接跳转到完整方案即可 起因 老板的一个东西找不到了, 让查监控 场景 东西放在一个架子上, 由一个海康威视全天候录像的摄像头监控, 但是巧就巧在这个要找的东西被放在了摄像头的死角里, 正好被柜子的隔…

nginx crlf+xss漏洞组合拳

1.crlf漏洞概述 CRLF是指回车和换行符的组合&#xff0c;它们的十六进制编码分别为0x0d和0x0a。在HTTP协议中&#xff0c;HTTP头和HTTP正文之间使用两个CRLF来进行分隔。如果攻击者能够注入恶意的换行符&#xff0c;就能够向HTTP消息中插入恶意的代码或会话Cookie。CRLF漏洞通常…

Redis是如何保证高可用的?

Redis这种基于内存的关系型数据库我们在选用的时候就是考虑到它的快。而且可以很方便的实现诸如分布式锁、消息队列等功能。 笔者在前一段秋招面试的时候就被提问&#xff0c;“Redis是怎么保证高可用的&#xff1f;” 后续的子问题包含&#xff0c;集群模式是怎么实现的&…

HTTP连接管理

基础知识&#xff1a;非持久连接 HTTP初始时1.0版本在浏览器每一次向服务器请求完资源都会立即断开TCP连接&#xff0c;如果想要请求多个资源&#xff0c;就必须建立多个连接&#xff0c;这就导致了服务端和客户端维护连接的开销。 例如&#xff1a;一个网页中包含文字资源也包…

1、攻防世界第一天

1、网站目录下会有一个robots.txt文件&#xff0c;规定爬虫可以/不可以爬取的网站。 2、URL编码细则&#xff1a;URL栏中字符若出现非ASCII字符&#xff0c;则对其进行URL编码&#xff0c;浏览器将该请求发给服务端&#xff1b;服务端会可能会先对收到的url进行解码&#xff0…

完全二叉树O(1)插入

919. 完全二叉树插入器 - 力扣&#xff08;LeetCode&#xff09; 完全二叉树 是每一层&#xff08;除最后一层外&#xff09;都是完全填充&#xff08;即&#xff0c;节点数达到最大&#xff09;的&#xff0c;并且所有的节点都尽可能地集中在左侧。 设计一种算法&#xff0c…