今天介绍飞机发射子弹的内容。发射的子弹和敌方炮弹有些类似,炮弹和子弹死亡的时候不在屏幕出现,存活期内位置由系统确定,不再受玩家控制,都是是沿着直线匀速运动,定时更新等等。但也有很多不同的地方,区别如下。
目录
一、发射子弹与敌方炮弹的区别
(一)死亡期间位置不同
1、敌方炮弹
敌方炮弹死亡期间的位置x坐标是随机生成的,y坐标为游戏可见区域下一行。
2、飞机子弹
飞机子弹死亡期间的位置x坐标为0,y坐标为游戏可见区域下两行。
(二)存活期初始位置
1、敌方炮弹
敌方炮弹存活期初始位置x坐标与死亡期间一致,y坐标为0。
2、飞机子弹
飞机子弹存活期初始位置x坐标为飞机正中心,y坐标为飞机y坐标上一行。
(三)出现时间不同
1、敌方炮弹
敌方炮弹死亡后的复活时间是随机生成的,复活时间减少到0时,炮弹出现在屏幕上,出现时间由系统决定。
2、飞机子弹
飞机子弹是在飞机的开火键按下后出现的,出现时间由玩家控制。
(四)数量不同
1、敌方炮弹
敌方炮弹同一波数量在同一关是固定的,死亡一个复活一个。屏幕中出现的总的炮弹数量保持不变。在存放炮弹的数组中,数组的角标是固定不变的。
2、飞机子弹
飞机子弹是数量由玩家开火的频率确定,数量不固定。但是炮弹的数量有上限,每架飞机发射的子弹最密集为每行一个子弹,最少为0个。所以存放子弹数组的角标不固定,本例采用的方法为从头开始查询,哪个位置的子弹是死亡状态,那么飞机开火后的存活子弹就存放到这个位置,这个位置赋值为存活状态。当子弹冲出屏幕上方或者击中敌方炮弹,存放子弹的位置赋值为死亡状态。
(五)飞行方向不同
1、敌方炮弹
敌方炮弹从屏幕上方向下飞行,y坐标为自增1。
2、飞机子弹
飞机子弹从飞机前端向屏幕上方飞行,y坐标为自减1。
二、程序代码
(一)新增子弹相关代码
1、构造Bullet结构体
//定义子弹结构体
typedef struct{
Location location; //子弹位置
bool alive; //子弹是否存活
int color; //子弹颜色
string icon; //子弹图标
int hp; //hp=hit point 生命值,子弹击中炮弹或冲出屏幕上方hp直接降为0,子弹死亡
int dam; //dam=damage 伤害值
int type; //子弹类型
}Bullet;
2、新增子弹相关函数
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); //显示子弹图标
(二)最终程序代码
1、主程序
#include "control_plane.h"
#include "quenue.h"
using namespace std;
Plane plane[eq_plane];
Game game;
Bomb bomb[eq_bombs_round];
Bullet bullet[eq_plane][eq_bullets_round];
int main(int argc, char** argv) {
init(); //初始化
bgmusic();//播放背景音乐
getkey();
bomb_location_update();
bullet_location_update();
while(1) //循环等待键盘指令
{
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");
for(int i=0;i<game.num_plane;i++)
{
show_plane(plane[i]); //刷新飞机图标
}
for(int i=0;i<game.bombs_round;i++)
{
if(bomb[i].alive)
{
show_bomb(bomb[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]);
}
}
}
}
}
return 0;
}
2、头文件
#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 100 //图形显示区域右侧边界
#define b_b 20 //图形显示区域下侧边界
#define plane_width 9
#define plane_height 6
#define eq_plane 2 //飞机架数
#define eq_bombs_round 23 //eq=end quantity最终炮弹数量
#define eq_rt 10 //复活最大时间
#define eq_bullets_round 20 //eq=end quantity飞机一次发射最多子弹数量
//定义飞机造型
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 stage; //游戏当前关
int bombs_round; //敌方每轮发射炮弹数量
int bombs_stage; //每关总计出现炮弹数量
bool clear; //游戏过关
bool complete; //游戏通关
bool gameover; //游戏结束
int num_plane; //飞机数量
int cur_num_bomb; //当前已发射炮弹数量
int bomb_interval; //位置更新间隔
bool bomb_move; //炮弹是否移动
bool bullet_move;
}Game;
//定义飞机结构体
typedef struct{
Location location;
int color;
int icon;
direction_cmd keycmd;
bool fire;
int cnt_bullets; //按一次开火键加1,然后初始化bullet[cnt_bullets],子弹死亡一个减1
}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];
//声明刷新飞机位置函数
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 getkey();
//输出彩色字符函数
template<typename T> //T表示任何可以被cout输出的类型
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0);
void init_bombs(void);
Bomb init_(Bomb bomb);
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); //显示子弹图标
#endif
3、库函数
#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 init(void)
{
plane[0].location={2*r_b/3,b_b};
plane[1].location={r_b/3,b_b};
plane[0].color=1;
plane[1].color=2;
plane[0].icon=1;
plane[1].icon=2;
srand(time(NULL));
game.num_plane=2;
game.bombs_round=3;
game.bomb_move=false;
game.bullet_move=false;
game.bomb_interval=1000;
init_bombs();
init_bullets();
system("cls");
for(int i=0;i<game.num_plane;i++)//刷新飞机图标
{
show_plane(plane[i]);
plane[i].keycmd=none_cmd;
}
// game.num_plane=2;
game.bombs_round=3;
hide();//隐藏光标
}
//********************************************************************************
//以下三个函数为获得按键指令线程函数
//********************************************************************************
void* thread_key(void* arg)
{
while(1)
{
Sleep(60); //获取指令延时一定时间,起滤波作用,延缓获取指令的响应速度
key(); //获取按键指令
plane_location_update() ;//获取完指令马上更新飞机坐标
}
}
void getkey()
{
pthread_t tid;
pthread_create(&tid, NULL, thread_key, NULL);
}
//获取键盘指令函数
void key(void)
{
direction_cmd c=none_cmd;
direction_cmd d=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;
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[0].keycmd=c;
plane[1].keycmd=d;
if (GetAsyncKeyState('P') & 0x8000) plane[0].fire = true;
if (GetAsyncKeyState('F') & 0x8000) plane[1].fire = true;
}
void gotoxy(int x, int y) {
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出设备句柄
SetConsoleCursorPosition(hOut, pos);//两个参数分别指定哪个窗口,具体位置
}
//飞机图标刷新函数
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)
{
bomb.location.x=rand()%r_b;
bomb.location.y=b_b+6;
bomb.icon=icon_bomb;
bomb.color=6;
bomb.dam=1;
bomb.hp=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]);
}
}
//炮弹位置更新 线程
void* thread_bomb(void* arg)
{
while(1)
{
Sleep(game.bomb_interval);
game.bomb_move=true;
for(int i=0;i<game.bombs_round;i++)
{
if(bomb[i].alive)
{
bomb[i].location.y++;
}
else
{
bomb[i].rt--;
if(bomb[i].rt<=0)
{
bomb[i].alive=true;
bomb[i].location.y=0;
}
}
if(bomb[i].location.y>b_b+5)
{
bomb[i].hp=0;
}
if(bomb[i].hp<=0)
{
bomb[i]=init_bomb(bomb[i]);
}
}
}
}
//炮弹位置更新
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;
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]);
}
}
}
//子弹位置更新 线程
void* thread_bullet(void* arg)
{
while(1)
{
Sleep(game.bomb_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;
}
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<0)
{
bullet[i][j].hp=0;
}
if(bullet[i][j].hp<=0)
{
//bullet[i][j].alive=false;
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);
}
//********************************************************************************
//以下三个函数为播放背景音乐功能
//********************************************************************************
//播放一遍背景音乐
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);
}
三、运行效果
运行效果如下,注意本节只介绍了子弹实现的方法,后边会介绍子弹与炮弹相撞、炮弹与飞机相撞的算法。
(未完待续)