字元基礎——簡易版飛機大戰
使用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;
}
執行效果截圖: