字符基础——简易版飞机大战

使用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;
} 

运行效果截图:

猜你喜欢

转载自blog.csdn.net/A1344714150/article/details/85637068