最近在学习C语言,闲来无事,花了3天搞了个坦克大战。代码粗陋,大佬勿喷~
坦克大战效果图:
坦克大战流程图:
1、键盘信号获取
由 _kbhit(),_getch() ,函数获取
_kbhit()检测有没有键按下
_getch()读取键值
2、坦克模型
typedef struct Tank
{
int id;//坦克ID
int x;
int y;
int tankType;//0 普通坦克 1 打三次才会爆炸的坦克 2速度型坦克
int blood;//血条
int moveSpeed;//移动速度
int moveCount;//计数,当计数等于moveSpeed 则移动一步
int lives;//生命
int distance;//移动距离 1 ~ 20 格
int invincibleCount;//无敌时间计数
Bullet bullet;//子弹 结构体
bool overLapping;//检测坦克刚出来时敌我双方是否重叠
bool hasShow;//坦克是否已经显示在地图上
bool propsTank;//爆炸后随机出现道具
BYTE camp;//阵营 0 我方 1 敌方
BYTE direction; //移动方向 0 上 1 右 2下 3左
Tank *pNextTank;
}TANK, *PTANK;
(1)坦克在控制台上表现是9个方块,显示上下左右的图案。
(2)敌方坦克数据使用链表存储
(3)敌方坦克自动移动,随机移动距离,随机移动方向
3、子弹模型
struct Bullet
{
int x;
int y;
int speed;//子弹速度
int speedCount;//子弹速度计数,当计数等于speed则子弹移动一步
int shootCount;//发射子弹倒计时
BYTE direction;//子弹方向 0 上 1 右 2下 3左
bool fly;//子弹是否还在飞行
};
(1)在控制台上表现是一个小方块,随着线程循环移动
4、道具模型
typedef struct Props
{
int x;
int y;
int disappear;//道具消失计时
BYTE type;//道具类型 0 生命+1 1 城堡临时加固 2无敌金身 3地雷敌军全体爆炸
Props* pNext;
}PROPS, *PPROPS;
(1)条件有限,道具就在控制台上打印了中文字符:生命、地雷、加固、无敌,共4种道具
5、光标跳转到指定位置
void gotoxy(int x, int y, int ForgC)
{
if(ForgC == -1)
{
ForgC = LIGHTGREEN;
}
// 更新光标位置
COORD pos;
WORD wColor;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(hOutput, &csbi))
{
//设置字体颜色
wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
SetConsoleTextAttribute(hOutput, wColor);
}
// 隐藏光标
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = FALSE;
cursor.dwSize = 1; //值介于1 ~ 100 之间 单元格底部为下划线 ~ 完全填充单元格
SetConsoleCursorInfo(hOutput, &cursor);
}
这样方便将光标移动到指定位置打印字符或者删除字符。
6、判断游戏是否结束
(1)敌方全部消灭
(2)我方生命清零
(3)城堡被攻陷
7、子弹碰撞检测
(1)检测子弹是否碰到游戏边界
(2)检测子弹是否碰到游戏障碍物(砖块或铁块)
(3)检测我方子弹是否碰到敌方坦克,敌方子弹是否碰到我方坦克
(4)检测我方子弹是否碰到敌方坦克
(5)检测子弹是否攻入城堡
8、坦克碰撞检测
(1)检测坦克是否撞到游戏边界
(2)检测坦克是否撞到地图内障碍物(砖块或铁块)
(3)检测坦克是否互撞
以下是坦克大战游戏完整C语言源代码:
// 坦克大战.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "string.h"
#include "stdlib.h"
#include "windows.h"
#include "conio.h"
#include "time.h"
#include "math.h"
#define FilePath "C:\\GameTank.dat"
#define GameX 36
#define GameY 36
#define ENEMYNUM 8 //地图中敌人数量
#define DISAPPEAR 1000 //道具消失时间间隔
//颜色
enum
{
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
typedef struct Props
{
int x;
int y;
int disappear;//道具消失计时
BYTE type;//道具类型 0 生命+1 1 城堡临时加固 2无敌金身 3地雷敌军全体爆炸
Props* pNext;
}PROPS, *PPROPS;
struct CastleWall//城墙结构体
{
int x;
int y;
int* pScreenPoint;//指针指向城墙中每个砖块的内存,方便后续改变城墙材质
};
struct Bullet
{
int x;
int y;
int speed;//子弹速度
int speedCount;//子弹速度计数,当计数等于speed则子弹移动一步
int shootCount;//发射子弹倒计时
BYTE direction;//子弹方向 0 上 1 右 2下 3左
bool fly;//子弹是否还在飞行
};
typedef struct Tank
{
int id;//坦克ID
int x;
int y;
int tankType;//0 普通坦克 1 打三次才会爆炸的坦克 2速度型坦克
int blood;//血条
int moveSpeed;//移动速度
int moveCount;//计数,当计数等于moveSpeed 则移动一步
int lives;//生命
int distance;//移动距离 1 ~ 20 格
int invincibleCount;//无敌时间计数
Bullet bullet;//子弹 结构体
bool overLapping;//检测坦克刚出来时敌我双方是否重叠
bool hasShow;//坦克是否已经显示在地图上
bool propsTank;//爆炸后随机出现道具
BYTE camp;//阵营 0 我方 1 敌方
BYTE direction; //移动方向 0 上 1 右 2下 3左
Tank *pNextTank;
}TANK, *PTANK;
TANK G_Tank;//我方坦克
PTANK G_pEnemyTankHead;//敌方坦克链表头
PPROPS G_PropsHead;//道具链表头
char name[200];
int score;//得分
//指令集: w 向上 a 向左 s 向下 d 向右 o 结束游戏并保存 p 暂停游戏 空格 开炮
char KEYVALUESET[] = {'w', 'a', 's', 'd', 'o', 'p', ' '};
BYTE DIRECTIONDATA[4][9] = {0};//坦克各个方向的状态图
int G_ScreenMap[GameX + 1][GameY + 1] = {0};//初始化标记地图上的点 1 砖头 2 铁块 3地图墙壁 0 无障碍物
int G_xArr[] = {1, GameX / 2, GameX - 3};//敌方坦克随机出现的X轴位置
CastleWall G_CastleWallArr[26];//城堡围墙结构体
int G_CastleWallChangeCount = 1000;//城堡围墙从铁块变成砖头的时间计数
//函数声明
void createProps();//生成道具
/************************************************************************/
/*光标跳转到指定位置*/
/************************************************************************/
void gotoxy(int x, int y, int ForgC)
{
if(ForgC == -1)
{
ForgC = LIGHTGREEN;
}
// 更新光标位置
COORD pos;
WORD wColor;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(hOutput, &csbi))
{
//设置字体颜色
wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
SetConsoleTextAttribute(hOutput, wColor);
}
// 隐藏光标
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = FALSE;
cursor.dwSize = 1; //值介于1 ~ 100 之间 单元格底部为下划线 ~ 完全填充单元格
SetConsoleCursorInfo(hOutput, &cursor);
}
/************************************************************************/
/*光标跳转到指定位置(函数重载)*/
/************************************************************************/
void gotoxy(int x, int y)
{
gotoxy(x, y, -1);
}
/************************************************************************/
/*指定位置打印游戏边框*/
/************************************************************************/
int switchFlag = 0;
void gotoprint(int x, int y, int color)
{
gotoxy(x * 2, y, color);
printf("■");
}
void gotoprint(int x, int y)
{
int color = (switchFlag = ! switchFlag) ? LIGHTGRAY : LIGHTRED;
gotoprint(x, y, color);
}
/************************************************************************/
/*从指定位置开始清除指定长度元素*/
/************************************************************************/
void gotodelete(int x, int y, int length)
{
int i;
gotoxy(x * 2, y);
for (i = 0; i < length; i++)
{
printf(" ");
}
}
/************************************************************************/
/*清除指定位置元素*/
/************************************************************************/
void gotodelete(int x, int y)
{
gotodelete(x, y, 1);
}
/************************************************************************/
/* 绘制游戏边框 */
/************************************************************************/
void drawGameMap()
{
int i, j, count;
//初始化游戏地图
for (i = 0; i < GameX; i++)
{
gotoprint(i, 0);
G_ScreenMap[i][0] = 3;//墙壁
}
for (i = 0; i < GameX; i++)
{
gotoprint(i, GameY);
G_ScreenMap[i][GameY] = 3;//墙壁
}
for (i = 0; i < GameY; i++)
{
gotoprint(0, i);
G_ScreenMap[0][i] = 3;//墙壁
}
for (i = 0; i <= GameY; i++)
{
gotoprint(GameX, i);
G_ScreenMap[GameX][i] = 3;//墙壁
}
//初始化图内砖块
for (i = 0; i < GameX; i++)
{
for (j = 0; j < GameY; j++)
{
if((i >= 5 && i <= 10 || i >= 15 && i <= 20 || i >= 25 && i <= 30) && j >=6 && j <= 30)
{
gotoprint(i, j, LIGHTGRAY);
G_ScreenMap[i][j] = 1;//砖块
}
}
}
//初始化城堡
count = 0;
for (i = GameX / 2 - 3; i <= GameX / 2 + 3; i++)
{
for (j = GameY - 5; j < GameY; j ++)
{
if(! (i >= GameX / 2 - 1 && i <= GameX / 2 + 1 && j >= GameY - 3))
{
G_ScreenMap[i][j] = 1;//砖块
G_CastleWallArr[count].x = i;
G_CastleWallArr[count].y = j;
G_CastleWallArr[count].pScreenPoint = &G_ScreenMap[i][j];
count++;
gotoprint(i, j , LIGHTGRAY);
}
else if(i == GameY / 2 || j == GameY - 2)
{
gotoprint(i, j , LIGHTGREEN);
}
}
}
}
/************************************************************************/
/*游戏读档后恢复地图*/
/************************************************************************/
void recoveryScreenMap()
{
int i, j, count;
count = 0;
for (i = 0; i < GameX + 1; i++)
{
for (j = 0; j < GameY + 1; j++)
{
if(G_ScreenMap[i][j] == 1)//砖块
{
gotoprint(i, j, LIGHTGRAY);
}
else if(G_ScreenMap[i][j] == 2)//铁块
{
gotoprint(i, j, YELLOW);
}
else if(G_ScreenMap[i][j] == 3)//游戏边界
{
if(i == 0 || i == GameX)
{
gotoprint(i, j);
}
else
{
gotoprint(i, j, i % 2 == 1 ? LIGHTRED : LIGHTGRAY);
}
}
}
}
//恢复城堡数据
for (i = GameX / 2 - 3; i <= GameX / 2 + 3; i++)
{
for (j = GameY - 5; j < GameY; j ++)
{
if(! (i >= GameX / 2 - 1 && i <= GameX / 2 + 1 && j >= GameY - 3))
{
G_CastleWallArr[count].x = i;
G_CastleWallArr[count].y = j;
G_CastleWallArr[count].pScreenPoint = &G_ScreenMap[i][j];
count++;
}
else if(i == GameY / 2 || j == GameY - 2)
{
gotoprint(i, j , LIGHTGREEN);
}
}
}
}
/************************************************************************/
/* 初始化游戏开始界面*/
/************************************************************************/
bool initGameScreen(bool state)
{
bool result;
char keyValue;
PTANK p1, p2;
PPROPS pProps1, pProps2;
system("title 坦克大战 by 机智蛋");
result = false;
gotoxy(GameX - 10, 10);
printf("/**********************************************/");
gotoxy(GameX - 10, 22);
printf("/**********************************************/");
gotoxy(GameX- 5, 13);
printf("WELCOME TO THE BATTLE OF TANKS");
gotoxy(GameX - 10, 16);
printf("W:左移 D:右移 S:下移 空格:开炮 P暂停 O结束游戏并保存");
gotoxy(GameX - 5, 18);
if(state)
{
printf("游戏有存档,是否读取?Y/N");
while(1)
{
keyValue = _getch();
if(keyValue == 'Y' || keyValue == 'y')//同意读档
{
result = true;
break;
}
else if(keyValue == 'N' || keyValue == 'n')//不同意读档
{
//DeleteFileA(FilePath); //删除存档
//以下清除已读取的数据
score = 0;
p1 = G_pEnemyTankHead;
while(p1->pNextTank != NULL)
{
p2 = p1->pNextTank;
p1->pNextTank = p1->pNextTank->pNextTank;
free(p2);
}
pProps1 = G_PropsHead;
while(pProps1->pNext != NULL)
{
pProps2 = pProps1->pNext;
pProps1->pNext = pProps1->pNext->pNext;
free(pProps2);
}
memset(G_ScreenMap, 0, sizeof(G_ScreenMap));//地图大数组清零
G_CastleWallChangeCount = 0;
result = false;
break;
}
}
}
if(! result) //不同意读档或无存档
{
gotodelete(GameX - 5, 18, 15);
gotoxy(GameX - 5, 18);
printf("PLEASE ENTER YOUR NAME:");
scanf("%s", name);
}
system("CLS");
gotoxy((GameX+1) * 2, 15);
printf("Your life remains %d", G_Tank.lives);
gotoxy((GameX+1) * 2, 20);
printf("hello %s,Welcome To Play", name);
gotoxy((GameX+1) * 2, 25);
printf("Your Score Is:%d = ̄ω ̄= ", score);
gotoxy((GameX+1) * 2, 30);
printf("This Game Is Created By 机智蛋");
return result;
}
/************************************************************************/
/* 初始化游戏开始界面(函数重载)*/
/************************************************************************/
bool initGameScreen()
{
return initGameScreen(0);
}
/************************************************************************/
/* 游戏结束处理*/
/************************************************************************/
void FinishGame()
{
system("CLS");
gotoxy(GameX - 10, 10);
printf("/**********************************************/");
gotoxy(GameX - 10, 20);
printf("/**********************************************/");
gotoxy(GameX - 5, 14);
printf("GAME OVER o(* ̄▽ ̄*)o");
gotoxy(GameX - 5, 16);
printf("Your Score is %d ", score);
gotoxy(GameX - 5, 18);
printf("还不错哦, 继续努力O(∩_∩)O");
gotoxy(0, 27);
system("pause");
}
/************************************************************************/
/*打印分数*/
/************************************************************************/
void printScore()
{
gotoxy((GameX+1) * 2, 15);
printf("Your life remains %d", G_Tank.lives);
gotoxy((GameX+1) * 2, 25);
printf("Your Score Is:%d = ̄ω ̄= ", score);
}
/************************************************************************/
/*读档游戏*/
/************************************************************************/
bool readGameFile()
{
FILE *fp;
int i;
PTANK p1, p2;
TANK tmpTank;
PPROPS pProps1, pProps2;
PROPS tmpProps;
int tmpScreenMap[GameX + 1][GameY + 1] = {0};
int tmpCastleWallChangeCount = 0;
bool readSucc;
readSucc = true;
i = 0;
if((fp = fopen(FilePath, "rb")) == NULL)
{
return false;
}
fread(DIRECTIONDATA, sizeof(DIRECTIONDATA), 1, fp);
p1 = G_pEnemyTankHead;
do
{
fread(&tmpTank, sizeof(TANK), 1, fp);//读取链表头
if(i++ > 0)
{
if(tmpTank.camp != 1 || tmpTank.lives <= 0)//检测数据合法性
{
readSucc = false;
break;
}
tmpTank.hasShow = false;//这里置为false方便后面调用方法显示出来
p2 = (PTANK)malloc(sizeof(TANK));
MoveMemory(p2, &tmpTank, sizeof(TANK));
p1->pNextTank = p2;
p1 = p2;
}
} while (tmpTank.pNextTank != NULL);
fread(&tmpCastleWallChangeCount, sizeof(int), 1, fp);//读取城堡围墙材质更改倒计时
fread(tmpScreenMap, sizeof(G_ScreenMap), 1, fp);//读取地图各点状态
fread(&tmpTank, sizeof(TANK), 1, fp);
tmpTank.hasShow = false;
fread(name, sizeof(name), 1, fp);
fread(&score, sizeof(int), 1, fp);
i = 0;
pProps1 = G_PropsHead;
do
{
fread(&tmpProps, sizeof(PROPS), 1, fp);//读取链表头
if(i++ > 0)
{
if(tmpProps.type < 0 || tmpProps.type > 3)//检测数据合法性
{
readSucc = false;
break;
}
pProps2 = (PPROPS)malloc(sizeof(PROPS));
MoveMemory(pProps2, &tmpProps, sizeof(PROPS));
pProps1->pNext = pProps2;
pProps1 = pProps2;
}
} while (tmpProps.pNext != NULL && ! feof(fp));
fclose(fp);
//检测数据合法性
if(tmpTank.lives <=0 || tmpTank.pNextTank != NULL || tmpTank.camp != 0)
{
readSucc = false;
}
if(readSucc)//读取成功
{
MoveMemory(G_ScreenMap, tmpScreenMap, sizeof(G_ScreenMap));
MoveMemory(&G_Tank, &tmpTank, sizeof(TANK));
G_CastleWallChangeCount = tmpCastleWallChangeCount;
}
else//读取失败释放内存
{
DeleteFileA(FilePath);
p1 = G_pEnemyTankHead;
while(p1->pNextTank != NULL)
{
p2 = p1->pNextTank;
p1->pNextTank = p1->pNextTank->pNextTank;
free(p2);
}
pProps1 = G_PropsHead;
while(pProps1->pNext != NULL)
{
pProps2 = pProps1->pNext;
pProps1->pNext = pProps1->pNext->pNext;
free(pProps2);
}
}
return readSucc;
}
/************************************************************************/
/*保存游戏*/
/************************************************************************/
void saveGame()
{
FILE *fp;
PTANK pTank;
PPROPS pProps;
if((fp = fopen(FilePath, "wb")) == NULL)
{
gotoxy(0, GameY + 2);
printf("游戏保存失败\n");
return;
}
fwrite(DIRECTIONDATA, sizeof(DIRECTIONDATA), 1, fp);
pTank = G_pEnemyTankHead;
while(pTank != NULL)
{
fwrite(pTank, sizeof(TANK), 1, fp);
pTank = pTank->pNextTank;
}
fwrite(&G_CastleWallChangeCount, sizeof(int), 1, fp);
fwrite(G_ScreenMap, sizeof(G_ScreenMap), 1, fp);
fwrite(&G_Tank, sizeof(G_Tank), 1, fp);
fwrite(name, sizeof(name), 1, fp);
fwrite(&score, sizeof(int), 1, fp);
pProps = G_PropsHead;
while(pProps != NULL)
{
fwrite(pProps, sizeof(PROPS), 1, fp);
pProps = pProps->pNext;
}
fclose(fp);
}
/************************************************************************/
/*判断游戏是否结束*/
/************************************************************************/
bool checkGameOver()
{
PTANK pTank;
if(G_pEnemyTankHead->pNextTank == NULL)
{
return true;
}
if(G_Tank.lives == 0)
{
return true;
}
//检测敌方子弹有没有攻入城堡
pTank = G_pEnemyTankHead->pNextTank;
while(pTank != NULL)
{
if(pTank->bullet.fly)
{
if(pTank->bullet.x >= GameX / 2 - 1 && pTank->bullet.x <= GameX / 2 + 1 && pTank->bullet.y >= GameY - 3)
{
return true;
}
}
pTank = pTank->pNextTank;
}
//检测我方子弹有没有攻入城堡
if(G_Tank.bullet.x >= GameX / 2 - 1 && G_Tank.bullet.x <= GameX / 2 + 1 && G_Tank.bullet.y >= GameY - 3)
{
return true;
}
return false;
}
/************************************************************************/
/*改变城堡城墙材质 0 砖头 1 铁块*/
/************************************************************************/
void castleWallChangeState(int state)
{
int i;
if(state == 1)
{
G_CastleWallChangeCount = 1000;
}
for (i = 0; i < 26; i++)
{
if(state == 0)//城堡城墙变成砖头
{
gotoprint(G_CastleWallArr[i].x, G_CastleWallArr[i].y , LIGHTGRAY);
*(G_CastleWallArr[i].pScreenPoint) = 1;
}
else if(state == 1)//城堡城墙变成铁块
{
gotoprint(G_CastleWallArr[i].x, G_CastleWallArr[i].y , YELLOW);
*(G_CastleWallArr[i].pScreenPoint) = 2;
}
}
}
/************************************************************************/
/*绘制坦克*/
/************************************************************************/
void drawTank(PTANK pTank, bool clear)
{
int i, color;
if(pTank->invincibleCount > 0)//坦克无敌金身
{
color = YELLOW;
}
else
{
color = pTank->blood + LIGHTBLUE;//坦克颜色随血量变化
}
for (i = 0; i < 9; i++)
{
gotoxy((pTank->x + i % 3)* 2, pTank->y + i / 3, color);
if(DIRECTIONDATA[pTank->direction][i] == 1)
{
if(clear)
{
printf(" ");
}
else
{
printf("■");
}
}
else
{
printf(" ");
}
}
}
void drawTank(PTANK pTank)
{
drawTank(pTank, false);
}
void clearTank(PTANK pTank)
{
drawTank(pTank, true);
}
/************************************************************************/
/*绘制子弹*/
/************************************************************************/
void drawBullet(PTANK pTank, bool clear)
{
if(clear)
{
gotodelete(pTank->bullet.x, pTank->bullet.y);
}
else
{
if(pTank->bullet.fly)
{
gotoprint(pTank->bullet.x, pTank->bullet.y, LIGHTBLUE);
}
}
}
void drawBullet(PTANK pTank)
{
drawBullet(pTank, false);
}
void clearBullet(PTANK pTank)
{
drawBullet(pTank, true);
}
/************************************************************************/
/*检测坦克位置是否撞墙或撞到其他坦克*/
/************************************************************************/
bool checkTankLocation(PTANK pTank, bool checkOverLapping)
{
int i, j;
PTANK p;
p = G_pEnemyTankHead->pNextTank;
for (i = 0; i < 3; i++)
{
for (j = 0; j< 3; j++)
{
if(G_ScreenMap[pTank->x + i][pTank->y + j] != 0)//检测坦克是否撞墙或撞到图内砖头
{
return false;
}
}
}
while(p != NULL)
{
if(p != pTank && p->hasShow && abs(p->x - pTank->x) < 3 && abs(p->y - pTank->y) < 3)//检测坦克是否互撞
{
if(checkOverLapping)
{
pTank->overLapping = true;
}
return false;
}
p = p->pNextTank;
}
// if(pTank != &G_Tank)//检测敌方坦克有没有撞到我方坦克
// {
// if(! checkOverLapping && pTank->overLapping)
// {
// return true;
// }
// if(G_Tank.hasShow && abs(G_Tank.x - pTank->x) < 3 && abs(G_Tank.y - pTank->y) < 3)//检测坦克是否互撞
// {
// if(checkOverLapping)
// {
// pTank->overLapping = true;
// }
// return false;
//
// }
// }
return true;
}
bool checkTankLocation(PTANK pTank)
{
return checkTankLocation(pTank, false);
}
/************************************************************************/
/*检测敌我双方坦克是否有重叠*/
/************************************************************************/
void checkTankOverLapping()
{
PTANK pTank;
pTank = G_pEnemyTankHead->pNextTank;
while(pTank != NULL)
{
if(pTank->overLapping)
{
pTank->overLapping = false;
checkTankLocation(pTank, true);
}
pTank = pTank->pNextTank;
}
if(G_Tank.overLapping)
{
G_Tank.overLapping = false;
checkTankLocation(&G_Tank, true);
}
}
/************************************************************************/
/*坦克移动*/
/************************************************************************/
bool moveTank(PTANK pTank)
{
bool canMove;
TANK tankTemp;
if(! pTank->hasShow)
{
return false;
}
// if(pTank->overLapping)
// {
// return true;
// }
if(++ pTank->moveCount < pTank->moveSpeed)//
{
return false;
}
pTank->moveCount = 0;
MoveMemory(&tankTemp, pTank, sizeof(TANK));
if(pTank->direction == 0)//上
{
pTank->y --;
}
else if(pTank->direction == 1)//右
{
pTank->x += 1;
}
else if(pTank->direction == 2)//下
{
pTank->y ++;
}
else if(pTank->direction == 3)//左
{
pTank->x -= 1;
}
if(! (canMove = checkTankLocation(pTank)))
{
MoveMemory(pTank, &tankTemp, sizeof(TANK));
}
clearTank(&tankTemp);
drawTank(pTank);
if(pTank->camp == 1)//敌方坦克
{
pTank->distance --;
if(! canMove || pTank->distance <= 0)
{
pTank->distance = rand() % 20 + 1;//随机行进距离
pTank->direction = rand() % 4;//随机行进方向
}
}
return canMove;
}
/************************************************************************/
/*检测坦克是否显示在地图上*/
/************************************************************************/
void checkShowTank()
{
PTANK pTank = G_pEnemyTankHead->pNextTank;
while(pTank != NULL)
{
if(! pTank->hasShow && checkTankLocation(pTank))
{
pTank->hasShow = true;
drawTank(pTank);
}
pTank = pTank->pNextTank;
}
// if(! G_Tank.hasShow)
// {
if(G_Tank.invincibleCount > 0)
{
G_Tank.invincibleCount --;
}
G_Tank.hasShow = true;
drawTank(&G_Tank);
// }
}
/************************************************************************/
/*创建敌方坦克数据*/
/************************************************************************/
PTANK createTank(PTANK pTank)
{
if(pTank == NULL)//如果传的参数不为NULL,则代表坦克复活
{
pTank = (PTANK)malloc(sizeof(TANK));
pTank->pNextTank = NULL;
pTank->lives = 5;
pTank->bullet.fly = false;
pTank->bullet.shootCount = rand() % 100;
pTank->bullet.speed = 1;
pTank->bullet.speedCount = 0;
pTank->bullet.x = 0;
pTank->bullet.y = 0;
}
pTank->x = G_xArr[rand() % 3];
pTank->y = 1;
pTank->tankType = rand() % 3;//坦克类型
pTank->blood = 1;//血量
pTank->moveSpeed = 10;//移动速度,数值越大速度越慢
pTank->moveCount = 0;//移动距离,当数值等于distance则移动一步
pTank->direction = 2;
pTank->camp = 1;
pTank->invincibleCount = 0;
pTank->hasShow = false;
pTank->overLapping = false;
pTank->distance = rand() % 20 + 1;
pTank->propsTank = rand() % 2 == 1;
if(pTank->tankType == 1)
{
pTank->blood = 3;
}
else if(pTank->tankType == 2)
{
pTank->moveSpeed = 3;
}
//checkTankLocation(pTank, true);
return pTank;
}
/************************************************************************/
/*初始化敌我双方坦克数据*/
/************************************************************************/
void initTank()
{
int i, j;
PTANK pTank;
pTank = G_pEnemyTankHead;
//初始化方向状态数组 上下左右
for (i = 0; i < 4; i++)
{
for (j = 0; j < 9; j++)
{
if(i == 0)
{
if(j != 0 && j != 2)
{
DIRECTIONDATA[i][j] = 1;//上
}
}
else if(i == 1)
{
if(j != 2 && j != 8)
{
DIRECTIONDATA[i][j] = 1;//右
}
}
else if(i == 2)
{
if(j != 6 && j != 8)
{
DIRECTIONDATA[i][j] = 1;//下
}
}
else if(i == 3)
{
if(j != 0 && j != 6)
{
DIRECTIONDATA[i][j] = 1;//左
}
}
}
}
//初始化敌方坦克数据
for (i = 0; i < ENEMYNUM; i++)
{
pTank->pNextTank = createTank(NULL);
pTank = pTank->pNextTank;
//drawTank(pTank);
}
//初始化我方坦克数据
G_Tank.id = 1;
G_Tank.x = GameX / 2 - 8;
G_Tank.y = GameY - 3;
G_Tank.tankType = 1;
G_Tank.blood = 1;
G_Tank.moveSpeed = 1;
G_Tank.moveCount = 0;
G_Tank.direction = 0;
G_Tank.camp = 0;
G_Tank.lives = 5;
G_Tank.distance = 0;
G_Tank.invincibleCount = 0;
G_Tank.hasShow = false;
G_Tank.bullet.fly = false;
G_Tank.bullet.shootCount = 1;
G_Tank.bullet.speed = 1;
G_Tank.bullet.speedCount = 0;
G_Tank.bullet.x = 0;
G_Tank.bullet.y = 0;
G_Tank.pNextTank = NULL;
}/************************************************************************/
/*子弹碰撞检测*/
/************************************************************************/
bool bulletImpactCheck(PTANK pTank)
{
PTANK p, p1;
if(G_ScreenMap[pTank->bullet.x][pTank->bullet.y] == 3 || G_ScreenMap[pTank->bullet.x][pTank->bullet.y] == 2
|| pTank->bullet.y >= GameY || pTank->bullet.x >= GameX)//子弹撞边界或撞到铁块
{
return true;
}
else if(G_ScreenMap[pTank->bullet.x][pTank->bullet.y] == 1)//碰到砖头
{
if(pTank->bullet.direction == 0 || pTank->bullet.direction == 2)//子弹向上或向下飞行
{//删除砖块位置
G_ScreenMap[pTank->bullet.x][pTank->bullet.y] = 0;
G_ScreenMap[pTank->bullet.x + 1][pTank->bullet.y] = 0;
G_ScreenMap[pTank->bullet.x - 1][pTank->bullet.y] = 0;
gotodelete(pTank->bullet.x, pTank->bullet.y);
gotodelete(pTank->bullet.x + 1, pTank->bullet.y);
gotodelete(pTank->bullet.x - 1, pTank->bullet.y);
}
else if(pTank->bullet.direction == 1 || pTank->bullet.direction == 3)//子弹向右或向左飞行
{//删除砖块位置
G_ScreenMap[pTank->bullet.x][pTank->bullet.y] = 0;
G_ScreenMap[pTank->bullet.x][pTank->bullet.y + 1] = 0;
G_ScreenMap[pTank->bullet.x][pTank->bullet.y - 1] = 0;
gotodelete(pTank->bullet.x, pTank->bullet.y);
gotodelete(pTank->bullet.x, pTank->bullet.y + 1);
gotodelete(pTank->bullet.x, pTank->bullet.y - 1);
}
return true;
}
else
{
if(pTank->camp == 0)//如果是我方的子弹,则判断是否打到敌人
{
p = G_pEnemyTankHead->pNextTank;
p1 = G_pEnemyTankHead;
while(p != NULL)
{
if(p->hasShow && pTank->bullet.x - p->x < 3 && pTank->bullet.x - p->x >= 0
&& pTank->bullet.y - p->y < 3 && pTank->bullet.y - p->y >= 0)
{
p->blood --;
if(p->blood == 0)//判断血量为0
{
if(p->propsTank)//如果是道具坦克
{
createProps();//创建道具
}
if(p->tankType == 0)//普通坦克
{
score += 100;
}
else if(p->tankType == 1)//装甲坦克
{
score += 300;
}
else if(p->tankType == 2)//速度坦克
{
score += 200;
}
printScore();
clearTank(p);//清除界面上坦克图像
p->lives --;//生命-1
if(p->lives == 0)//如果生命清零,则删除该敌方坦克数据
{
if(p->bullet.fly)//如果敌方坦克生命清零,则需要停止其子弹飞行并擦除图像,否则子弹会停留在地图中
{
p->bullet.fly = false;
clearBullet(p);
}
p1->pNextTank = p->pNextTank;
free(p);
}
else//否则初始化坦克位置
{
p = createTank(p);
}
}
return true;//子弹打到坦克,停止遍历
}
else if(p->bullet.fly && p->bullet.x == pTank->bullet.x && p->bullet.y == pTank->bullet.y)
{//判断我方子弹是否跟敌方子弹对撞
p->bullet.fly = false;
clearBullet(p);
pTank->bullet.fly = false;
return true;
}
p1 = p;
p = p->pNextTank;
}
}
else if(pTank->camp == 1)//如果是敌方子弹,则判断是否打到我方坦克
{
if(G_Tank.hasShow && pTank->bullet.x - G_Tank.x < 3 && pTank->bullet.x - G_Tank.x >= 0
&& pTank->bullet.y - G_Tank.y < 3 && pTank->bullet.y - G_Tank.y >= 0)
{
if(G_Tank.invincibleCount > 0)//我方坦克处于无敌金身状态,则子弹无效
{
return true;
}
G_Tank.blood --;
if(G_Tank.blood == 0)
{
G_Tank.lives --;
G_Tank.blood = 1;
clearTank(&G_Tank);//清除界面上坦克图像
G_Tank.hasShow = false;
G_Tank.direction = 0;
G_Tank.x = GameX / 2 - 8;
G_Tank.y = GameY - 3;
G_Tank.overLapping = false;
//checkTankLocation(&G_Tank, true);
printScore();
}
return true;
}
else if(G_Tank.bullet.fly && G_Tank.bullet.x == pTank->bullet.x && G_Tank.bullet.y == pTank->bullet.y)
{//判断敌方子弹是否跟我方子弹对撞
G_Tank.bullet.fly = false;
clearBullet(&G_Tank);
pTank->bullet.fly = false;
return true;
}
}
}
return false;
}
/************************************************************************/
/*子弹飞行*/
/************************************************************************/
void bulletFlying(PTANK pTank)
{
if(pTank->bullet.fly)
{
clearBullet(pTank);
if(pTank->bullet.direction == 0)//上
{
pTank->bullet.y --;
}
else if(pTank->bullet.direction == 1)//右
{
pTank->bullet.x ++;
}
else if(pTank->bullet.direction == 2)//下
{
pTank->bullet.y ++;
}
else if(pTank->bullet.direction == 3)//左
{
pTank->bullet.x --;
}
if(! bulletImpactCheck(pTank))//子弹未碰撞到任何物体,则画出下个子弹位置
{
drawBullet(pTank);
}
else//如子弹碰撞到物体,则将子弹状态置为不在飞行
{
pTank->bullet.fly = false;
}
}
}
/************************************************************************/
/*发射子弹*/
/************************************************************************/
void shootBullet(PTANK pTank)
{
if(pTank->hasShow && ! pTank->bullet.fly)
{
if(pTank->camp == 1)
{
if(pTank->bullet.shootCount-- > 0)
{
return;
}
else
{
pTank->bullet.shootCount = rand() % 100;//重置下次发射子弹的时间间隔
}
}
pTank->bullet.fly = true;//标志子弹正在飞行
pTank->bullet.direction = pTank->direction;//子弹飞行方向跟坦克当前行驶方向一致
if(pTank->direction == 0)//上
{
pTank->bullet.x = pTank->x + 1;
pTank->bullet.y = pTank->y;
}
else if(pTank->direction == 1)//右
{
pTank->bullet.x = pTank->x + 2;
pTank->bullet.y = pTank->y + 1;
}
else if(pTank->direction == 2)//下
{
pTank->bullet.x = pTank->x + 1;
pTank->bullet.y = pTank->y + 2;
}
else if(pTank->direction == 3)//左
{
pTank->bullet.x = pTank->x;
pTank->bullet.y = pTank->y + 1;
}
}
}
/************************************************************************/
/*生成道具*/
/************************************************************************/
void createProps()
{
PPROPS pProps, pPropsNew;
pProps = G_PropsHead;
while(pProps->pNext != NULL)
{
pProps = pProps->pNext;
}
pPropsNew = (PPROPS)malloc(sizeof(PROPS));
do
{
pPropsNew->x = rand() % (GameX - 2) + 1;
pPropsNew->y = rand() % (GameY - 1) + 1;
pPropsNew->disappear = DISAPPEAR;
pPropsNew->type = rand() % 4;
pPropsNew->pNext = NULL;
if(G_ScreenMap[pPropsNew->x][pPropsNew->y] == 0 && G_ScreenMap[pPropsNew->x + 1][pPropsNew->y] == 0
&& ! (G_Tank.y - pPropsNew->y <= 0 && G_Tank.y - pPropsNew->y >-3 && (G_Tank.x >= pPropsNew->x && G_Tank.x - pPropsNew->x < 2
|| G_Tank.x < pPropsNew->x && pPropsNew->x - G_Tank.x < 3)))//判断生成的道具不在障碍物上,且不在角色坐标上
{
break;
}
} while (1);//检测地图中对应坐标是否有障碍物
pProps->pNext = pPropsNew;
}
/************************************************************************/
/*显示道具*/
/************************************************************************/
void showProps()
{
PPROPS pProps, pProsPre;
pProps = G_PropsHead->pNext;
pProsPre = G_PropsHead;
while(pProps != NULL)
{
pProps->disappear --;//道具消失计时
gotoxy(pProps->x * 2, pProps->y);
if(pProps->disappear > 0)
{//这里不停打印防止坦克走过图像被覆盖
if(pProps->type == 0)
{
printf("生命");
}
else if(pProps->type == 1)
{
printf("加固");
}
else if(pProps->type == 2)
{
printf("无敌");
}
else if(pProps->type == 3)
{
printf("地雷");
}
pProsPre = pProps;
pProps = pProps->pNext;
}
else
{
gotodelete(pProps->x, pProps->y, 2);//清除道具显示
pProsPre->pNext = pProps->pNext;
free(pProps);
pProps = pProsPre->pNext;
}
}
}
/************************************************************************/
/*检测是否吃到道具*/
/************************************************************************/
void checkGetProps()
{
PPROPS pProps, pPropsPre;
bool hasGet;
PTANK pTank, pTankPre;
pProps = G_PropsHead->pNext;
pPropsPre = G_PropsHead;
while(pProps != NULL)
{
hasGet = false;
if(G_Tank.y - pProps->y <= 0 && G_Tank.y - pProps->y >-3)
{
if(G_Tank.x >= pProps->x && G_Tank.x - pProps->x < 2)
{
hasGet = true;
}
else if(G_Tank.x < pProps->x && pProps->x - G_Tank.x < 3)
{
hasGet = true;
}
if(hasGet)
{
gotodelete(pProps->x, pProps->y, 2);//清除道具图像
if(pProps->type == 0)//生命
{
G_Tank.lives++;
printScore();
}
else if(pProps->type == 1)//加固
{
castleWallChangeState(1);
}
else if(pProps->type == 2)//无敌
{
G_Tank.invincibleCount = 1000;
}
else if(pProps->type == 3)//地雷,敌方坦克全体爆炸
{
pTank = G_pEnemyTankHead->pNextTank;
pTankPre = G_pEnemyTankHead;
while(pTank != NULL)
{
clearTank(pTank);//清除界面上坦克图像
pTank->lives --;//生命-1
if(pTank->lives == 0)//如果生命清零,则删除该敌方坦克数据
{
if(pTank->bullet.fly)//如果敌方坦克生命清零,则需要停止其子弹飞行并擦除图像,否则子弹会停留在地图中
{
pTank->bullet.fly = false;
clearBullet(pTank);
}
pTankPre->pNextTank = pTank->pNextTank;
free(pTank);
pTank = pTankPre->pNextTank;
}
else//否则初始化坦克位置
{
pTank = createTank(pTank);
pTankPre = pTank;
pTank = pTank->pNextTank;
}
}
}
pPropsPre->pNext = pProps->pNext;
free(pProps);
pProps = pPropsPre->pNext;
}
}
if(! hasGet)
{
pPropsPre = pProps;
pProps = pProps->pNext;
}
}
}
/************************************************************************/
/*指令控制*/
/************************************************************************/
bool orderController(char key)
{
int i;
for (i = 0; i < sizeof(KEYVALUESET); i++)
{
if(key == KEYVALUESET[i])
{
break;
}
}
if(i >= sizeof(KEYVALUESET)) //判断按键是否在指令集中,如不在其中则不做处理
{
return true;
}
if(key == 'o') //结束游戏并保存
{
saveGame();
FinishGame();
return false;
}
else if(key == 'p')//暂停游戏
{
gotoxy(0, GameY + 2);
printf("按任意键继续......");
_getch();
gotodelete(0, GameY + 2, 10);
}
else if(key == 'w')//上
{
G_Tank.direction = 0;
moveTank(&G_Tank);
}
else if(key == 'a')//左
{
G_Tank.direction = 3;
moveTank(&G_Tank);
}
else if(key == 'd')//右
{
G_Tank.direction = 1;
moveTank(&G_Tank);
}
else if(key == 's')//下
{
G_Tank.direction = 2;
moveTank(&G_Tank);
}
else if(key == ' ')//开炮
{
shootBullet(&G_Tank);
}
return true;
}
int main(int argc, char* argv[])
{
char order;
int count;
PTANK p;
G_pEnemyTankHead = (PTANK)malloc(sizeof(TANK));
G_pEnemyTankHead->pNextTank = NULL;
G_PropsHead = (PPROPS)malloc(sizeof(PROPS));
G_PropsHead->pNext = NULL;
count = 0;
srand(time(0));
if(initGameScreen(readGameFile()))
{
recoveryScreenMap();//恢复地图各点状态
}
else
{
drawGameMap();//绘制地图
castleWallChangeState(1);//改变城堡围墙材质为铁块
initTank();//初始化敌我双方坦克数据
}
printScore();//显示分数
while(1)
{
checkShowTank();//检测是否有坦克未显示在地图上
//checkTankOverLapping();//检测所有坦克之间是否有位置重叠
p = G_pEnemyTankHead->pNextTank;
while(p != NULL)//移动敌方坦克,敌方坦克发射子弹,敌方坦克子弹飞行
{
moveTank(p);
shootBullet(p);
bulletFlying(p);
p = p->pNextTank;
}
bulletFlying(&G_Tank);//角色子弹飞行
showProps();//显示道具
checkGetProps();//检测是否吃到道具
if(count++ >= 3 && _kbhit())//延迟获取键盘状态
{
count = 0;
order = _getch();
if(! orderController(order))
{
return 0;
}
}
if(count > 3)
{
count = 0;
}
if(checkGameOver())//检测游戏是否结束
{
FinishGame();
return 0;
}
//城堡外墙计时改变材质
if(G_CastleWallChangeCount > 0)
{
if(-- G_CastleWallChangeCount == 0)
{
castleWallChangeState(0);
}
}
Sleep(20);
}
return 0;
}