//Data.h
#pragma once
#define MAPH 40 //地图高度
#define MAPW 40 //地图宽度
//地形
enum AREA
{
空地,边界,土墙,钢墙,河流,草地,沙地
};
//方向
enum DIR
{
UP,DOWN,LEFT,RIGHT
};
//游戏模式
enum OPTION
{
开始游戏 = 1, 双人模式 , 用户地图 , 载入存档, 退出游戏
};
//颜色
enum COLOR
{
黑色, 蓝色, 绿色, 浅绿色, 红色, 紫色, 黄色, 白色, 灰色,
淡蓝色, 淡绿色, 淡浅绿色, 淡红色, 淡紫色, 淡黄色, 亮白色
};
//人物标志
enum FLAG
{
玩家A = 10, 玩家B = 11, AI_1 = 20, AI_2 = 21, AI_3 = 22, AI_4 = 23, 食物_A = 30, 食物_B = 31
};
//计数器
enum COUNTER
{
游戏速度,玩家A复活时间, 玩家B复活时间,AI复活时间,AI移动冷却
};
//游戏菜单结构体
typedef struct _MENU
{
const UINT NUM = 7; //菜单项数
COORD Coord[7]; //菜单打印坐标
char menu[7][50] =
{
"*************************************************",
" 开始游戏 ",
" 双人模式 ",
" 用户地图 ",
" 载入存档 ",
" 退出游戏 ",
"*************************************************"
};
}MENU,*PMENU;
//坦克结构体
typedef struct _TANK
{
DWORD ID : 8; //坦克ID(0-255)
DWORD Type : 2; //坦克类型(0-3)0是玩家A,1是玩家B,2是普通AI,3是特殊AI
DWORD Model : 2; //坦克图案模型(0-3)0是玩家,AI不能使用
DWORD Alive : 1; //存活为1,死亡为0
DWORD Revive : 2; //复活次数(0-3)
DWORD Direction : 2; //方向(0-3)
DWORD Health : 8; //生命值(255)
DWORD SpeedLevel : 2; //速度等级(0-3)
DWORD color : 8; //坦克颜色
DWORD Score : 24; //坦克杀敌分数(玩家AB有效)
DWORD BulletCD : 5; //坦克子弹CD
COORD codself; //中心坐标
}TANK, *PTANK;
//子弹结构体
typedef struct _BULLET
{
COORD codself; //中心坐标
DWORD Direction : 2; //方向(4)
DWORD Damage : 6; //子弹伤害(64)
DWORD Exist : 1; //子弹存在与否的变量,1为存在,0不存在
DWORD Type : 1; //1为AI子弹,0为玩家子弹
DWORD ID : 6; //子弹ID(64)
}BULLET, *PBULLET;
extern char g_MAP[40][40];
extern char g_TankMap[40][40];
extern DWORD g_counter[12];
//Data.cpp
#include "pch.h"
//全局地图==>记录地形
char g_MAP[40][40] = { 0 };
//坦克坐标地图,0空地,100是玩家A,101是玩家B,200+num是AI
char g_TankMap[40][40] = { 0 };
DWORD g_counter[12] = { 1,1,1,1,1,1,1,1,1,1,1,1 }; //间隔计数器数组,用于控制速度
2.坦克类:坦克的创建,移动,清除等功能
#pragma once
#include "AStar.h"
class Tank
{
public:
static UINT remain_AI; //剩余AI(未出现)
TANK tankData{}; //坦克属性
public:
Tank();
~Tank();
void InitPlayer(DWORD type); //初始化玩家
void InitAI(DWORD type, DWORD id = 0); //初始化AI
void CreatePlayer(); //创建玩家坦克
void CreateAI(); //创建AI坦克
void Show(); //显示坦克
void Clear(); //清除坦克
bool GetPath(COORD end); //利用AStar算法获取最短路径
void Move(DIR dire); //根据输入方向移动
//void MoveAI(DIR dire); //移动AI
void MoveToHome(); //追踪敌方老巢
void MoveToTank(COORD enemy); //追踪敌方坦克
void MoveRandom(); //随机移动
bool GoCheck(); //通行检测
bool PosCheck(COORD pos); //检测当前九宫格能否创建坦克
void UpdateMapInfo(bool IsClear = false); //在地图上更新坦克方位
//void Collision(); //碰撞检测
private:
AStar Astar; //寻路算法
COORD home; //老家坐标
char* tank_figure[4][3][4] = //坦克图案模型
{
{
{" ■ ", "■ ■", " ■■", "■■ "},
{"■●■", "■●■", "■● ", " ●■"},
{"■ ■", " ▉ ", " ■■", "■■ "}
},
{
{"┏┃┓", "┏┳┓", "┏┳┓", "┏┳┓"},
{"┣●┫", "┣●┫", "━●┫", "┣●━"},
{"┗┻┛", "┗┃┛", "┗┻┛", "┗┻┛"}
},
{
{"┏┃┓", "◢━◣", "┏┳◣", "◢┳┓"},
{"┣●┫", "┣●┫", "━●┃", "┃●━"},
{"◥━◤", "┗┃┛", "┗┻◤", "◥┻┛"}
},
{
{"╔┃╗", "╔╦╗", "╔╦╗", "╔╦╗"},
{"╠█╣", "╠█╣", "━█╣", "╠█━"},
{"╚╩╝", "╚┃╝", "╚╩╝", "╚╩╝"}
}
};
};
//Tank.cpp
#include "pch.h"
#include "Tank.h"
UINT Tank::remain_AI = 16;
Tank::Tank()
{
home.X = 19;
home.Y = 37;
}
Tank::~Tank()
{
}
/********************************************************
函数功能:初始化玩家坦克
参数 :玩家类型0(A)1(B)
返回值 :无
*********************************************************/
void Tank::InitPlayer(DWORD type)
{
tankData.Type = type;
tankData.Model = 0;
tankData.Alive = 1;
tankData.Revive = 3;
tankData.Direction = UP;
tankData.Health = 64;
tankData.SpeedLevel = 1;
tankData.color = 紫色;
tankData.BulletCD = 0;
if (tankData.Type == 0) //玩家A
{
tankData.codself.X = 14;
tankData.codself.Y = 37;
}
else //玩家B
{
tankData.codself.X = 24;
tankData.codself.Y = 37;
}
}
/********************************************************
函数功能:初始化AI坦克
参数1 :AI类型
参数2 :AI编号
返回值 :无
*********************************************************/
void Tank::InitAI(DWORD type, DWORD id /*= 0*/)
{
tankData.ID = id;
tankData.Type = type;
tankData.Model = rand() % 3 + 1;
tankData.Alive = 0;
tankData.Revive = 0;
tankData.Direction = UP;
tankData.Health = 64;
tankData.SpeedLevel = 1;
tankData.color = rand() % 5 + 4;
tankData.BulletCD = 0;
tankData.codself.X = rand() % 36 + 2;
tankData.codself.Y = 2;
}
/********************************************************
函数功能:创建玩家坦克
参数1 :无
返回值 :无
*********************************************************/
void Tank::CreatePlayer()
{
//玩家A
if (tankData.Type == 0 && tankData.Alive == 0 && tankData.Revive > 0)
{
tankData.Alive = 1;
tankData.Revive--;
tankData.Direction = 0;
tankData.Health = 64;
tankData.SpeedLevel = 1;
tankData.BulletCD = 0;
tankData.codself.X = 14;
tankData.codself.Y = 37;
}
//玩家B
if (tankData.Type == 1 && tankData.Alive == 0 && tankData.Revive > 0)
{
tankData.Alive = 1;
tankData.Revive--;
tankData.Direction = 0;
tankData.Health = 64;
tankData.SpeedLevel = 1;
tankData.BulletCD = 0;
tankData.codself.X = 24;
tankData.codself.Y = 37;
}
Show();
}
/********************************************************
函数功能:创建AI坦克
参数1 :无
返回值 :无
*********************************************************/
void Tank::CreateAI()
{
remain_AI--;
tankData.Alive = 1;
tankData.Health = 64;
tankData.SpeedLevel = 1;
tankData.BulletCD = 0;
tankData.Direction = rand() % 4;
tankData.codself.X = rand() % 36 + 2;
tankData.codself.Y = 2;
while (!PosCheck(tankData.codself)) //有障碍物重置坐标
{
tankData.codself.X = rand() % 36 + 2;
tankData.codself.Y = 2;
}
Show();
}
/********************************************************
函数功能:画坦克(人机共用)
参数 :无
返回值 :无
*********************************************************/
void Tank::Show()
{
if (!tankData.Alive) //坦克死亡不打印
return;
char* (*tankF)[4] = tank_figure[tankData.Model]; //选择坦克图案
for (int i = 0; i < 3; i++) //打印坦克
{
if (g_MAP[tankData.codself.Y - 1 + i][tankData.codself.X - 1] != 草地)
WriteChar(tankData.codself.X - 1, tankData.codself.Y - 1 + i, tankF[i][tankData.Direction], tankData.color);
}
//在地图上更新坦克方位
UpdateMapInfo();
}
/********************************************************
函数功能:清除坦克(人机共用)
参数 :无
返回值 :无
*********************************************************/
void Tank::Clear()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (g_MAP[tankData.codself.Y + j - 1][tankData.codself.X + i - 1] == 空地)
WriteChar(tankData.codself.X + i - 1, tankData.codself.Y + j - 1, " ", 黑色);
}
//清除地图上的坦克方位
UpdateMapInfo(true);
}
//*******************************************************
//函数功能 :获取最短路径
//参数 :终点坐标
//返回值 :无
//*******************************************************
bool Tank::GetPath(COORD end)
{
Astar.InitMapInfo((char*)g_MAP, MAPH, MAPW);
Astar.InitCoordInfo(tankData.codself,end);
if (Astar.FindPath())
{
Astar.GetPath();
return true;
}
return false;
}
/********************************************************
函数功能:坦克移动函数
参数 :方向
返回值 :无
*********************************************************/
void Tank::Move(DIR dire)
{
tankData.Direction =(DWORD)dire;
if (!tankData.Alive)
{
return;
}
Clear();
if (GoCheck())
{
//检测坦克当前方向可通行
switch (tankData.Direction)
{
case UP:tankData.codself.Y--; break;
case DOWN:tankData.codself.Y++; break;
case LEFT:tankData.codself.X--; break;
case RIGHT:tankData.codself.X++; break;
}
}
Show();
}
/********************************************************
函数功能:AI锁定老巢移动
参数1 :无
返回值 :无
*********************************************************/
void Tank::MoveToHome()
{
if (!GetPath(home)) //没有找到路径就随机移动
{
tankData.Direction = rand() % 4;
Move((DIR)tankData.Direction); //移动
return;
}
if (Astar.m_Path.empty()) //路径为空,说明到达目的地,随机切换方向发射子弹
{
tankData.Direction = rand() % 3 + 1;
Move((DIR)tankData.Direction); //移动
return;
}
if (rand() % 5)//坦克有五分之四的几率选择正确的方向
{
int nIndex = Astar.m_Path.size() - 1;
tankData.Direction = Astar.m_Path[nIndex].d;
Move((DIR)tankData.Direction);
}
else
{
//坦克有五分之一的几率不动
}
}
/********************************************************
函数功能:AI锁定敌方坦克移动
参数1 :敌方坦克坐标
返回值 :无
*********************************************************/
void Tank::MoveToTank(COORD enemy)
{
if (GetPath(enemy)) //获取路径
{
if (Astar.m_Path.empty()) //路径为空,说明到达目的地,随机切换方向发射子弹
{
tankData.Direction = rand() % 4;
return;
}
int nIndex = Astar.m_Path.size() - 1;
tankData.Direction = Astar.m_Path[nIndex].d;//更新方向
Move((DIR)tankData.Direction); //移动
return;
}
//没有找到路径就随机移动
tankData.Direction = rand() % 4;
Move((DIR)tankData.Direction); //移动
}
/********************************************************
函数功能:AI随机移动
参数1 :无
返回值 :无
*********************************************************/
void Tank::MoveRandom()
{
DWORD CurrentDire = tankData.Direction; //当前方向
//遇到障碍物重置方向
if (!GoCheck())
{
while (tankData.Direction == CurrentDire)
{
tankData.Direction = rand() % 4;
}
Move((DIR)tankData.Direction); //移动
return;
}
//有%10的概率重置方向
if (rand() % 10 == 5)
{
tankData.Direction = rand() % 4;
}
Move((DIR)tankData.Direction); //移动
}
/********************************************************
函数功能:坦克通行检测
参数1 :无
返回值 :返回true可通行,返回0阻挡
*********************************************************/
bool Tank::GoCheck()
{
switch (tankData.Direction)
{ //当前方向为空地、草地、沙地并且无坦克时可通行
case UP:
if ((g_MAP[tankData.codself.Y - 2][tankData.codself.X - 1] == 0 || g_MAP[tankData.codself.Y - 2][tankData.codself.X - 1] == 5 || g_MAP[tankData.codself.Y - 2][tankData.codself.X - 1] == 6) &&
(g_MAP[tankData.codself.Y - 2][tankData.codself.X] == 0 || g_MAP[tankData.codself.Y - 2][tankData.codself.X] == 5 || g_MAP[tankData.codself.Y - 2][tankData.codself.X] == 6) &&
(g_MAP[tankData.codself.Y - 2][tankData.codself.X + 1] == 0 || g_MAP[tankData.codself.Y - 2][tankData.codself.X + 1] == 5 || g_MAP[tankData.codself.Y - 2][tankData.codself.X + 1] == 6)&&
(g_TankMap[tankData.codself.Y - 2][tankData.codself.X - 1] ==0 && g_TankMap[tankData.codself.Y - 2][tankData.codself.X] == 0 && g_TankMap[tankData.codself.Y - 2][tankData.codself.X + 1] == 0))
return true;
else
return false;
case DOWN:
if ((g_MAP[tankData.codself.Y + 2][tankData.codself.X - 1] == 0 || g_MAP[tankData.codself.Y + 2][tankData.codself.X - 1] == 5 || g_MAP[tankData.codself.Y + 2][tankData.codself.X - 1] == 6) &&
(g_MAP[tankData.codself.Y + 2][tankData.codself.X] == 0 || g_MAP[tankData.codself.Y + 2][tankData.codself.X] == 5 || g_MAP[tankData.codself.Y + 2][tankData.codself.X] == 6) &&
(g_MAP[tankData.codself.Y + 2][tankData.codself.X + 1] == 0 || g_MAP[tankData.codself.Y + 2][tankData.codself.X + 1] == 5 || g_MAP[tankData.codself.Y + 2][tankData.codself.X + 1] == 6)&&
(g_TankMap[tankData.codself.Y + 2][tankData.codself.X - 1] == 0 && g_TankMap[tankData.codself.Y + 2][tankData.codself.X] == 0 && g_TankMap[tankData.codself.Y + 2][tankData.codself.X + 1] == 0))
return true;
else
return false;
case LEFT:
if ((g_MAP[tankData.codself.Y - 1][tankData.codself.X - 2] == 0 || g_MAP[tankData.codself.Y - 1][tankData.codself.X - 2] == 5 || g_MAP[tankData.codself.Y - 1][tankData.codself.X - 2] == 6) &&
(g_MAP[tankData.codself.Y][tankData.codself.X - 2] == 0 || g_MAP[tankData.codself.Y][tankData.codself.X - 2] == 5 || g_MAP[tankData.codself.Y][tankData.codself.X - 2] == 6) &&
(g_MAP[tankData.codself.Y + 1][tankData.codself.X - 2] == 0 || g_MAP[tankData.codself.Y + 1][tankData.codself.X - 2] == 5 || g_MAP[tankData.codself.Y + 1][tankData.codself.X - 2] == 6)&&
(g_TankMap[tankData.codself.Y - 1][tankData.codself.X - 2] == 0 && g_TankMap[tankData.codself.Y ][tankData.codself.X-2] == 0 && g_TankMap[tankData.codself.Y +1][tankData.codself.X-2] == 0))
return true;
else
return false;
case RIGHT:
if ((g_MAP[tankData.codself.Y - 1][tankData.codself.X + 2] == 0 || g_MAP[tankData.codself.Y - 1][tankData.codself.X + 2] == 5 || g_MAP[tankData.codself.Y - 1][tankData.codself.X + 2] == 6) &&
(g_MAP[tankData.codself.Y][tankData.codself.X + 2] == 0 || g_MAP[tankData.codself.Y][tankData.codself.X + 2] == 5 || g_MAP[tankData.codself.Y][tankData.codself.X + 2] == 6) &&
(g_MAP[tankData.codself.Y + 1][tankData.codself.X + 2] == 0 || g_MAP[tankData.codself.Y + 1][tankData.codself.X + 2] == 5 || g_MAP[tankData.codself.Y + 1][tankData.codself.X + 2] == 6)&&
(g_TankMap[tankData.codself.Y - 1][tankData.codself.X + 2] == 0 && g_TankMap[tankData.codself.Y][tankData.codself.X + 2] == 0 && g_TankMap[tankData.codself.Y + 1][tankData.codself.X + 2] == 0))
return true;
else
return false;
default:return false;
}
}
/********************************************************
函数功能:障碍物检测(人机共用)
参数1 :坦克中心坐标
返回值 :无障碍返回true
*********************************************************/
bool Tank::PosCheck(COORD pos)
{
COORD tankCoord[9] = { 0 };
tankCoord[0] = pos;
//顶点坐标
tankCoord[1].X = tankCoord[0].X - 1; //左上顶点
tankCoord[1].Y = tankCoord[0].Y - 1;
tankCoord[2].X = tankCoord[0].X + 1; //右上顶点
tankCoord[2].Y = tankCoord[0].Y - 1;
tankCoord[3].X = tankCoord[0].X - 1; //左下顶点
tankCoord[3].Y = tankCoord[0].Y + 1;
tankCoord[4].X = tankCoord[0].X + 1; //右下顶点
tankCoord[4].Y = tankCoord[0].Y + 1;
//四周坐标
tankCoord[5].X = tankCoord[0].X; //上
tankCoord[5].Y = tankCoord[0].Y - 1;
tankCoord[6].X = tankCoord[0].X; //下
tankCoord[6].Y = tankCoord[0].Y + 1;
tankCoord[7].X = tankCoord[0].X - 1; //左
tankCoord[7].Y = tankCoord[0].Y;
tankCoord[8].X = tankCoord[0].X + 1; //右
tankCoord[8].Y = tankCoord[0].Y;
for (int i = 0; i < 9; i++)
{
if (g_TankMap[tankCoord[i].Y][tankCoord[i].X] != 0 ||
g_MAP[tankCoord[i].Y][tankCoord[i].X] != 0)
{
return false;
}
}
return true;
}
void Tank::UpdateMapInfo(bool IsClear)
{
COORD tankCoord[9] = { 0 };
int tankType = 0; //地图坦克标志
tankCoord[0] = tankData.codself; //获取坦克坐标
tankCoord[1].X = tankCoord[0].X - 1; //左上顶点
tankCoord[1].Y = tankCoord[0].Y - 1;
tankCoord[2].X = tankCoord[0].X + 1; //右上顶点
tankCoord[2].Y = tankCoord[0].Y - 1;
tankCoord[3].X = tankCoord[0].X - 1; //左下顶点
tankCoord[3].Y = tankCoord[0].Y + 1;
tankCoord[4].X = tankCoord[0].X + 1; //右下顶点
tankCoord[4].Y = tankCoord[0].Y + 1;
tankCoord[5].X = tankCoord[0].X; //上
tankCoord[5].Y = tankCoord[0].Y - 1;
tankCoord[6].X = tankCoord[0].X; //下
tankCoord[6].Y = tankCoord[0].Y + 1;
tankCoord[7].X = tankCoord[0].X - 1; //左
tankCoord[7].Y = tankCoord[0].Y;
tankCoord[8].X = tankCoord[0].X + 1; //右
tankCoord[8].Y = tankCoord[0].Y;
switch (tankData.Type) //获取坦克类型
{
case 0:tankType = 10; break; //玩家A
case 1:tankType = 11; break; //玩家B
case 2:tankType = 20 + tankData.ID; break; //普通AI
case 3:tankType = 23; break; //特殊AI
default:break;
}
if (IsClear)
{
for (int i = 0; i < 9; i++) //清除坦克方位
{
g_TankMap[tankCoord[i].Y][tankCoord[i].X] = 0;
}
}
else
{
for (int i = 0; i < 9; i++) //更新坦克方位
{
g_TankMap[tankCoord[i].Y][tankCoord[i].X] = tankType;
}
}
}
3.子弹类:子弹的创建,移动,清除,碰撞检测
#pragma once
#include "Tank.h"
class Bullet
{
public:
BULLET Data = {}; //子弹属性结构体
public:
Bullet();
~Bullet();
void CreateAI(Tank &tank); //创建AI子弹
void Create(Tank &tank); //创建普通子弹
void Move(); //移动子弹
bool Hit(Tank (&tank)[6]); //子弹碰撞处理
void Show(); //打印子弹
void Clear(); //清除子弹
bool GoCheck(); //通行检测
private:
};
//Bullet.cpp
#include "pch.h"
#include "Bullet.h"
Bullet::Bullet()
{
}
Bullet::~Bullet()
{
}
/********************************************************
函数功能:创建AI子弹
参数 :无
返回值 :无
*********************************************************/
void Bullet::CreateAI(Tank &tank)
{
if (!(rand() % 10)) //冷却结束后在随后的每个游戏周期中有10分之一的可能发射子弹
{
Create(tank);
}
}
//************************************
// Method: Create
// FullName: Bullet::Create
// Access: public
// Returns: void
// Qualifier:
// Parameter: const Tank & tank
//************************************
void Bullet::Create(Tank &tank)
{
//更新子弹信息-->|方向|存在状态|类型|坐标|伤害
SHORT tankx = tank.tankData.codself.X;
SHORT tanky = tank.tankData.codself.Y;
Data.Damage = 32;
Data.Direction = tank.tankData.Direction;
Data.Exist = 1;
if(tank.tankData.Type==0 || tank.tankData.Type ==1)
Data.Type = 0;
else
Data.Type = 1;
switch (Data.Direction)
{
case 0:Data.codself.X = tankx; Data.codself.Y= tanky - 2; break;
case 1:Data.codself.X = tankx; Data.codself.Y = tanky + 2; break;
case 2:Data.codself.X = tankx - 2; Data.codself.Y = tanky; break;
case 3:Data.codself.X = tankx + 2; Data.codself.Y = tanky; break;
}
}
/********************************************************
函数功能:子弹移动
参数 :无
返回值 :无
*********************************************************/
void Bullet::Move()
{
switch (Data.Direction)
{
case UP:Data.codself.Y--; break;
case DOWN:Data.codself.Y++; break;
case LEFT:Data.codself.X--; break;
case RIGHT:Data.codself.X++; break;
}
}
/********************************************************
函数功能:子弹碰撞函数
参数 :坦克对象数组
返回值 :正常返回0,炸毁老家返回1
*********************************************************/
bool Bullet::Hit(Tank(&tank)[6])
{
SHORT X = Data.codself.X;
SHORT Y = Data.codself.Y;
//碰撞障碍物
switch (g_MAP[Data.codself.Y][Data.codself.X])
{
//可正常通行
case 沙地:
case 河流:Show(); break;
//隐藏通行:
case 草地:break;
//同归于尽,一次性打掉三块砖,方便坦克出行
case 土墙:
{
Data.Exist = 0;
if (Data.Direction == LEFT || Data.Direction == RIGHT) //纵向删除三块土墙,非土墙忽略
{
for (int i = -1; i <= 1; i++)
{
if (g_MAP[Data.codself.Y + i][Data.codself.X] == 土墙)
{
g_MAP[Data.codself.Y + i][Data.codself.X] = 空地;
WriteChar(Data.codself.X, Data.codself.Y + i, " ", 0x00);
}
}
}
else
{
for (int i = -1; i <= 1; i++)
{
if (g_MAP[Data.codself.Y][Data.codself.X + i] == 土墙)
{
g_MAP[Data.codself.Y][Data.codself.X + i] = 空地;
WriteChar(Data.codself.X + i, Data.codself.Y, " ", 0x00);
}
}
}
break;
}
//自杀
case 边界:
case 钢墙:Data.Exist = 0; break;
case 9:Data.Exist = 0; //老巢炸毁
WriteChar(18, 36, " ", 0x0F);
WriteChar(18, 37, "◢◣ ", 0x0F);
WriteChar(18, 38, "███", 0x0F);
return true;
}
if (g_TankMap[Y][X] != 0) //碰撞坦克
{
//子弹与友方坦克碰撞, 隐藏子弹, 不用处理
//if (Data.Type == 0 && (g_TankMap[Y][X] == 10 || g_TankMap[Y][X] == 11) ||
// Data.Type == 1 && g_TankMap[Y][X] >= 20)
//子弹与敌方坦克碰撞
if (Data.Type == 1 && (g_TankMap[Y][X] == 10 || g_TankMap[Y][X] == 11) ||
Data.Type == 0 && g_TankMap[Y][X] >= 20)
{
Data.Exist = 0; //子弹消失
//获取坦克信息,坦克消失
if (g_TankMap[Y][X] == 10)
{
tank[0].tankData.Alive = 0; //玩家A死亡
tank[0].Clear();
}
if (g_TankMap[Y][X] == 11)
{
tank[1].tankData.Alive = 0; //玩家B死亡
tank[1].Clear();
}
if (g_TankMap[Y][X] >= 20)
{
int nIndex = g_TankMap[Y][X] % 20 + 2; //AI下标
tank[nIndex].tankData.Alive = 0;
tank[nIndex].Clear();
tank[0].tankData.Score += 10; //更新坦克得分分数
//if (nIndex == 3) //特殊坦克死亡会留下食物
//{
// WriteChar(tank[3].tankData.codself.X, tank[3].tankData.codself.X, "卐", 黄色);
// //更新地图标志
//}
}
}
}
//与敌军子弹碰撞
//if (Data.Type == 1 && g_TankMap[Y][X] == 1 || Data.Type == 0 && g_TankMap[Y][X] == 2)
//{
// Data.Exist = 0;
//}
return false;
}
/********************************************************
函数功能:打印子弹
参数 :子弹
返回值 :无
*********************************************************/
void Bullet::Show()
{
if (Data.Exist) //子弹存在
{
WriteChar(Data.codself.X, Data.codself.Y, "☉", 0x0A);
//在g_TankMap更新子弹方位
/*if (Data.Type)
g_TankMap[Data.codself.Y][Data.codself.X] = 2;
else
g_TankMap[Data.codself.Y][Data.codself.X] = 1;*/
}
}
/********************************************************
函数功能:清除子弹
参数1 :无
返回值 :无
*********************************************************/
void Bullet::Clear()
{
//如果该位置是坦克,不用清除
if (g_TankMap[Data.codself.Y][Data.codself.X])
{
return;
}
switch (g_MAP[Data.codself.Y][Data.codself.X])
{
case 空地:WriteChar(Data.codself.X, Data.codself.Y, " ", 0x0F); break;
case 河流:WriteChar(Data.codself.X, Data.codself.Y, "~", 0x1F); break;
case 沙地:WriteChar(Data.codself.X, Data.codself.Y, "▓", 0x06); break;
default:break;
}
//在g_TankMap更新子弹方位
//g_TankMap[Data.codself.Y][Data.codself.X] = 0;
}
/********************************************************
函数功能:子弹当前位置检测
参数 :无
返回值 :可通行返回true
*********************************************************/
bool Bullet::GoCheck()
{
return (g_MAP[Data.codself.Y][Data.codself.X] == 空地 &&
g_TankMap[Data.codself.Y][Data.codself.X] == 0);
}
4.AStar寻路算法类,AI锁定敌人移动算法
#pragma once
/*********************************************************************************
AStar算法:
openList:保存待检测的点
closeList:保存已检测的点(包含最短路径)
G Path:移动损耗(即从起点开始每走一步加1,斜走加根号2)
H Path:离目的地距离
F Path:F=G+H
**********************************************************************************/
class AStar
{
public:
AStar();
~AStar();
//自定义的坐标结构体
typedef struct _MY_COORD :public COORD
{
//重载等号运算符,方便比较
bool operator==(COORD cod)
{
return (X == cod.X) && (Y == cod.Y);
}
//重载赋值运算符,方便赋值
void operator=(COORD cod)
{
X = cod.X;
Y = cod.Y;
}
short d; //坐标点方向
}MY_COORD, *PMY_COORD;
//节点的信息结构体
typedef struct _NODE_INFO
{
int g;//移动损耗(每一动一次加1)
int h;//距离终点最短距离
int f;//g+h
void SetH_F(COORD cod2)
{
h = abs(codSelf.X - cod2.X) + abs(codSelf.Y - cod2.Y);
f = g + h;
}
MY_COORD codSelf; //自身坐标
MY_COORD codParent;//父坐标点(由谁扩散出来的点的坐标)
}NODE_INFO, *PNODE_INFO;
private:
void GetTankCoord(COORD tank); //获取坦克九宫格坐标坐标
vector m_Open; //代检测的点的集合
vector m_Close; //检测过的点(扩散过的点)的集合
COORD m_tankCoord[4][4]; //坦克中心点坐标+当前方向三点坐标
int m_Block; //地图中的障碍物
int m_MapH; //地图高度
int m_MapW; //地图宽度
char* m_pMap; //地图首地址
COORD m_Start; //起点坐标
COORD m_End; //终点坐标
bool m_bInitMapInfo; //是否初始化地图信息
bool m_bInitCoordInfo; //是否初始化起始坐标
public:
void InitMapInfo(char* pMap, int nHigh, int nWidth);
void InitCoordInfo(COORD codStart, COORD codEnd);
void GetPath(); //获取最短路径
bool FindPath(); //寻找包含最短路径的点
//临时地图,保存该节点是否在open表或close中
typedef struct _TEMP_MAP
{
char isOpen : 1;
char isClose : 1;
char recver : 6;
}TEMP_MAP, *PTEMP_MAP;
PTEMP_MAP m_pTempMap; //临时地图
vector m_Path; //最短路径
};
//算法写的比较乱,没写好,这是思路:这是点到点的,坦克是九个点,得改装有点头疼
//bool FindPath(); //寻找包含最短路径的点
/*********************************************************************************
寻路过程:
1.初始化起点
2.把起点添加到openList
3.判断open是否为空(说明没找到)
4.从oprn表中找到F最小值,进行扩散
5.把扩散过的点,添加到close表中,并从open表中删除
6.检测扩散出来的点是否存在终点,如果不是,就检测是否可以添加到open表中
6.1.是否是终点(是,就直接返回true)
6.2.是否越界
6.3.是否是障碍物
6.4.是否在open表中
6.5.是否在close表中
7.把检测合格的点添加到open表中
8.重复3-7步
**********************************************************************************/
5.游戏类:贴的不全,我的地图是40*40一行一行输的,太长
void InitGameSet(); //初始化游戏设置
void GameMenu(); //游戏主菜单
void InitGame(); //初始化游戏
void GameLoop(); //游戏主循环
void GetMap(int level); //获取关卡地图
private:
bool SetWindowSize(TCHAR* pszWindowTitle, short x, short y); //设置窗口大小
void DrawMainScreen(char(*pMap)[40]); //画主屏幕地图
void DrawSideScreen(); //打印副屏幕(游戏信息)
void UpdateSideScreen(); //更新游戏信息
void DrawUserSideScreen(); //自定义地图地形选择窗口
void ClearMainScreen(); //清除主屏幕
void ClearFullScreen(); //清除全屏
COLOR SetColor(int No); //设置字体颜色
void MouseOp(); //鼠标操作控制
void GameStop(); //游戏暂停
void GameOver(bool home); //游戏结束
void UserMap(); //自定义地图
void GameCheck(); //游戏胜负检测
void MytankRebirth(); //复活玩家坦克
void CreatBullet(Tank &tank); //创建子弹
void MoveBullet(); //移动子弹
void KeyboardA(); //键盘输入控制
void KeyboardB(); //控制玩家B
void SaveInfo(); //存档
void ReadInfo(); //读档
void LoadRecord(); //加载存档信息
void NextLevel(); //加载下一关卡
private:
MENU menu; //游戏菜单
UINT GameSpeed = 10; //游戏速度
bool SingleMode = true; //单人模式标志
UINT GameLevel = 1; //游戏关卡
UINT MaxLevel = 3; //最大游戏关卡
Tank tankArr[6]; //坦克数组
Tank& tankA = tankArr[0]; //玩家A
Tank& tankB = tankArr[1]; //玩家B
Tank* AItank = &tankArr[2]; //AI
vector m_bullet; //子弹
char obType = 0; //地形类型
#include "pch.h"
#include "Game.h"
#include
#include
#pragma comment(lib, "WINMM.LIB")
Game::Game()
{
}
Game::~Game()
{
}
/********************************************************
第一部分:初始化游戏设置
1.设置窗口大小
2.隐藏光标
3.切换英文输入法
4.背景音乐
*********************************************************/
void Game::InitGameSet()
{
TCHAR* GameName = L"坦克大战";
SetWindowSize(GameName, 120, 40); //设置窗口标题,大小
//切换输入法
keybd_event(VK_SHIFT, 0, 0, 0);
Sleep(100);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
//隐藏光标
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); //标准输出设备句柄
CONSOLE_CURSOR_INFO cci; //鼠标结构体
cci.dwSize = 1;
cci.bVisible = false;
SetConsoleCursorInfo(hStdOut, &cci);
//设置背景音乐
PlaySound(L"..\\TankDemo\\Sound\\tank.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
GetMap(GameLevel); //地图初始化
}
/********************************************************
第二部分:游戏主菜单
1.清屏
2.打印游戏菜单
3.模式选择控制
*********************************************************/
void Game::GameMenu()
{
ClearFullScreen(); //清屏
//初始化游戏菜单
for (UINT i=0;i< menu.NUM;i++)
{
menu.Coord[i].X = 16;
}
menu.Coord[0].Y = 20;
menu.Coord[1].Y = 23;
menu.Coord[2].Y = 25;
menu.Coord[3].Y = 27;
menu.Coord[4].Y = 29;
menu.Coord[5].Y = 31;
menu.Coord[6].Y = 34;
//打印游戏菜单
/****************************************************/
char* Tank[8] =
{
{" ★★█〓███████▇▅▅▅▅▅▅▅▄▄▄▄▄▅▇▇▇ ● ●"},
{" █●█"},
{" ◢▄██●★●█████●★●███▄◣"},
{" ◢〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓◣"},
{" ▄▅██████████████████████▅▄▃"},
{"◢〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓◣"},
{"◥███████████████████████████◤"},
{" ◥●▲●▲●▲●▲●▲●▲●▲●▲●▲●▲●▲●▲●◤"}
};
for (UINT i = 0; i < 8; i++)
{
WriteChar(14, 2 + 2 * i, Tank[i], 0x02);
}
for (UINT i = 0; i < menu.NUM; i++)
{
WriteChar(menu.Coord[i].X, menu.Coord[i].Y, menu.menu[i], 蓝色);
}
//游戏模式选择
/****************************************************/
WriteChar(menu.Coord[1].X, menu.Coord[1].Y, menu.menu[1], 红色); //默认选择第一项
int No = 1;
while (1)
{
if (GetAsyncKeyState(VK_UP) & 0x8000) //↑按键处理
{
WriteChar(menu.Coord[No].X, menu.Coord[No].Y, menu.menu[No], 蓝色);
if (No == 1)
No = 5;
else
No--;
WriteChar(menu.Coord[No].X, menu.Coord[No].Y, menu.menu[No], 红色);
}
else if (GetAsyncKeyState(VK_DOWN) & 0x8000)//↓按键处理
{
WriteChar(menu.Coord[No].X, menu.Coord[No].Y, menu.menu[No], 蓝色);
if (No == 5)
No = 1;
else
No++;
WriteChar(menu.Coord[No].X, menu.Coord[No].Y, menu.menu[No], 红色);
}
else if (GetAsyncKeyState(0xD) & 0x8000) //回车确定
{
switch (No) //进入游戏
{
case 开始游戏: SingleMode = true; return;
case 双人模式: SingleMode = false; return;
case 用户地图: UserMap(); return;
case 载入存档: LoadRecord(); return;
case 退出游戏: exit(0);
}
}
Sleep(100);
}
}
/********************************************************
函数功能:接收键盘输入,控制坦克
参数 :无
返回值 :无
*********************************************************/
void Game::KeyboardA()
{
int nCount = 0; //计数器
if (GetAsyncKeyState('W') & 0x8000)
tankA.Move(UP);
else if (GetAsyncKeyState('S') & 0x8000)
tankA.Move(DOWN);
else if (GetAsyncKeyState('A') & 0x8000)
tankA.Move(LEFT);
else if (GetAsyncKeyState('D') & 0x8000)
tankA.Move(RIGHT);
else if (GetAsyncKeyState('J') & 0x8000)
{
CreatBullet(tankA);
}
else if (GetAsyncKeyState(VK_SPACE) & 0x8000) //空格
{
GameStop();
}
else if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) // Esc键
exit(0);
else if (nCount++ % 5 == 0) //防止按键粘连,达到微调速度
{
if (GameSpeed > 1 && GetAsyncKeyState(VK_ADD) & 0x8000)
GameSpeed--;
if (GameSpeed < 20 && GetAsyncKeyState(VK_SUBTRACT) & 0x8000)
GameSpeed++;
} //退出程序函数
else
{
//return;
}
}
void Game::KeyboardB()
{
if (GetAsyncKeyState(VK_UP) & 0x8000)
tankB.Move(UP);
else if (GetAsyncKeyState(VK_DOWN) & 0x8000)
tankB.Move(DOWN);
else if (GetAsyncKeyState(VK_LEFT) & 0x8000)
tankB.Move(LEFT);
else if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
tankB.Move(RIGHT);
else if (GetAsyncKeyState(VK_NUMPAD5) & 0x8000)
{
CreatBullet(tankB);
}
else
{ }
}
/********************************************************
函数功能:存档
参数 :无
返回值 :无
*********************************************************/
void Game::SaveInfo()
{
FILE *fp;
errno_t err;
err = fopen_s(&fp, "..\\TankDemo\\GameInfo\\Info.txt", "wb");
if (err)
{
WriteChar(20, 20, "存档失败…",红色);
return;
}
//保存地图
fwrite(g_MAP, sizeof(char), 40 * 40, fp);
//保存坦克
fwrite(tankArr, sizeof(Tank), 6, fp);
//保存子弹数量
UINT bulletNum = m_bullet.size();
fwrite(&bulletNum, sizeof(UINT), 1, fp);
fwrite(&m_bullet, sizeof(vector), bulletNum, fp);
//保存游戏信息-->|关卡|单人/双人模式标志|游戏速度
fwrite(&GameLevel, sizeof(UINT), 1, fp);
fwrite(&SingleMode, sizeof(bool), 1, fp);
fwrite(&GameSpeed, sizeof(UINT), 1, fp);
fclose(fp);
}
/********************************************************
函数功能:读档
参数 :无
返回值 :无
*********************************************************/
void Game::ReadInfo()
{
FILE *fp;
errno_t err;
err = fopen_s(&fp, "..\\TankDemo\\GameInfo\\Info.txt", "rb");
if (err)
{
WriteChar(20, 20, "读档失败…", 红色);
return;
}
//读取地图
fread(g_MAP, sizeof(char), 40 * 40, fp);
//读取坦克
fread(tankArr, sizeof(Tank), 6, fp);
//读取子弹/数量
UINT bulletNum = 0;
fread(&bulletNum, sizeof(UINT), 1, fp);
fread(&m_bullet, sizeof(vector), bulletNum, fp);
//读取游戏信息-->|关卡|单人/双人模式标志|游戏速度
fread(&GameLevel, sizeof(UINT), 1, fp);
fread(&SingleMode, sizeof(bool), 1, fp);
fread(&GameSpeed, sizeof(UINT), 1, fp);
fclose(fp);
//初始化游戏
DrawMainScreen(g_MAP); //打印主屏幕
DrawSideScreen(); //打印副屏幕
for (UINT i = 0; i < 6; i++) //打印坦克
{
tankArr[i].Show();
}
for (UINT i = 0; i < bulletNum; i++) //打印子弹
{
m_bullet[i].Show();
}
}
/********************************************************
函数功能:加载存档,进行游戏
参数 :无
返回值 :无
*********************************************************/
void Game::LoadRecord()
{
//清屏
ClearFullScreen();
//初始化随机数种子
srand((unsigned int)(time(NULL)));
ReadInfo();
GameLoop();
}
/********************************************************
函数功能:游戏胜利进入下一关
参数 :无
返回值 :无
*********************************************************/
void Game::NextLevel()
{
int timing = 0, ColorNo = 1;
COLOR Color;
GameLevel++;
if (GameLevel <= MaxLevel)
while (1)
{
if (timing++ % 30 == 0) //30次打印一次
{
Color = SetColor(ColorNo);
WriteChar(18, 20, "恭喜过关!", Color); //主屏幕中心打印
WriteChar(50, 15, "等待下关", Color); //副屏幕打印
WriteChar(45, 18, "请按回车键进入下一关卡!", Color);
WriteChar(45, 19, "或按 Esc键退出游戏!", Color);
if (++ColorNo == 8)
ColorNo = 1;
}
if (GetAsyncKeyState(0xD) & 0x8000) //回车键
{
WriteChar(50, 15, "正在进行", 0x01); //清除副屏幕提示
WriteChar(45, 18, " ", 0x00);
WriteChar(45, 19, " ", 0x00);
ClearMainScreen(); //主屏清屏函数
GetMap(GameLevel); //获取关卡地图
InitGame(); //从本关重新开始
break;
}
else if (GetAsyncKeyState(0x1B) & 0x8000) //Esc键退出
exit(0);
Sleep(20);
}
else // 通关
while (1)
{
if (timing++ % 5 == 0)
{
Color = SetColor(ColorNo);
WriteChar(18, 20, "恭喜通过全部关卡!", Color); //主屏幕中心打印
WriteChar(50, 15, "全部通关", Color); //副屏幕打印
WriteChar(45, 18, "恭喜通过全部关卡!", Color);
WriteChar(45, 19, "按 Esc键退出游戏!", Color);
if (++ColorNo == 8)
ColorNo = 1;
}
if (GetAsyncKeyState(0x1B) & 0x8000) //Esc键退出
exit(0);
Sleep(20);
}
}
/********************************************************
函数功能:自定义地图副屏幕(地形选择)
参数 :无
返回值 :无
*********************************************************/
void Game::DrawUserSideScreen()
{
for (int i = 0; i < 40; i++)
for (int j = 0; j < 20; j++)
{
switch (UserSideScreen[i][j])
{
case 1:WriteChar(j + 40, i, "■", 0x0F); break;
case 2:WriteChar(j + 40, i, "▄ 土墙", 0x04); break; //红色的土墙
case 3:WriteChar(j + 40, i, "■ 钢墙", 0x0F); break; //白色的钢墙
case 4:WriteChar(j + 40, i, "~ 河流", 0x1F); break; //蓝色的河流
case 5:WriteChar(j + 40, i, "▓ 草地", 0x02); break; //绿色的草地
case 6:WriteChar(j + 40, i, "▓ 沙地", 0x06); break; //黄色的沙地
case 7:WriteChar(j + 40, i, " 保存地图", 0x09); break;
case 8:WriteChar(j + 40, i, "**", 0x0D); break;
case 9:WriteChar(j + 40, i, "返回主菜单", 0x09); break;
}
}
}
/********************************************************
函数功能:设置字体颜色
参数 :颜色编号
返回值 :颜色
*********************************************************/
COLOR Game::SetColor(int No)
{
switch (No)
{
case 0:return 黑色;
case 1:return 蓝色;
case 2:return 绿色;
case 3:return 浅绿色;
case 4:return 红色;
case 5:return 紫色;
case 6:return 黄色;
case 7:return 白色;
case 8:return 灰色;
case 9:return 淡蓝色;
case 10:return 淡绿色;
case 11:return 淡浅绿色;
case 12:return 淡红色;
case 13:return 淡紫色;
case 14:return 淡黄色;
default:return 亮白色;
};
}
/********************************************************
函数功能:鼠标按键处理函数
参数 :无
返回值 :无
*********************************************************/
void Game::MouseOp()
{
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
INPUT_RECORD stcRecord = { 0 }; //定义输入事件结构体
MOUSE_EVENT_RECORD mer; //鼠标事件
DWORD dwRead = 0; //用于存储读取记录
COORD pos = { 0 }; //用于存储鼠标当前位置
//重设接受模式;避免受cls影响;
SetConsoleMode(hStdIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
while (1)
{
//等待事件
ReadConsoleInput(hStdIn, &stcRecord, 1, &dwRead);
//处理事件
if (stcRecord.EventType == MOUSE_EVENT)
{
mer = stcRecord.Event.MouseEvent;
if (mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
{ //鼠标左击,绘制障碍物
if (mer.dwMousePosition.X / 2 >= 18 && mer.dwMousePosition.X / 2 <= 20 && mer.dwMousePosition.Y >= 36)//鼠标在在老巢范围内
continue;
if (mer.dwMousePosition.X / 2 >= 1 && mer.dwMousePosition.X / 2 <= 38 &&
mer.dwMousePosition.Y >= 1 && mer.dwMousePosition.Y <= 38) //鼠标在地图范围内
{
switch (obType)
{
case 土墙:WriteChar(mer.dwMousePosition.X / 2, mer.dwMousePosition.Y, "▄", 0x04); break; //红色的土墙
case 钢墙:WriteChar(mer.dwMousePosition.X / 2, mer.dwMousePosition.Y, "■", 0x0F); break; //白色的钢墙
case 河流:WriteChar(mer.dwMousePosition.X / 2, mer.dwMousePosition.Y, "~", 0x1F); break; //蓝色的河流
case 草地:WriteChar(mer.dwMousePosition.X / 2, mer.dwMousePosition.Y, "▓", 0x02); break; //绿色的草地
case 沙地:WriteChar(mer.dwMousePosition.X / 2, mer.dwMousePosition.Y, "▓", 0x06); break; //黄色的沙地
default:break;
}
//保存坐标
obMap[mer.dwMousePosition.Y][mer.dwMousePosition.X / 2] = obType;
}
}
if (mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
{ //鼠标右击,读取信息
int obymin = 3, obymax = 5;
if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin && mer.dwMousePosition.Y <= obymax)
{
obType = 土墙;
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 5 && mer.dwMousePosition.Y <= obymax + 5)
{
obType = 钢墙;
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 10 && mer.dwMousePosition.Y <= obymax + 10)
{
obType = 河流;
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 15 && mer.dwMousePosition.Y <= obymax + 15)
{
obType = 草地;
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 20 && mer.dwMousePosition.Y <= obymax + 20)
{
obType = 沙地;
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 25 && mer.dwMousePosition.Y <= obymax + 25)
{ //保存地图
for (int i = 1; i < 39; i++)
for (int j = 1; j < 39; j++)
{
g_MAP[i][j] = obMap[i][j];
}
}
else if (mer.dwMousePosition.X >= 46 * 2 && mer.dwMousePosition.X <= 52 * 2 &&
mer.dwMousePosition.Y >= obymin + 30 && mer.dwMousePosition.Y <= obymax + 30)
{ //返回主菜单
GameMenu();
return;
}
else
{
obType = 空地;
}
}
if (mer.dwEventFlags == MOUSE_MOVED)
{
char szBuf[100] = { 0 };
sprintf_s(szBuf, sizeof(szBuf), "x=%2d,y=%2d ", mer.dwMousePosition.X / 2, mer.dwMousePosition.Y);
SetConsoleTitleA(szBuf);
}
}
}
}
/********************************************************
函数功能:游戏暂停处理
参数 :无
返回值 :无
*********************************************************/
void Game::GameStop()
{
int counter = 0, No = 1;
COLOR color;
while (1)
{
color = SetColor(No);
if (counter++ % 30 == 0)
{
WriteChar(50, 15, "游戏暂停", color);
WriteChar(45, 18, "请按回车键继续游戏!", color);
WriteChar(45, 19, "或按 Esc键退出游戏!", color);
WriteChar(45, 20, "或按右Shift保存游戏!", color);
if (++No == 8)
No = 1;
}
if (GetAsyncKeyState(VK_RETURN) & 0x8000) //回车键
{
WriteChar(50, 15, "正在进行", 0x01);
WriteChar(45, 18, " ", 0x00);
WriteChar(45, 19, " ", 0x00);
WriteChar(45, 20, " ", 0x00);
break;
}
else if (GetAsyncKeyState(VK_RSHIFT) & 0x8000) //右Shift键保存
{
SaveInfo();
Sleep(1000);
exit(0); //保存玩退出游戏
}
else if (GetAsyncKeyState(0x1B) & 0x8000) //Esc键退出
exit(0);
Sleep(100);
}
}
/********************************************************
第三部分:游戏初始化
1.初始化随机数种子
2.根据模式初始化坦克
3.初始化子弹
4.初始化地图
5.初始化屏幕
*********************************************************/
void Game::InitGame()
{
//清屏
ClearFullScreen();
//初始化随机数种子
srand((unsigned int)(time(NULL)));
//初始化坦克地图
memset(g_TankMap, 0, sizeof(char) * 40 * 40);
//初始化我的坦克复活次数
tankA.InitPlayer(0);
tankA.CreatePlayer();
if (!SingleMode) //双人游戏模式
{
tankB.InitPlayer(1);
tankB.CreatePlayer();
}
//AI坦克初始化
switch (GameLevel) //根据关卡初始化AItank数量
{
case 1:AItank[0].remain_AI = 4; break;
case 2:AItank[0].remain_AI = 8; break;
case 3:AItank[0].remain_AI = 16; break;
default:
break;
}
for(int i=0;i<3;i++)
{
AItank[i].InitAI(2, i);
}
AItank[3].InitAI(3, 3); //创建特殊坦克
AItank[0].CreateAI();
AItank[1].CreateAI();
//子弹清空
if(!m_bullet.empty())
m_bullet.clear();
DrawMainScreen(g_MAP); //打印主屏幕
DrawSideScreen(); //打印副屏幕
}
/********************************************************
第四部分:游戏主循环
1.游戏胜负检测
2.移动子弹
3.移动AI坦克
4.AI < 4并且剩余AI数量>0, 创建新的AI, 一次只创建一个
5.AI发射子弹
6.接收键盘输入
7.控制我方坦克
8.我方坦克死亡, 复活(死亡瞬间复活, 不必放在主函数)
********************************************************/
void Game::GameLoop()
{
while (1) //游戏主循环
{
if (g_counter[游戏速度]++%GameSpeed == 0)
{
GameCheck(); //游戏胜负检测
UpdateSideScreen(); //更新游戏信息
MoveBullet(); //移动子弹
//移动AI坦克--四种AI坦克-->|锁定老巢|锁定玩家A|锁定玩家B|随机AI|
AItank[0].MoveToHome();
AItank[1].MoveToTank(tankA.tankData.codself);
if (!SingleMode)
{
AItank[2].MoveToTank(tankB.tankData.codself);
}
else
{
AItank[2].MoveRandom();
}
AItank[3].MoveToTank(tankA.tankData.codself);
//AI<4并且剩余AI数量>0,创建新的AI,一次只创建一个
for (int i = 0; i < 4; i++)
{
if (AItank[i].tankData.Alive == 0 && AItank[i].remain_AI > 0 &&
g_counter[AI复活时间]++ % 90 == 0)
{ //如果坦克不存活。计时,每次建立有间隔1800ms
AItank[i].CreateAI();
}
}
//AI发射子弹
for (int i = 0; i < 4; i++)
{
if (AItank[i].tankData.Alive && AItank[i].tankData.BulletCD == 10)
{
CreatBullet(AItank[i]);
AItank[i].tankData.BulletCD = 0;
}
else
{
AItank[i].tankData.BulletCD++;
}
}
//接收键盘输入,控制我方坦克
KeyboardA();
KeyboardB();
//我方坦克死亡,复活
MytankRebirth();
}
Sleep(5);
}
}
/********************************************************
函数功能:获取关卡地图
参数 :无
返回值 :无
*********************************************************/
void Game::GetMap(int level)
{
int i, j;
//int GameMap[3][40][40] =
//我把数组删了,太长了
for (i = 0; i < 40; i++)
for (j = 0; j < 40; j++)
g_MAP[i][j] = GameMap[level-1][i][j]; //获取关卡地图
}
/********************************************************
函数功能:创建子弹
参数 :坦克对象
返回值 :无
*********************************************************/
void Game::CreatBullet(Tank &tank)
{
Bullet bullet = {};
//判断是AI子弹还是玩家子弹
if (tank.tankData.Type > 1 && !(rand()%11))
{
bullet.CreateAI(tank);
}
else
bullet.Create(tank);
//添加子弹,显示子弹
m_bullet.push_back(bullet);
int nIndex = m_bullet.size() - 1; //添加子弹的下标
if (nIndex < 0)
{
cout << "子弹添加失败!" << endl;
system("pause");
return;
}
if (m_bullet[nIndex].GoCheck())
{
m_bullet[nIndex].Show();
}
else
{ //判断子弹碰撞
if (m_bullet[nIndex].Hit(tankArr))
{
GameOver(1); //1表示老家炸毁
}
if (m_bullet[nIndex].Data.Exist == 0 ) //子弹碰撞消失
{
m_bullet.pop_back();
}
}
}
/********************************************************
函数功能:移动子弹
参数 :
返回值 :无
*********************************************************/
void Game::MoveBullet()
{
bool Ret = false;
for (UINT i = 0; i < m_bullet.size(); i++)
{
m_bullet[i].Clear(); //清除子弹
m_bullet[i].Move(); //移动子弹
if (m_bullet[i].GoCheck()) //检测障碍物
{
m_bullet[i].Show(); //正常通行
}
else
{
Ret = m_bullet[i].Hit(tankArr); //碰撞处理
if (Ret)
{
GameOver(1); //1表示老家炸毁
}
if (m_bullet[i].Data.Exist == 0 && m_bullet.size()>0) //子弹碰撞消失
{
m_bullet.erase(m_bullet.begin() + i);
}
}
}
}
/********************************************************
函数功能:打印主屏幕地图
参数 :地图
返回值 :无
*********************************************************/
void Game::DrawMainScreen(char(*pMap)[40])
{
for (int i = 0; i < 40; i++)
{
for (int j = 0; j < 40; j++)
{
switch (pMap[i][j])
{
case 边界:WriteChar(j, i, "■", 0x0F); break; //白色的边界
case 土墙:WriteChar(j, i, "▄", 0x04); break; //红色的土墙
case 钢墙:WriteChar(j, i, "■", 0x0F); break; //白色的钢墙
case 河流:WriteChar(j, i, "~", 0x1F); break; //蓝色的河流
case 草地:WriteChar(j, i, "▓", 0x02); break; //绿色的草地
case 沙地:WriteChar(j, i, "▓", 0x06); break; //黄色的沙地
default: break;
}
}
WriteChar(18, 36, "◣★◢", 0x0F); //老家
WriteChar(18, 37, "███", 0x0F); //老家
WriteChar(18, 38, "◢█◣", 0x0F); //老家
}
}
/********************************************************
函数功能:清除主屏幕
参数 :无
返回值 :无
*********************************************************/
void Game::ClearMainScreen()
{
for (int i = 1; i < 39; i++)
{
WriteChar(1, i, " \
", 0x00);
}
}
/********************************************************
函数功能:打印副屏幕
参数 :无
返回值 :无
*********************************************************/
void Game::DrawSideScreen()
{
for (int i = 0; i < 40; i++)
{
for (int j = 40; j < 60; j++)
{
if (i == 0 || i == 39 || j == 59)
WriteChar(j, i, "█", 0x0F);
}
}
int basey = 2;
WriteChar(47, basey, "第 关", 0x0F);
WriteChar(46, basey + 3, "分 数: ", 0x0F);
WriteChar(46, basey + 5, "生 命: ", 0x0F);
WriteChar(46, basey + 7, "生 命: ", 0x0F);
WriteChar(43, basey + 9, "剩余敌方坦克: ", 0x0F);
WriteChar(43, basey + 11, "当前游戏速度: ", 0x0F);
WriteChar(43, basey + 13, "当前游戏状态: ", 0x0F);
WriteChar(40, basey + 15, "════════════════════", 0x01);
WriteChar(48, basey + 21, "帮 助", 0x02);
WriteChar(43, basey + 23, "坦克A: 方向键 w a s d ", 0x02);
WriteChar(43, basey + 25, " : 射击键 j 键 ", 0x02);
WriteChar(43, basey + 27, "坦克B: 方向键 ←↑→↓", 0x02);
WriteChar(43, basey + 29, " : 射击键 5 键 ", 0x02);
WriteChar(45, basey + 31, "+ - 调整游戏速度", 0x02);
WriteChar(45, basey + 33, "空格键 暂停游戏", 0x02);
WriteChar(45, basey + 35, "Esc键 退出游戏", 0x02);
}
/********************************************************
函数功能:更新游戏信息
参数1 :无
返回值 :无
*********************************************************/
void Game::UpdateSideScreen()
{
int basey = 2;
//清除上一次信息
WriteChar(49, basey, " ", 黑色);
WriteChar(50, basey + 3, " ", 黑色);
WriteChar(50, basey + 5, " ", 黑色);
WriteChar(50, basey + 7, " ", 黑色);
WriteChar(50, basey + 9, " ", 黑色);
WriteChar(50, basey + 11, " ", 黑色);
//更新
WriteChar(49, basey, "", 黄色);
printf("%d", GameLevel); //游戏关卡
WriteChar(50, basey + 3, "", 红色);
printf("%d", tankA.tankData.Score + tankB.tankData.Score);//更新游戏分数
WriteChar(50, basey + 5, "", 绿色);
printf("%d", tankA.tankData.Revive); //更新玩家A生命值(初始3条命)
WriteChar(50, basey + 7, "", 绿色);
printf("%d", tankB.tankData.Revive); //更新玩家B生命值
WriteChar(50, basey + 9, "", 红色);
printf("%d", tankA.remain_AI); //剩余AI数
WriteChar(50, basey + 11, "", 黄色);
printf("%d", 21-GameSpeed); //游戏速度
WriteChar(50, basey + 13, "正在进行", 黄色); //游戏状态
}
/********************************************************
函数功能:设置窗口信息
参数1 :窗口名称
参数2 :窗口宽度
参数3 :窗口高度
返回值 :成功返回真
*********************************************************/
bool Game::SetWindowSize(TCHAR* pszWindowTitle, short x, short y)
{
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTitle(pszWindowTitle); //设置窗口名称
//获取最大控制台窗口大小
COORD pos = GetLargestConsoleWindowSize(hStdOut);
COORD BuffSize = { pos.X + 1,pos.Y + 1 };
//设置控制台缓冲区大小
if (!SetConsoleScreenBufferSize(hStdOut, BuffSize))
{ //设置失败
printf("buffer err(%d,%d)%d\n", BuffSize.X, BuffSize.Y, GetLastError());
return false;
}
SMALL_RECT srctWindow = { 0,0,x,y }; //矩形,保存左上角坐标和右下角坐标
if (!SetConsoleWindowInfo(hStdOut, true, &srctWindow))
{ //设置控制台窗口大小失败
printf("size err%d\n", GetLastError());
return false;
}
//设置控制台缓冲区大小
COORD Buff = { x + 1,y + 1 };
if (!SetConsoleScreenBufferSize(hStdOut, Buff))
{ //设置失败
printf("buffer err(%d,%d)%d\n", Buff.X, Buff.Y, GetLastError());
return false;
}
return true;
}
/********************************************************
函数功能:游戏结束处理
参数 :0:我方死光1:老家炸毁
返回值 :无
*********************************************************/
void Game::GameOver(bool home)
{
int timing = 0, ColorNo = 1;
COLOR Color;
while (1)
{
if (timing++ % 30 == 0) //30次打印一次
{
Color = SetColor(ColorNo);
if(home)
{
WriteChar(18, 20, "老家炸毁!", Color); //主屏幕中心打印
}
WriteChar(18, 21, "游戏结束!", Color); //主屏幕中心打印
WriteChar(50, 15, "游戏结束!", Color); //副屏幕打印
WriteChar(45, 18, "请按回车键继续游戏!", Color);
WriteChar(45, 19, "或按 Esc键退出游戏!", Color);
if (++ColorNo == 8)
ColorNo = 1;
}
if (GetAsyncKeyState(0xD) & 0x8000) //回车键
{
ClearMainScreen(); //主屏清屏函数
GetMap(GameLevel); //获取关卡地图
InitGame(); //从本关重新开始
break;
}
if (GetAsyncKeyState(0x1B) & 0x8000) //Esc键退出
exit(0);
Sleep(20);
}
}
/********************************************************
函数功能:用户自定义地图
参数1 :无
返回值 :无
*********************************************************/
void Game::UserMap()
{
ClearFullScreen(); //清屏
DrawMainScreen(obMap); //打印主屏幕
DrawUserSideScreen(); //打印副屏幕
MouseOp(); //鼠标操作处理
}
/********************************************************
函数功能:游戏胜负检测
参数 :无
返回值 :无
*********************************************************/
void Game::GameCheck()
{
//胜利条件:AI坦克死光
//AItank死光,胜利
if (tankA.remain_AI == 0 && AItank[0].tankData.Alive == 0 && AItank[1].tankData.Alive == 0 &&
AItank[2].tankData.Alive == 0 && AItank[3].tankData.Alive == 0)
{
NextLevel();
}
//胜利处理函数
//失败条件:我方坦克死光或者老家炸毁(老家炸毁瞬间调用GameOver函数,在这不做判断)
if (SingleMode) //单人模式
{
if (tankA.tankData.Revive == 0 && tankA.tankData.Alive == 0)
GameOver(0);
}
else
{
if (tankA.tankData.Revive == 0 && tankA.tankData.Alive == 0 &&
tankB.tankData.Revive == 0 && tankB.tankData.Alive == 0)
GameOver(0);
}
}
/********************************************************
函数功能:清屏函数(全屏)
参数 :无
返回值 :无
*********************************************************/
void Game::ClearFullScreen()
{
for (int i = 0; i < 40; i++)
{
WriteChar(0, i, " \
", 0x00);
}
}
/********************************************************
函数功能:玩家坦克复活
参数 :无
返回值 :无
*********************************************************/
void Game::MytankRebirth()
{
if (SingleMode) //单人模式
{
while (1)
{
if (tankA.tankData.Alive == 0 && g_counter[玩家A复活时间]++ % 10 == 0) //复活时间1s
tankA.CreatePlayer();
else
return;
Sleep(100);
}
}
else
{ //双人模式
while (1)
{
if (tankA.tankData.Alive == 0 && g_counter[玩家A复活时间]++ % 10 == 0) //复活时间1s
tankA.CreatePlayer();
else if (tankB.tankData.Alive == 0 && g_counter[玩家B复活时间]++ % 10 == 0)
tankB.CreatePlayer();
else
return;
Sleep(100);
}
}
}