C语言——坦克大战

最近在学习C语言,闲来无事,花了3天搞了个坦克大战。代码粗陋,大佬勿喷~

坦克大战效果图:
C语言——坦克大战

坦克大战流程图:
C语言——坦克大战
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;
}


全篇完~各位兄弟留下一个赞再走呗!

C语言——坦克大战

上一篇:Java输入输出流


下一篇:Java中的转换流