还是为了更好对学习的知识进行巩固,所以又基于ubuntu的终端写了坦克大战的游戏。此坦克大战只有一张地图,玩家的坦克需要击败所有敌方的坦克才能胜利,而敌方杀死玩家坦克或者大本营被敌方攻克玩家都算失败。
本代码中用到的c++新知识是list容器。
本代码中的敌方坦克存放在一个list容器中,所有子弹也是存放在一个list容器中,通过采用对敌方坦克的行为赋以不同概率来实现敌方坦克的行动。
具体代码如下:
TankWar.h
#ifndef TANK_WAR_H
#define TANK_WAR_H
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <pthread.h>
#include <vector>
#include <list>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include <linux/input.h>
#include <stdexcept>
using namespace std;
enum {DIR_UP=0,DIR_DOWN,DIR_LEFT,DIR_RIGHT}; //坦克及炮弹朝向
enum {EMPTY=0,WALL,NORMAL_BULLET,LIGHT_BULLET,TANK,BOSS}; //地图上的所有可能的单元
enum {NORMAL=0,LIGHT}; //炮弹类型:普通炮弹,激光
enum {LEFT_BORN=0,CENTER_BORN,RIGHT_BORN}; //敌人出现的地点:左上角 中间上方 右上角
//炮弹
typedef struct Bullet
{
//炮弹坐标
int x;
int y;
short type;//炮弹类型
short direction;//炮弹朝向
} Bullet;
//坦克
typedef struct Tank
{
//坦克中心坐标
int centX;
int centY;
//坦克使用的炮弹类型
short bulletType;
//坦克的朝向
short direction;
} Tank;
//大本营的boss
typedef struct Boss
{
//boss坐标
int centX;
int centY;
//判断boss是否存活
bool alive;
} Boss;
class TankWar
{
private:
Tank theGod; //英雄坦克
Boss boss; //大本营
list<Bullet > bull; //保存所有炮弹的list容器
list<Tank > enemy; //保存所有敌方坦克的list容器
bool gameEnd; //游戏结束标记
short map[100][100]; //游戏区域
unsigned char screenWidth,screenHeight; //游戏区域宽度和高度
short EnemyNumOnScreen; //在游戏区域内的敌人数量
short EnemyNumUnderScreen; //还能上战场的敌人数量
public:
TankWar(unsigned char=30 ,unsigned char = 30,short = 20); //构造函数
void GameStart(); //开始游戏
private:
void InitMap(); //初始化地图
void GameOverNotice(string); //游戏结束提示
void TankMove(Tank&,short); //移动坦克
void MoveLeft(Tank&); //向左移动坦克
void MoveRight(Tank&); //向右移动坦克
void MoveUp(Tank&); //向上移动坦克
void MoveDown(Tank&); //向下移动坦克
void CreatEnemy(); //生成一个敌人
bool BornPlaceOk(short); //判断敌人出生点是否适合敌人出生
void CreatTankGod(); //创造英雄坦克
void CreatBoss(); //创造大本营
void ShowScreen(); //打印当前屏幕
void RedescribeTank(Tank&,short); //重新构成坦克
void KeyControl(); //按键控制
void EnemyControl(); //控制敌人
void BulletControl(); //控制子弹
void TankShoot(Tank&); //坦克射击
bool MoveBullet(Bullet&); //移动炮弹
void TankAutoRun(Tank&); //敌方坦克自行移动
bool TargetInFront(Tank&); //判断前方是否有攻击目标
void CloseToGodTank(Tank&); //向英雄坦克靠近
void CloseToHome(Tank&); //向大本营靠近
private:
static void *_Key_Control(void *);
static void *_Enemy_Control(void *);
static void *_Bullet_Control(void *);
private:
inline void EraseCurrentTank(Tank tank) //擦除当前坦克在地图上的痕迹
{
for(int r = tank.centY-1;r<=tank.centY+1;r++)
{
for(int c=tank.centX-1;c <= tank.centX+1;c++)
{
map[c][r] = EMPTY;
}
}
}
inline void EraseBoss(Boss &boss) //擦除大本营
{
for(int r = boss.centY-1;r<=boss.centY+1;r++)
{
for(int c=boss.centX-1;c<=boss.centX+1;c++)
{
map[c][r] = EMPTY;
}
}
}
};
#endif
TankWar.cpp
#include <iostream>
#include "TankWar.h"
TankWar::TankWar(unsigned char width, unsigned char height,short enemyNum)
{
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
map[j][i] = EMPTY;
}
}
srand(time(0));
EnemyNumOnScreen = 0;
EnemyNumUnderScreen = enemyNum;
screenWidth = width;
screenHeight = height;
CreatBoss();
CreatTankGod();
InitMap();
ShowScreen();
}
void TankWar::InitMap()
{
//BOSS外围修筑保护墙
for(int r = boss.centY-2;r<= boss.centY+1;r++)
map[boss.centX-2][r] = WALL;
for(int r = boss.centY-2;r<= boss.centY+1;r++)
map[boss.centX+2][r] = WALL;
for(int c = boss.centX-1;c<= boss.centX+1;c++)
map[c][boss.centY-2] = WALL;
//战场区域砖块
for(int r = 6;r<=screenHeight - 8;r++)
{
for(int c=0;c<screenWidth;c++)
{
map[c][r] = WALL;
}
}
}
void TankWar::GameStart()
{
gameEnd = false;
pthread_t id_key_ctl = 1,id_enemy = 2, id_bullet = 3;
pthread_create(&id_key_ctl,NULL,&_Key_Control,this);
pthread_create(&id_enemy,NULL,&_Enemy_Control,this);
pthread_create(&id_bullet,NULL,&_Bullet_Control,this);
while(!gameEnd)
{
if(EnemyNumUnderScreen + EnemyNumOnScreen == 0)
gameEnd = true;
if(boss.alive == false)
gameEnd = true;
}
pthread_join(id_key_ctl,NULL);
pthread_join(id_enemy,NULL);
pthread_join(id_bullet,NULL);
if(boss.alive) GameOverNotice("You Win");
else GameOverNotice("You Loss");
}
void TankWar::ShowScreen()
{
system("clear");
for(int i=0;i<screenWidth+2;i++)
cout<<"_";
cout<<endl;
for(int r=0;r < screenHeight;r++)
{
cout << "|";
for(int c=0;c < screenWidth;c++)
{
switch(map[c][r])
{
case EMPTY: cout<<" ";break;
case WALL: cout<< "#";break;
case NORMAL_BULLET:cout<<"*";break;
case LIGHT_BULLET:cout<<"|";break;
case TANK: cout<<"+";break;
case BOSS: cout<<"^";break;
}
}
cout << "|" << endl;
}
for(int i=0;i<screenWidth+2;i++)
cout<< "-";
cout << endl;
cout << "Enemy: "<< EnemyNumUnderScreen<<endl;
};
void TankWar::CreatBoss()
{
boss.centX = screenWidth/2;
boss.centY = screenHeight - 2;
for(int r = boss.centY-1;r<=boss.centY+1;r++)
{
for(int c = boss.centX-1;c<=boss.centX+1;c++)
map[c][r] = BOSS;
}
boss.alive = true;
}
void TankWar::CreatTankGod()
{
theGod.centX = boss.centX-4;
theGod.centY = boss.centY;
theGod.direction = DIR_UP;
theGod.bulletType = LIGHT;
for(int r = theGod.centY;r<=theGod.centY+1;r++)
{
for(int c=theGod.centX-1;c<=theGod.centX+1;c++)
map[c][r] = TANK;
}
map[theGod.centX][theGod.centY-1] = TANK;
}
void TankWar::MoveLeft(Tank& tank)
{
if(tank.centX-2 < 0 && tank.direction == DIR_LEFT) return;
for(int r = tank.centY-1;r <= tank.centY+1;r++)
{
if(map[tank.centX-2][r] != EMPTY && tank.direction == DIR_LEFT) return;
}
EraseCurrentTank(tank);
if(tank.direction == DIR_LEFT) tank.centX--;
RedescribeTank(tank,DIR_LEFT);
}
void TankWar::MoveRight(Tank& tank)
{
if(tank.centX+2 == screenWidth && tank.direction == DIR_RIGHT) return;
for(int r = tank.centY-1;r <= tank.centY+1;r++)
{
if(map[tank.centX+2][r] != EMPTY && tank.direction == DIR_RIGHT) return;
}
EraseCurrentTank(tank);
if(tank.direction == DIR_RIGHT) tank.centX++;
RedescribeTank(tank,DIR_RIGHT);
}
void TankWar::MoveUp(Tank& tank)
{
if(tank.centY-2 < 0 && tank.direction == DIR_UP) return;
for(int c = tank.centX-1;c <= tank.centX+1;c++)
{
if(map[c][tank.centY-2] != EMPTY && tank.direction == DIR_UP) return;
}
EraseCurrentTank(tank);
if(tank.direction == DIR_UP) tank.centY--;
RedescribeTank(tank,DIR_UP);
}
void TankWar::MoveDown(Tank& tank)
{
if(tank.centY+2 == screenHeight && tank.direction == DIR_DOWN) return;
for(int c = tank.centX-1;c <= tank.centX+1;c++)
{
if(map[c][tank.centY+2] != EMPTY && tank.direction == DIR_DOWN) return;
}
EraseCurrentTank(tank);
if(tank.direction == DIR_DOWN) tank.centY++;
RedescribeTank(tank,DIR_DOWN);
}
void TankWar::TankShoot(Tank& tank)
{
Bullet b;
switch(tank.direction)
{
case DIR_UP: b.x = tank.centX; b.y = tank.centY-2;break;
case DIR_DOWN: b.x = tank.centX; b.y = tank.centY+2;break;
case DIR_LEFT: b.x = tank.centX-2;b.y = tank.centY;break;
case DIR_RIGHT:b.x = tank.centX+2;b.y = tank.centY;break;
}
b.type = tank.bulletType;
b.direction = tank.direction;
bull.push_back(b);
}
void TankWar::RedescribeTank(Tank& tank,short direction)
{
for(int r = tank.centY-1;r <= tank.centY+1;r++)
{
for(int c = tank.centX-1;c<=tank.centX+1;c++)
{
map[c][r] = TANK;
}
}
switch(direction)
{
case DIR_UP:map[tank.centX-1][tank.centY-1] = EMPTY;
map[tank.centX+1][tank.centY-1] = EMPTY;
break;
case DIR_DOWN:map[tank.centX-1][tank.centY+1] = EMPTY;
map[tank.centX+1][tank.centY+1] = EMPTY;
break;
case DIR_LEFT:map[tank.centX-1][tank.centY-1] = EMPTY;
map[tank.centX-1][tank.centY+1] = EMPTY;
break;
case DIR_RIGHT:map[tank.centX+1][tank.centY-1] = EMPTY;
map[tank.centX+1][tank.centY+1] = EMPTY;
break;
}
tank.direction = direction;
}
void *TankWar::_Key_Control(void *param)
{
TankWar *p = (TankWar *)param;
p->KeyControl();
}
void *TankWar::_Enemy_Control(void *param)
{
TankWar *p = (TankWar *)param;
p->EnemyControl();
}
void *TankWar::_Bullet_Control(void *param)
{
TankWar *p = (TankWar *)param;
p->BulletControl();
}
void TankWar::KeyControl()
{
struct input_event ev_key;
int result = open("/dev/input/event3",O_RDONLY);
if(result < 0)
{
cout << "Link to keyboard fail!" <<endl;
exit(0);
}
while(!gameEnd)
{
int count = read(result,&ev_key,sizeof(struct input_event));
if(ev_key.type == EV_KEY && ev_key.value == 0)
{
switch(ev_key.code)
{
case KEY_W:MoveUp(theGod);break;
case KEY_A:MoveLeft(theGod);break;
case KEY_S:MoveDown(theGod);break;
case KEY_D:MoveRight(theGod);break;
case KEY_J:TankShoot(theGod);break;
case KEY_Q:gameEnd = true;break;
}
}
ShowScreen();
}
close(result);
}
void TankWar::EnemyControl()
{
while(!gameEnd)
{
if(EnemyNumUnderScreen > 0 && EnemyNumOnScreen < 3)
{
CreatEnemy();
}
for(list<Tank>::iterator enm = enemy.begin();enm != enemy.end();enm++)
{
int oper = 0;
if(TargetInFront(*enm))
{
oper = rand()%100;
if(oper < 50)
{
TankAutoRun(*enm);
}
else
{
TankShoot(*enm);
}
}
else
{
oper = rand()%100;
if(oper < 40)
{
CloseToGodTank(*enm);
}
else if(oper < 80)
{
CloseToHome(*enm);
}
else
{
TankAutoRun(*enm);
}
}
}
clock_t startTime = clock();
while(clock() < (startTime + 1000000)) ;
ShowScreen();
}
}
void TankWar::CreatEnemy()
{
Tank enm = {0};
enm.centY = 1;
enm.bulletType = NORMAL;
enm.direction = DIR_DOWN;
if(BornPlaceOk(LEFT_BORN))
{
enm.centX = 1;
}
else if(BornPlaceOk(CENTER_BORN))
{
enm.centX = screenWidth/2;
}
else if(BornPlaceOk(RIGHT_BORN))
{
enm.centX = screenWidth-2;
}
else return ;
enemy.push_back(enm);
for(int r=enm.centY-1;r<=enm.centY+1;r++)
{
for(int c=enm.centX-1;c<=enm.centX+1;c++)
{
map[c][r] = TANK;
}
}
map[enm.centX-1][enm.centY+1] = EMPTY;
map[enm.centX+1][enm.centY+1] = EMPTY;
EnemyNumOnScreen++;
EnemyNumUnderScreen--;
}
bool TankWar::BornPlaceOk(short place)
{
int x = 0,y = 1;
switch(place)
{
case LEFT_BORN: x = 1;break;
case CENTER_BORN: x = screenWidth/2;break;
case RIGHT_BORN: x = screenWidth-2;break;
}
for(int r = y-1;r<= y+1;r++)
{
for(int c = x-1;c <= x+1;c++)
{
if(map[x][y] != EMPTY) return false;
}
}
return true;
}
void TankWar::BulletControl()
{
while(!gameEnd)
{
try
{
for(list<Bullet>::iterator b = bull.begin();b != bull.end();b++)
{
if(!MoveBullet(*b))
{
bull.erase(b);
b--;
}
}
}
catch(runtime_error err)
{
cerr<<err.what()<<endl;
}
clock_t startTime = clock();
while(clock() < (startTime + 200000)) ;
ShowScreen();
}
}
bool TankWar::MoveBullet(Bullet& bullet)
{
Bullet b = bullet;
if(map[b.x][b.y] != NORMAL_BULLET && map[b.x][b.y] != LIGHT_BULLET && map[b.x][b.y] != EMPTY)
{
map[b.x][b.y] = EMPTY;
return false;
}
map[b.x][b.y] = EMPTY;
switch(bullet.direction)
{
case DIR_UP: b.y--;break;
case DIR_DOWN: b.y++;break;
case DIR_LEFT: b.x--;break;
case DIR_RIGHT:b.x++;break;
}
if(b.x < 0 || b.x == screenWidth ||b.y < 0||b.y == screenHeight) return false;
switch(map[b.x][b.y])
{
case WALL: map[b.x][b.y] = EMPTY; return false;
case NORMAL_BULLET:
case LIGHT_BULLET:
map[b.x][b.y] = EMPTY;
for(list<Bullet>::iterator bl = bull.begin();bl != bull.end();bl++)
{
if((*bl).x == b.x &&(*bl).y == b.y)
{
bull.erase(bl);
return false;
}
}
case TANK:
for(list<Tank>::iterator enm = enemy.begin();enm != enemy.end();enm++)
{
if((*enm).centX-1 <= b.x && (*enm).centX+1 >=b.x && (*enm).centY-1<=b.y &&(*enm).centY +1 >= b.y)
{
EraseCurrentTank(*enm);
enemy.erase(enm);
EnemyNumOnScreen--;
return false;
}
}
EraseCurrentTank(theGod);
boss.alive = false;
return false;
case BOSS:
EraseBoss(boss);
boss.alive = false;
return false;
}
bullet = b;
switch(bullet.type)
{
case NORMAL: map[bullet.x][bullet.y] = NORMAL_BULLET;break;
case LIGHT: map[bullet.x][bullet.y] = LIGHT_BULLET;break;
}
return true;
}
void TankWar::TankAutoRun(Tank& tank)
{
unsigned char count = 0;
unsigned char dir[4] = {0}; //保存坦克可以移动的方向,若该方向为可移动,保存为1
unsigned char arr[4] = {0};
if(tank.centY-2 > 0 && map[tank.centX-1][tank.centY-2] == EMPTY &&map[tank.centX][tank.centY-2]==EMPTY && map[tank.centX+1][tank.centY-2] == EMPTY)
{
dir[DIR_UP] = 1;
arr[count++] = DIR_UP;
}
if(tank.centX+2 < screenWidth && map[tank.centX+2][tank.centY-1] == EMPTY &&map[tank.centX+2][tank.centY]==EMPTY && map[tank.centX+2][tank.centY+1] == EMPTY)
{
dir[DIR_RIGHT] = 1;
arr[count++] = DIR_RIGHT;
}
if(tank.centX-2 > 0 && map[tank.centX-2][tank.centY-1] == EMPTY &&map[tank.centX-2][tank.centY]==EMPTY && map[tank.centX-2][tank.centY+1] == EMPTY)
{
dir[DIR_LEFT] = 1;
arr[count++] = DIR_LEFT;
}
if(tank.centY+2 < screenHeight && map[tank.centX-1][tank.centY+2] == EMPTY &&map[tank.centX][tank.centY+2]==EMPTY && map[tank.centX+1][tank.centY+2] == EMPTY)
{
dir[DIR_DOWN] = 1;
arr[count++] = DIR_DOWN;
}
if(count == 0) return ;
short oper = rand()%count;
TankMove(tank,oper);
}
void TankWar::TankMove(Tank& tank,short dir)
{
switch(dir)
{
case DIR_LEFT: MoveLeft(tank);break;
case DIR_RIGHT:MoveRight(tank);break;
case DIR_UP: MoveUp(tank);break;
case DIR_DOWN: MoveDown(tank);break;
}
}
bool TankWar::TargetInFront(Tank& tank)
{
if(tank.direction == DIR_UP)
{
for(int i=tank.centY - 2;i>=0;i--)
{
if(tank.centX <= theGod.centX+1 &&tank.centX>=theGod.centX-1 &&tank.centY >= theGod.centY)
return true;
for(int j=tank.centX-1;j<=tank.centX+1;j++)
{
if(map[j][i] == WALL || map[j][i]==NORMAL_BULLET || map[j][i]==LIGHT_BULLET || map[j][i] == BOSS)
return true;
}
}
return false;
}
if(tank.direction == DIR_DOWN)
{
for(int i=tank.centY + 2;i<screenHeight;i++)
{
if(tank.centX <= theGod.centX+1 &&tank.centX>=theGod.centX-1 &&tank.centY <= theGod.centY)
return true;
for(int j=tank.centX-1;j<=tank.centX+1;j++)
{
if(map[j][i] == WALL || map[j][i]==NORMAL_BULLET || map[j][i]==LIGHT_BULLET || map[j][i] == BOSS)
return true;
}
}
return false;
}
if(tank.direction == DIR_LEFT)
{
for(int i=tank.centX - 2;i>=0;i--)
{
if(tank.centY <= theGod.centY+1 &&tank.centY>=theGod.centY-1 &&tank.centX >= theGod.centX)
return true;
for(int j=tank.centY-1;j<=tank.centY+1;j++)
{
if(map[i][j] == WALL || map[i][j]==NORMAL_BULLET || map[i][j]==LIGHT_BULLET || map[i][j] == BOSS)
return true;
}
}
return false;
}
if(tank.direction == DIR_RIGHT)
{
for(int i=tank.centX + 2;i< screenWidth;i++)
{
if(tank.centY <= theGod.centY+1 &&tank.centY>=theGod.centY-1 &&tank.centX <= theGod.centX)
return true;
for(int j=tank.centY-1;j<=tank.centY+1;j++)
{
if(map[i][j] == WALL || map[i][j]==NORMAL_BULLET || map[i][j]==LIGHT_BULLET || map[i][j] == BOSS)
return true;
}
}
return false;
}
}
void TankWar::CloseToGodTank(Tank& tank)
{
int dir = 0;
if(tank.centY < theGod.centY && tank.centX < theGod.centX)
{
dir = rand()%100;
if(dir < 50) MoveDown(tank);
else MoveRight(tank);
}
if(tank.centY > theGod.centY && tank.centX < theGod.centX)
{
dir = rand()%100;
if(dir < 50) MoveUp(tank);
else MoveRight(tank);
}
if(tank.centY < theGod.centX && tank.centX >theGod.centX)
{
dir = rand()%100;
if(dir < 50) MoveDown(tank);
else MoveLeft(tank);
}
if(tank.centX > theGod.centX && tank.centX > theGod.centX)
{
dir = rand()%100;
if(dir < 50) MoveUp(tank);
else MoveLeft(tank);
}
}
void TankWar::CloseToHome(Tank& tank)
{
int dir = 0;
if(tank.centY < boss.centY && tank.centX < boss.centX)
{
dir = rand()%100;
if(dir < 50) MoveDown(tank);
else MoveRight(tank);
}
if(tank.centY > boss.centY && tank.centX < boss.centX)
{
dir = rand()%100;
if(dir < 50) MoveUp(tank);
else MoveRight(tank);
}
if(tank.centY < boss.centX && tank.centX > boss.centX)
{
dir = rand()%100;
if(dir < 50) MoveDown(tank);
else MoveLeft(tank);
}
if(tank.centX > boss.centX && tank.centX > boss.centX)
{
dir = rand()%100;
if(dir < 50) MoveUp(tank);
else MoveLeft(tank);
}
}
void TankWar::GameOverNotice(string str)
{
system("clear");
cout <<str <<endl;
cout << "Congratulation!"<<endl;
}
main.cpp
#include <iostream>
#include "TankWar.h"
using namespace std;
int main()
{
TankWar tank(30,20,20);
tank.GameStart();
return 0;
}
makefile
TankWar: main.o TankWar.o
g++ -lpthread main.o TankWar.o -o TankWar
main.o: main.cpp TankWar.h
g++ -c main.cpp
TankWar.o: TankWar.cpp
g++ -c TankWar.cpp
clean:
rm *.o