使用C/C++开发一个字符版的飞机大战,玩家可以上下左右移动,空格键开火,子弹为激光,敌机可以自我移动,玩家发射的激光如果击中敌机,绘画简易版爆炸效果。
开发环境:Dev-C++ 5
介绍:使用表示主角飞机,使用表示敌机,使用表示敌机被击中的形态,使用表示正在开火的主角飞机,"|"表示激光。当敌机被击中后,分数score++并且在一个随机的位置生成新的敌机,这里限制了敌机只能在第0行到第4行以及第0列到第width-3列交错的区域内移动,通过多次循环移动一次敌机,确保了玩家飞机的机动性更强。
制作过程:
使用x、y变量记录主角飞机的左上角顶点位于第x行第y列;
使用target_x、target_y变量记录敌机的左上角顶点位于第target_x行第target_y列;
使用target_x_v、target_y_v变量记录敌机垂直方向和水平方向的速度;
使用isFire记录玩家是否按下了开火键,如果玩家按空格键开火,将1赋值给isFire变量,当发射完激光后,将0重新赋值回isFire;
使用score记录分数,击中敌机,分数+1;
使用input记录玩家按下的按键,使用getch()可以不按下回车就触发按键事件;
使用width和height记录游戏区域共有width列height行;
使用count记录第几次循环了,每到第五次循环,敌机进行一次移动。
int i,j;
int x = 15;
int y = 10;
int score = 0;
int isFire = 0;
int target_x = 1;//敌机点坐标
int target_y = 1;
int target_x_v = 1;//敌机纵向速度,值为正数向下移动,值为负数向上移动
int target_y_v = 1; //敌机横向速度,值为正数向右移动,值为负数向左移动
int width = 40;
int height = 22;
int count = 0;//每循环五次,敌机移动一次
char input;
由于使用传统的system("cls")清屏会有严重的闪烁效果,这里使用windows.h下的SetConsoleCursorPosition(Handle handle,COORD pos)来将光标进行移动pos点处,每次循环前先执行下该方法,从而实现清屏效果。
//将光标移动到(x,y)处,实现清屏效果
void goToXY(int x,int y){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle,pos);
}
除此之外,光标的闪烁效果也是比较严重的,这里可以使用HideCursor()函数对光标进行隐藏。
//隐藏光标
void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
重点说下打印玩家飞机和敌机以及碰撞检测:
先使用双层for循环对游戏区域的信息进行打印,i表示遍历到第几行,j表示遍历到第几列;如果当前敌机刚好位于第j列,那么根据当前打印的行数和敌机各部分所在的行数进行对比,假如当前行数是敌机第1行,就打印敌机第一行,注意使用空格对输出的空白处进行填充,打印玩家飞机同理;
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" @ ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"@@@";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j<width)
cout<<" ";
}
cout<<endl;
}
在这里为了简便起见,玩家发射的是即时性的激光,根据前面的截图可以看到玩家飞机有3行5列,敌机有2行3列,玩家按了空格之后,进入到开火事件中。玩家飞机发射的激光是在最中间一列发射出去的,即激光所在列数 = 玩家左上角列数y+2;如果
敌机左上角y列数<=激光所在列数<=敌机左上角y列数+2,说明激光打中了敌机;
碰撞检测化简得到:敌机左上角y列数-2<=主角左上角y列数<=敌机左上角y列数时,激光射中敌机;
如果激光射中敌机,需要修改敌机的显示状态为"-",并且随机生成新的敌机位置和移动方向;
开火完毕后记得把isFire修改回未开火状态。
if(isFire){
if(target_y-2<=y&&y<=target_y){//激光射中敌机
score++;
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" - ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"---";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j==y+2&&i<x){
cout<<"|";
}else{
cout<<" ";
}
}
cout<<endl;
}
//重置敌机坐标,随机移动方向
target_x = rand()%5;
target_y = rand()%(width-2);
int flag1 = rand()%2;
int flag2 = rand()%2;
if(flag1)
target_x_v = - target_x_v;
if(flag2)
target_y_v = - target_y_v;
}else{//激光未射中敌机
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" @ ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"@@@";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j==y+2&&i<x){
cout<<"|";
}else{
cout<<" ";
}
}
cout<<endl;
}
}
isFire = 0;
}
打印分数,对敌机进行指定方向的移动,在碰到边界时,调整速度方向:
for(i=0;i<2;i++)
cout<<endl;
cout<<"score:"<<score<<endl;
if(count==5){//每循环五次移动一次敌机
if(target_x==4&&target_x_v>0){//敌机到第4行时往回走
target_x_v = -target_x_v;
}
if(target_x==0&&target_x_v<0){//敌机到第0行时往回走
target_x_v = -target_x_v;
}
if(target_y==width-3&&target_y_v>0){//敌机到第width-3列时往回走
target_y_v = -target_y_v;
}
if(target_y==0&&target_y_v<0){//敌机到第0列时往回走
target_y_v = -target_y_v;
}
target_x += target_x_v;//按照垂直速度和水平速度修改敌机坐标
target_y += target_y_v;
count = 0;//重置循环次数
}
if(kbhit()){
input = getch();
if(input == 'a'&&y>0){
y--;
}
if(input == 'd'&&y<width-5){
y++;
}
if(input == 'w'&&x>0){
x--;
}
if(input == 's'&&x<height-3){
x++;
}
if(input == ' '){
isFire = 1;
}
}
Sleep(500);
count++;
}
完整源码
#include<iostream>
#include<stdlib.h>
#include<conio.h>
#include<windows.h>
using namespace std;
//将光标移动到(x,y)处,实现清屏效果
void goToXY(int x,int y){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle,pos);
}
//隐藏光标
void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
int main(){
int i,j;
int x = 15;
int y = 10;
int score = 0;
int isFire = 0;
int target_x = 1;//敌机点坐标
int target_y = 1;
int target_x_v = 1;//敌机纵向速度,值为正数向下移动,值为负数向上移动
int target_y_v = 1; //敌机横向速度,值为正数向右移动,值为负数向左移动
int width = 40;
int height = 22;
int count = 0;//每循环五次,敌机移动一次
char input;
HideCursor();
//打中目标,碰撞检测:敌机y<=主角y+2<=敌机y+2;化简得: 敌机y-2<=主角y<=敌机y时,激光射中敌机
while(1){
// system("cls");
goToXY(0,0);
if(isFire){
if(target_y-2<=y&&y<=target_y){//激光射中敌机
score++;
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" - ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"---";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j==y+2&&i<x){
cout<<"|";
}else{
cout<<" ";
}
}
cout<<endl;
}
//重置敌机坐标,随机移动方向
target_x = rand()%5;
target_y = rand()%(width-2);
int flag1 = rand()%2;
int flag2 = rand()%2;
if(flag1)
target_x_v = - target_x_v;
if(flag2)
target_y_v = - target_y_v;
}else{//激光未射中敌机
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" @ ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"@@@";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j==y+2&&i<x){
cout<<"|";
}else{
cout<<" ";
}
}
cout<<endl;
}
}
isFire = 0;
}else{//未发射激光
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(i==target_x&&j==target_y){
//绘画敌机第一行
cout<<" @ ";
j+=3;
}else if(i==target_x+1&&j==target_y){
//绘画敌机第二行
cout<<"@@@";
j+=3;
}else if(i==x&&j==y){
//绘画主角第一行
cout<<" * ";
j+=5;
}else if(i==x+1&&j==y){
//绘画主角第二行
cout<<"*****";
j+=5;
}else if(i==x+2&&j==y){
//绘画主角第三行
cout<<" * * ";
j+=5;
}
if(j<width)
cout<<" ";
}
cout<<endl;
}
}
for(i=0;i<2;i++)
cout<<endl;
cout<<"score:"<<score<<endl;
if(count==5){//每循环五次移动一次敌机
if(target_x==4&&target_x_v>0){//敌机到第4行时往回走
target_x_v = -target_x_v;
}
if(target_x==0&&target_x_v<0){//敌机到第0行时往回走
target_x_v = -target_x_v;
}
if(target_y==width-3&&target_y_v>0){//敌机到第width-3列时往回走
target_y_v = -target_y_v;
}
if(target_y==0&&target_y_v<0){//敌机到第0列时往回走
target_y_v = -target_y_v;
}
target_x += target_x_v;//按照垂直速度和水平速度修改敌机坐标
target_y += target_y_v;
count = 0;
}
if(kbhit()){//如果玩家有按键输入
input = getch();
if(input == 'a'&&y>0){
y--;
}
if(input == 'd'&&y<width-5){
y++;
}
if(input == 'w'&&x>0){
x--;
}
if(input == 's'&&x<height-3){
x++;
}
if(input == ' '){
isFire = 1;
}
}
Sleep(20);
count++;
}
return 0;
}
运行效果截图: