/*从网上看到的,觉得好玩,分享一下*/
#include <stdio.h>
#include <windows.h>
#include <time.h>
//里规格:长39*2=78 (真坐标)(假坐标宽为39) 高39
//外规格:长41*2=82 (真坐标)(假坐标宽为41) 高41
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define MAX_LEVEL 8
#define BULLET_NUM 20
#define MAX_LIFE 4
//程序中未写入函数参数表中且未说明的变量只有map二维数组,level_info数组和level
/*
此程序中涉及的x,y类的坐标值,分为以下两种:
假坐标:这里的坐标指的是以一个■长度为单位的坐标,而不是真正的coord坐标 (用于map数组的坐标)
真坐标:头文件自带的坐标结构coord中的坐标(也可以说是控制台里的真正坐标值)
区别:纵坐标y两值一致,假横坐标x值与真正coord横坐标(真坐标)关系是 x * 2 = coord 横坐标
coord横坐标既指GoTo函数中的x参数,因为本程序游戏界面以一个■长度为基本单位,
可以说涉及的coord横坐标全是偶数。既假坐标要变真坐标(变真坐标才能发挥真正作用),横坐标须乘以2
*/
typedef struct //这里的出现次序指的是一个AI_tank变量中的次序,游戏共有四个AI_tank变量
{ //∵设定每个AI_tank每种特殊坦克只出现一次 ∴fast_tank & firm_tank 最多出现次数不超过1
int fast_tank_order; //fast_tank出现的次序(在第fast_tank_order次复活出现,从第0次开始),且每个AI_tank只出现一次
int firm_tank_order; //firm_tank出现的次序,同上
} LevInfo; //关卡信息(准确说是该关出现的坦克信息)
LevInfo level_info [MAX_LEVEL] = {{-1,-1},{3,-1},{-1,3},{2,3},{2,3},{2,3},{2,3},{2,3}}; //初始化,-1代表没有该类型坦克
typedef struct //子弹结构体
{
int x,y; //子弹坐标,假坐标
int direction; //子弹方向变量
bool exist; //子弹存在与否的变量,1为存在,0不存在
bool initial; //子弹是否处于建立初状态的值,1为处于建立初状态,0为处于非建立初状态
bool my; //区分AI子弹与玩家子弹的标记,0为AI子弹,1为玩家(我的)子弹
} Bullet;
Bullet bullet [BULLET_NUM]; //考虑到地图上不太可能同时存在20颗子弹,所以数组元素设置20个
typedef struct //坦克结构体
{
int x,y; //坦克中心坐标
int direction; //坦克方向
int color; //颜色参方向数,1到6分别代表不同颜色,具体在PrintTank函数定义有说明
int model; //坦克图案模型,值为1,2,3,分别代表不同的坦克图案,0为我的坦克图案,AI不能使用
int stop; //只能是AI坦克使用的参数,非0代表坦克停止走动,0为可以走动
int revive; //坦克复活次数
int num; //AI坦克编号(固定值,为常量,初始化函数中定下)0~3
int CD; //发射子弹冷却计时
bool my; //是否敌方坦克参数,我的坦克此参数为1,为常量
bool alive; //存活为1,不存活为0
} Tank;
Tank AI_tank[4] , my_tank; //my_tank为我的坦克,Ai_tank 代表AI坦克
//∵所有的函数都有可能对全局变量map进行读写(改变),
//∴函数中不另说明是否会对全局变量map读写
//基本操作与游戏辅助函数
void GoToxy(int x,int y); //光标移动
void HideCursor(); //隐藏光标
void keyboard (); //接受键盘输入
void Initialize(); //初始化(含有对多个数据的读写)
void Stop(); //暂停
void Getmap(); //地图数据存放与获取
void Frame (); //打印游戏主体框架
void PrintMap(); //打印地图(地图既地图障碍物)(含对level的读取)
void SideScreen (); //副屏幕打印
void GameCheak(); //检测游戏输赢
void GameOver( bool home ); //游戏结束
void ClearMainScreen(); //主屏幕清屏函数∵system("cls")后打印框架有一定几率造成框架上移一行的错误∴单独编写清屏函数
void ColorChoose(int color); //颜色选择函数
void NextLevel(); //下一关(含有对level全局变量的读写)
//子弹部分
void BuildAIBullet(Tank *tank); //AI坦克发射子弹(含有对my_tank的读取,只读取了my_tank坐标)
void BuildBullet (Tank tank); //子弹发射(建立)(人机共用)(含全局变量bullet的修改)我的坦克发射子弹直接调用该函数,AI通过AIshoot间接调用
void BulletFly (Bullet bullet[BULLET_NUM]); //子弹移动和打击(人机共用),
void BulletHit (Bullet* bullet); //子弹碰撞(人机共用)(含Tank全局变量的修改),只通过BulletFly调用,子弹间的碰撞不在本函数,子弹间碰撞已在BulletShoot中检测并处理
void PrintBullet (int x,int y,int T); //打印子弹(人机共用)
void ClearBullet (int x,int y,int T); //清除子弹(人机共用)
int BulletCheak (int x,int y); //判断子弹前方情况(人机共用)
//坦克部分
void BuildAITank (int* position, Tank* AI_tank); //建立AI坦克
void BuildMyTank (Tank* my_tank); //建立我的坦克
void MoveAITank (Tank* AI_tank); //AI坦克移动
void MoveMyTank (int turn); //我的坦克移动,只通过keyboard函数调用,既键盘控制
void ClearTank (int x,int y); //清除坦克(人机共用)
void PrintTank (Tank tank); //打印坦克(人机共用)
bool TankCheak (Tank tank,int direction); //检测坦克dirtection方向的障碍物,返值1阻碍,0 畅通
int AIPositionCheak (int position); //检测AI坦克建立位置是否有障碍物AIPositionCheak
//DWORD WINAPI InputX(LPVOID lpParameter); //声明线程函数,用于检查X键输入并设置X键的输入冷却时间
//注意map数组应是纵坐标在前,横坐标在后,既map[y][x],(∵数组行长度在前,列长度在后)
//map里的值: 个位数的值为地图方块部分,百位数的值为坦克,子弹在map上没有值(子弹仅仅是一个假坐标)
//map里的值: 0为可通过陆地,1为红砖,2黄砖,5为水,100~103为敌方坦克,200为我的坦克,
//全局变量
int map[41][41]; //地图二维数组
int key_x; // X键是否被“读入”的变量,也是子弹是否可以发射的变,
int bul_num; //子弹编号
int position; //位置计数,对应AI坦克生成位置,-1为左位置,0为中间,1为右,2为我的坦克位置
int speed=7; //游戏速度,调整用
int level=1; //游戏关卡数
int score=0; //游戏分数
int remain_enemy; //剩余敌人(未出现的敌人)
char* tank_figure[4][3][4]=
{
{
{"◢┃◣", "◢━◣", "◢┳◣", "◢┳◣"},
{"┣●┫", "┣●┫", "━●┃", "┃●━"},
{"◥━◤", "◥┃◤", "◥┻◤", "◥┻◤"}
},
{
{"┏┃┓", "┏┳┓", "┏┳┓", "┏┳┓"},
{"┣●┫", "┣●┫", "━●┫", "┣●━"},
{"┗┻┛", "┗┃┛", "┗┻┛", "┗┻┛"}
},
{
{"┏┃┓", "◢━◣", "┏┳◣", "◢┳┓"},
{"┣●┫", "┣●┫", "━●┃", "┃●━"},
{"◥━◤", "┗┃┛", "┗┻◤", "◥┻┛"}
},
{
{"╔┃╗", "╔╦╗", "╔╦╗", "╔╦╗"},
{"╠█╣", "╠█╣", "━█╣", "╠█━"},
{"╚╩╝", "╚┃╝", "╚╩╝", "╚╩╝"}
}
};
int main () //主函数
{
int i;
unsigned int interval[12]={1,1,1,1,1,1,1,1,1,1,1,1} ; //间隔计数器数组,用于控制速度
srand(time(NULL)); //设置随机数种子(若不设置种子而调用rand会使每次运行的随机数序列一致)随机数序列指:如首次调用rand得到1,第二次得2,第三次3,则此次随机数序列为1,2,3
HideCursor(); //隐藏光标
system("mode con cols=112 lines=42"); //控制窗口大小
Frame (); //打印游戏主体框架
Initialize(); //初始化,全局变量level初值便是1
// HANDLE h1 , h2 ; //定义句柄变量
for(;;)
{
if(interval[0]++%speed==0) //速度调整用,假设interval[0]为a, 语句意为 a % 2==0,a=a+1;
{
GameCheak(); //游戏胜负检测
BulletFly ( bullet );
for(i=0 ; i<=3 ; i++) //AI坦克移动循环
{
if(AI_tank[i].model==2 && interval[i+1]++%2==0) //四个坦克中的快速坦克单独使用计数器1,2,3,4
MoveAITank( & AI_tank[i]);
if(AI_tank[i].model!=2 && interval[i+5]++%3==0) //四个坦克中的慢速坦克单独使用计数器5,6,7,8
MoveAITank( & AI_tank[i]);
}
for(i=0;i<=3;i++) //建立AI坦克部分
if(AI_tank[i].alive==0 && AI_tank[i].revive<4 && interval[9]++%90==0) //一个敌方坦克每局只有4条命
{ //如果坦克不存活。计时,每次建立有间隔 1750 ms
BuildAITank( &position, & AI_tank[i] ); //建立AI坦克(复活)
break; //每次循环只建立一个坦克
}
for(i=0;i<=3;i++)
if(AI_tank[i].alive)
BuildAIBullet(&AI_tank[i]); //AIshoot自带int自增计数CD,不使用main中的CD interval
if(my_tank.alive && interval[10]++%2==0 )
keyboard ();
if(my_tank.alive==0 && interval[11]++%30==0 && my_tank.revive < MAX_LIFE)
BuildMyTank( &my_tank );
}
Sleep(5);
}
return 0;
}
/*//这里的多线程暂时不用 //x键用于子弹发射,x键的冷却时间不能和上下左右一同设置,那样就太快了
DWORD WINAPI InputX(LPVOID lpParameter) //如果不用多线程运行,那么在x键冷却时间内程序会因Sleep将会挂起,暂停运行
{ //因为只有一个变量改变,而且变量改变先后顺序是显而易见的,所以不必设置缓冲区
for(;;)
{
if(GetAsyncKeyState( 88 )& 0x8000) //88为x键键值,当摁下x并且x键处于可输入状态
{
key_x=1; // X键是否允许被“读入”的变量,也是子弹是否可以发射的变量
Sleep(600); // 子线程Sleep中,x就不能被"读入",主线程每操作完一次子弹发射,key_x会归零
}
Sleep(10);
}
return 0;
}*/
void keyboard ()
{ // kbhit() getch() 用法可用但是不好用
/*
函数功能:该函数判断在此函数被调用时,某个键是处于UP状态还是处于DOWN状态,及前次调用GetAsyncKeyState函数后,
是否按过此键.如果返回值的最高位被置位,那么该键处于DOWN状态;如果最低位被置位,那么在前一次调用此函数后,此键被按过,
否则表示该键没被按过.
这里GetAsyncKeyState比 kbhit() + getch() 好用,操作更顺畅. GetAsyncKeyState的返回值表示两个内容,
一个是最高位bit的值,代表这个键是否被按下。一个是最低位bit的值,代表上次调用GetAsyncKeyState后,这个键是否被按下。
&为与操作,&0x8000就是判断这个返回值的高位字节。如果high-order bit是1,则是按下状态,否则是弹起状态,为0
*/
int count=0;
if (GetAsyncKeyState(VK_UP)& 0x8000)
MoveMyTank( UP );
else if (GetAsyncKeyState(VK_DOWN)& 0x8000)
MoveMyTank( DOWN );
else if (GetAsyncKeyState(VK_LEFT)& 0x8000)
MoveMyTank( LEFT );
else if (GetAsyncKeyState(VK_RIGHT)& 0x8000)
MoveMyTank( RIGHT );
else if (GetAsyncKeyState( 0x1B )& 0x8000) // Esc键
exit(0); //退出程序函数
else if (GetAsyncKeyState( 0x20 )& 0x8000) //空格
Stop();
else if (count++%7==0) //这里添加计数器是为了防止按键粘连不能达到微调效果
{
if (speed>1 && GetAsyncKeyState( 0x6B )& 0x8000) // +键
{
speed--;
GoToxy(102,11); //在副屏幕打印出当前速度
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_RED);
printf("%d ",21-speed); //副屏幕显示的速度为1~10
}
else if (speed<20 && GetAsyncKeyState( 0x6D )& 0x8000) // - 键
{
speed++;
GoToxy(102,11); //在副屏幕打印出当前速度
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_RED);
printf("%d ",21-speed); //副屏幕显示的速度为1~10
}
}
if(my_tank.CD==7)
{
if(GetAsyncKeyState( 88 )& 0x8000)
{
BuildBullet(my_tank);
my_tank.CD=0;
}
}
else
my_tank.CD++;
}
0.0分
1 人评分
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:590 |
【亲和数】 (C语言代码)浏览:588 |
Pascal三角 (C语言代码)浏览:1252 |
C语言程序设计教程(第三版)课后习题7.1 (C语言代码)浏览:1267 |
WU-字符串比较 (C++代码)浏览:824 |
C语言程序设计教程(第三版)课后习题1.5 (C语言代码)浏览:468 |
Hello, world! (C语言代码)浏览:766 |
简单的a+b (C语言代码)浏览:457 |
A+B for Input-Output Practice (VII) (C语言代码)浏览:566 |
The 3n + 1 problem (C语言代码)浏览:550 |