代码地址如下:
http://www.demodashi.com/demo/14259.html

坦克大战-C语言-详注版

概述

本文详述了C语言版坦克大战游戏的原理以及实现方法,对游戏代码进行了详细的分析和注释,通过本文能够让大家对WIN32编程框架有一个大致了解,对C语言运用有一定提高,同时也能给大家提供一个C语言小游戏编程的思路,也能完全够通过自己的实力去编写一个属于自己的游戏.

游戏体验

视频版:坦克大战-C语言版-GameTank

代码框架

坦克大战游戏代码框架如下

main.c 中,创建应用窗口,并初始化一些系统资源,然后初始化gdi,初始化坦克大战游戏.
gdi.c 中,对系统的HDC及绘图接口进行了一次封装,使用WIN32系统提供的绘图接口来实现我们自己的图形绘制API.这里使用到了双缓冲技术,下文会简单介绍.
Tank.c 中,实现了坦克大战的游戏逻辑,包括贴图系统,地图系统,坦克制造,炮弹制造,装备生成,坦克移动,炮弹移动,以及坦克被炮弹击中,坦克吃到装备,等等.
下面将对代码进行详细的分析.

代码主函数

main.c 中,创建应用窗口,并初始化一些系统资源,然后初始化gdi,初始化坦克大战游戏.

//主函数
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR    lpCmdLine,_In_ int       nCmdShow)
{//注册窗口类,创建窗口,初始化等: MyRegisterClass(hInstance);InitInstance(hInstance, nCmdShow);//显示初始化及游戏初始化gdi_init(hWnd);tank_init();//创建坦克大战运行线程hTankRunT = CreateThread(NULL, 0, TankRun, NULL, 0, &dwTankRunTId);//快捷键hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GAMETANK));// 主消息循环: while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){//TranslateMessage(&msg);DispatchMessage(&msg);}}return (int)msg.wParam;
}//目的: 注册窗口类。
ATOM MyRegisterClass(HINSTANCE hInstance)
{WNDCLASSEXW wcex;......wcex.lpfnWndProc = WndProc;//绑定窗口消息处理函数......return RegisterClassEx(&wcex);//注册窗口类
}//目的: 保存实例句柄并创建主窗口
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{hInst = hInstance; // 将实例句柄存储在全局变量中......hWnd = CreateWindow(......);//无窗口创建数据ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}//目的:    处理主窗口的消息。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{......switch (message){case WM_COMMAND:{// 分析菜单选择: switch (wmId){case IDM_ABOUT: //关于GAME_CTRL.run = FALSE;//点击对话框的时候暂停DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_README: //说明GAME_CTRL.run = FALSE;//点击对话框的时候暂停DialogBox(hInst, MAKEINTRESOURCE(IDD_READMEBOX), hWnd, Readme);break;case IDM_EXIT://退出DestroyWindow(hWnd);break;......default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;case WM_PAINT:{hdc = BeginPaint(hWnd, &ps);// TODO: 在此处添加使用 hdc 的任何绘图代码...gdi_update();EndPaint(hWnd, &ps);}break;case WM_DESTROY:gdi_dinit();PostQuitMessage(0);break;case WM_KEYDOWN:switch (wParam){case VK_UP://上GAME_CTRL.dir = DIR_UP;break;case VK_DOWN://下GAME_CTRL.dir = DIR_DOWN;break;case VK_LEFT://左GAME_CTRL.dir = DIR_LEFT;break;case VK_RIGHT://右GAME_CTRL.dir = DIR_RIGHT;break;case VK_RETURN://回车键开火GAME_CTRL.fire = TRUE;break;......default:break;}break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}//游戏运行线程
DWORD WINAPI TankRun(LPVOID lpProgram)
{while (TRUE){if (GAME_CTRL.run)//暂停游戏{if (GAME_CTRL.auto_fire != FALSE){// GameTankDir = ((i++) % DIR_MAX);GAME_CTRL.fire = TRUE;}if (tank_run(&(GAME_CTRL.dir), &(GAME_CTRL.fire)/*, GAME_CTRL.difficult*/) != RTN_OK){//DEBUG_LOG("ERR");MessageBox(/*NULL*/hWnd, TEXT("你输了!"), TEXT("你输了!"), MB_OK);GameCtrlInit();tank_init();}Sleep(1 + GAME_CTRL.speed);}}return 0;
}

GDI绘图

gdi.c 中,对系统的HDC及绘图接口进行了一次封装,使用WIN32系统提供的绘图接口来实现我们自己的图形绘制API.这里使用到了双缓冲技术.

双缓冲技术

坦克大战的每一次运行,坦克和炮弹的每一次移动,我们都要不断的清除屏幕上原来的内容,然后重新绘制新的内容,假如我们每一次更改显示内容都直接调用WIN32的api实时的刷新到屏幕上,一旦游戏运行速度很快的时候,就会造成屏幕上的内容一直在闪烁,非常影响游戏体验.
双缓冲技术主要是为了解决坦克大战实时刷新造成的屏幕闪烁问题.其原理就是,加入地图上共有10辆坦在战斗,每辆坦克都在移动,每移动一步我们都需要重新计算并显示10辆坦克的位置,但是我们不必在计算每一辆坦克的时候都把他刷新到屏幕上,而是先把这辆坦克绘制到内存上,等到所有的坦克都计算并完成移动之后,再同一把内存上的内容刷新到屏幕上,这样做就大大减少了刷新屏幕的次数,也就可以避免实时刷新造成的屏幕闪烁问题.
更详细的介绍,请参考下面这篇文章:

双缓冲技术讲解

#include "Gdi.h"HPEN        hGdiPen = NULL;           //画笔
HBRUSH      hGdiBrush = NULL;      //画刷
HDC         mGdiHdc;                //内存设备(双缓冲技术)
HDC         hGdiHdc;                //硬件设备
HWND        hGdiWnd;                //窗口
RECT        hGdiWndRect;            //窗口客户区大小HBITMAP     mGdiBmp;
HBITMAP     mGdiBmpOld;#define maxX                SCREEN_X
#define maxY                SCREEN_Ystatic void _gdi_clr_pencol(HPEN _hGdiPen)
{DeleteObject(_hGdiPen);//释放资源SelectObject(mGdiHdc, hGdiPen);//恢复初始画刷
}static HPEN _gdi_set_pencol(int32 color)
{HPEN _hGdiPen;COLORREF color_t = (COLORREF)color;_hGdiPen = CreatePen(PS_SOLID, 1, color_t);//画笔hGdiPen = SelectObject(mGdiHdc, _hGdiPen);//为缓存DC选择画笔return _hGdiPen;
}static void _gdi_clr_brushcol(HBRUSH _hGdiBrush)
{DeleteObject(_hGdiBrush);//释放资源SelectObject(mGdiHdc, hGdiBrush);//恢复初始画刷
}static HBRUSH _gdi_set_brushcol(int32 color)
{HBRUSH _hGdiBrush;COLORREF color_t = (COLORREF)color;_hGdiBrush = CreateSolidBrush(color_t);//画刷hGdiBrush = SelectObject(mGdiHdc, _hGdiBrush);//为缓存DC选择画刷return _hGdiBrush;
}/** gdi_clear:*    Clear the display to the given colour.********************************************************************************/
void gdi_clear(int32 colour)
{gdi_rectangle(0, 0, maxX, maxY, colour, TRUE);}/** gdi_set_point:* Plot a pixel.********************************************************************************/
void gdi_set_point(int32 x, int32 y, int32 colour)
{x = ((x < 0) ? 0 : ((x > (maxX - 1)) ? (maxX - 1) : x));y = ((y < 0) ? 0 : ((y > (maxY - 1)) ? (maxY - 1) : y));HPEN hPen = _gdi_set_pencol(colour);SetPixel(mGdiHdc, x, y, colour);_gdi_clr_pencol(hPen);
}/** gdi_get_point:*    Plot a pixel.********************************************************************************/
int32 gdi_get_point(int32 x, int32 y)
{x = ((x < 0) ? 0 : ((x > (maxX - 1)) ? (maxX - 1) : x));y = ((y < 0) ? 0 : ((y > (maxY - 1)) ? (maxY - 1) : y));COLORREF col = GetPixel(mGdiHdc, x, y);return (int32)col;
}....../*
* gdi_triangle:
*   A triangle is a spoilt days fishing
*******************************************************************************
*/
void gdi_triangle(int32 x1, int32 y1, int32 x2, int32 y2, int32 colour, int32 filled)
{HPEN _hPen;HBRUSH _hBrush;POINT triangle[3] = { 0 };int32 halfx = 0;halfx = ((x2 - x1 + 1) / 2);triangle[0].x = x1 + halfx;triangle[0].y = y1;triangle[1].x = x1;triangle[1].y = y2;triangle[2].x = x2;triangle[2].y = y2;if (filled){_hPen = _gdi_set_pencol(colour);_hBrush = _gdi_set_brushcol(colour);Polygon(mGdiHdc, triangle, 3);_gdi_clr_pencol(_hPen);_gdi_clr_brushcol(_hBrush);}else{_hPen = _gdi_set_pencol(colour);Polygon(mGdiHdc, triangle, 3);_gdi_clr_pencol(_hPen);}
}....../** gdi_init:*   Initialise the display and GPIO.********************************************************************************/
int32 gdi_init(HWND hWnd)
{int32 hGdiWndWidth = 0;//窗口客户区宽度int32 hGdiWndHeight = 0;//窗口客户区高度hGdiWnd = hWnd;hGdiHdc = GetDC(hGdiWnd);                     //获取硬件设备mGdiHdc = CreateCompatibleDC(hGdiHdc);        //创建软件设备,双缓冲技术GetClientRect(hGdiWnd, &hGdiWndRect);hGdiWndWidth = hGdiWndRect.right - hGdiWndRect.left;hGdiWndHeight = hGdiWndRect.bottom - hGdiWndRect.top;//双缓冲技术核心:先创建一个软件绘图设备HDC,为这个软件HDC选择一个内存画布,//所有的绘图都通过这个软件HDC来完成,都被绘制到了这个内存画布之上//当所有的绘图工作都完成之后,就通过BitBlt把内存画布上的内容拷贝到硬件绘图设备HDC上,//这样就完成了显示(gdi_update)mGdiBmp = CreateCompatibleBitmap(hGdiHdc, hGdiWndWidth, hGdiWndHeight);//创建BMP画布mGdiBmpOld = SelectObject(mGdiHdc, mGdiBmp);//为软件HDC设备选择BMP画布return OK;
}int32 gdi_dinit(void)
{DeleteObject(mGdiBmp);//删除BMP画布DeleteObject(mGdiHdc);//删除软件设备DeleteDC(hGdiHdc);//删除硬件设备return OK;
}int32 gdi_update(void)
{int32 hGdiWndWidth = 0;//窗口客户区宽度int32 hGdiWndHeight = 0;//窗口客户区高度hGdiWndWidth = hGdiWndRect.right - hGdiWndRect.left;hGdiWndHeight = hGdiWndRect.bottom - hGdiWndRect.top;//把软件设备上的内容拷贝到硬件设备上BitBlt(hGdiHdc, 0, 0, hGdiWndWidth, hGdiWndHeight, mGdiHdc, 0, 0, SRCCOPY);return OK;
}

坦克大战

坦克系统


如上图所示,每个坦克由6个小方块组成,把这6个小方块按顺序标号.每个坦克有上下左右四个方向.为了简化程序操作,把这四个方向的坦克放在一个(二维)数组 TANK_SHAPE_BOX 中,即TANK_SHAPE_BOX[4][6],4表示共有四种形状的坦克(四个方向),6表示每种坦克由6个小方块. 数组的每个元素保存的是这6个小方块的相对坐标.坦克的每个方块的实际坐标可以通过坦克左上角的坐标与这6个相对坐标计算得到.

typedef enum property_e
{PR_MIN = 0,PR_NULL = 0,//无PR_WALL, //墙PR_WEAPON, //武器PR_LIFE, //装备PR_BOMB, //炮弹PR_MYSELF, //自己PR_ENMY, //敌人PR_MAX
} property_t; //属性typedef struct point_s
{uint32_t x;uint32_t y;uint32_t col;
} point_t;//坐标点typedef struct tank_s
{int8        valid;//是否有效dir_t       dir;//坦克方向,同时也是其在TANK_SHAPE_BOX中的索引point_t     pnt;//坦克左上角的坐标,其它点的相对坐标在TANK_SHAPE_BOX中property_t  pr;//属性,只能是 PR_MYSEL, PR_ENMY, PR_NULL 三个之一int32       lf;//生命值,正常情况>1, 当为1时表示被击毁(变为黄色,随后死亡), 当为0时表示死亡int32       wep;//武器,>0时表示使用超级武器,每次发射3颗炮弹int32       mv;//移动(步数)int32       fire;//开火倒计时,int32       kill;//击杀的敌军数量
} tank_t;const point_t TANK_SHAPE_BOX[TANK_SHAPE_NUM_MAX][TANK_SHAPE_PNT_MAX]=
{//不论哪个方向的坦克,其车身点在数组中的位置都是固定的
{{1,0,TRUE},{0,1,TRUE},{2,1,TRUE},{1,1,TRUE},{0,2,TRUE},{2,2,TRUE}},//上
{{2,1,TRUE},{1,0,TRUE},{1,2,TRUE},{1,1,TRUE},{0,0,TRUE},{0,2,TRUE}},//右
{{1,2,TRUE},{2,1,TRUE},{0,1,TRUE},{1,1,TRUE},{2,0,TRUE},{0,0,TRUE}},//下
{{0,1,TRUE},{1,2,TRUE},{1,0,TRUE},{1,1,TRUE},{2,2,TRUE},{2,0,TRUE}}//左
};//参战斗坦克(最大),坦克仓库,保存了所有坦克的信息,包括在地图上的坐标(只需要保存其左上角的坐标),敌我属性,武器属性,生命值,移动等等
static tank_t TANK_WAR_BOX[TANK_WAR_NUM_MAX]={0};//制造一辆坦克
tank_t* tank_create_atank(tank_t* tank, int32 pr)
{point_t pnt = { 0 };int32 n = TankMaxX * TankMaxY;int32 i = 0;if (tank == RTN_NULL){return RTN_NULL;}memset(tank, 0, sizeof(tank_t));tank->valid = TRUE;tank->pr = PR_NULL;tank->dir = tank_get_rand(DIR_MIN, DIR_MAX);//获取一个随机的方向tank->wep = WP_NONE;tank->lf = LF_LIVE;tank->kill = 0;tank->bomb = 0;if (pr == PR_MYSELF)//我军{tank->mv = MV_STOP;//默认不可移动,(手动操控)tank->fire = 1;tank->pr = PR_MYSELF;}else//敌军{tank->mv = tank_get_rand(MV_MOVE, min(TankMaxX, TankMaxY));//产生一个随机的移动步数tank->fire = tank_get_rand(TANK_TIMER_FIRE_MIN, TANK_TIMER_FIRE_MAX);tank->pr = PR_ENMY;}while (n--)//寻找可以放置坦克的随机点{tank_get_randpnt(&(tank->pnt));//生成一个随机点for (i = 0; i < TANK_SHAPE_PNT_MAX; i++){pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][i].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][i].y;if (tank_get_warmap(&pnt) != PR_NULL){break;}}if (i >= TANK_SHAPE_PNT_MAX){tank->pnt.col = TANK_PR_COLOUR[tank->pr];return tank;//该位置可以放下一坦克}}memset(tank, 0, sizeof(tank_t));tank->pr = PR_NULL;tank->valid = FALSE;return RTN_NULL;
}//在地图上绘制坦克
int32 tank_draw_atank(tank_t* tank)
{int32 i = 0;point_t pnt = { 0 };int32 pr = 0;if (tank == NULL){return RTN_ERR;}if (tank->valid == FALSE){return RTN_ERR;}for (i = 0; i < TANK_SHAPE_PNT_MAX; i++){pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][i].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][i].y;pnt.col = tank->pnt.col;tank_set_warmap(&pnt, tank->pr);}return RTN_OK;
}//检查坦克是否能够继续移动
int32 tank_check_atank(tank_t* tank)
{int32 i = 0, pr = 0;point_t pnt = { 0 };point_t* ppnt = NULL;if (tank == NULL){return FALSE;}if ((tank->pr == PR_NULL) || (tank->valid == FALSE)){return FALSE;}//坦克不能越界if ((tank->pnt.x < 0) || (tank->pnt.x >= TankMaxX) ||(tank->pnt.y < 0) || (tank->pnt.y >= TankMaxY)){return FALSE;}for (i = 0; i < /*TANK_SHAPE_PNT_MAX*/3; i++)//只需要检查前三个点能否移动{pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][i].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][i].y;ppnt = tank_get_nextpnt(tank->dir, &pnt);if (ppnt == RTN_NULL){return FALSE;}//坦克不能越界if ((ppnt->x < 0) || (ppnt->x >= TankMaxX) ||(ppnt->y < 0) || (ppnt->y >= TankMaxY)){return FALSE;}//坦克的下一个位置必须不是坦克或墙pr = tank_get_warmap(ppnt);if ((pr == PR_WALL) || (pr == PR_MYSELF) || (pr == PR_ENMY)){return FALSE;}}return TRUE;
}

弹药系统

typedef struct bomb_s
{int8        valid;dir_t       dir;//炮弹飞行方向point_t     pnt;//炮弹的坐标点property_t  pr;//炮弹属性(敌人的炮弹还是自己的炮弹)tank_t*     ptank;//这颗炮弹是哪辆坦克发射的
} bomb_t;static bomb_t   TANK_BOMB_BOX[TANK_BOMB_NUM_MAX] = { 0 };//同一时刻所有坦克发出的炮弹//制造一颗炮弹
bomb_t* tank_create_abomb(tank_t* tank, bomb_t* bomb, int32* bnum)
{int32 i = 0;point_t pnt = { 0 };point_t* ppnt = NULL;if ((tank == NULL) || (bomb == NULL) || (bnum == NULL)){return RTN_NULL;}if (tank->pr == PR_NULL){return RTN_NULL;}if (tank->wep > WP_NONE)//加强版武器,每次发射三颗炮弹{//tank->wep -= 1;//武器使用一次tank->bomb += 3;for (i = 0; i < 3; i++){*bnum = i + 1;pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][i].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][i].y;ppnt = tank_get_nextpnt(tank->dir, &pnt);if (ppnt == RTN_NULL){*bnum = 0;memset(&(bomb[i]), 0, sizeof(bomb_t));bomb[i].valid = FALSE;bomb[i].pr = PR_NULL;return RTN_NULL;}bomb[i].valid = TRUE;bomb[i].dir = tank->dir;bomb[i].pr = tank->pr;bomb[i].ptank = tank;ppnt->col = TANK_PR_COLOUR[PR_BOMB];COPY_POINT(&(bomb[i].pnt), ppnt);}}else//普通武器,每次发射一颗炮弹{tank->bomb += 1;*bnum = 1;pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][0].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][0].y;ppnt = tank_get_nextpnt(tank->dir, &pnt);if (ppnt == RTN_NULL){*bnum = 0;memset(&(bomb[0]), 0, sizeof(bomb_t));bomb[0].valid = FALSE;bomb[0].pr = PR_NULL;return RTN_NULL;}bomb[0].valid = TRUE;bomb[0].dir = tank->dir;bomb[0].pr = tank->pr;bomb[0].ptank = tank;ppnt->col = TANK_PR_COLOUR[PR_BOMB];COPY_POINT(&(bomb[0].pnt), ppnt);}return bomb;
}//在地图上绘制炮弹
int32 tank_draw_abomb(bomb_t* bomb)
{int32 pr = 0;if (bomb == NULL){return RTN_ERR;}if (bomb->valid == FALSE){return RTN_ERR;}//炮弹也分为敌方炮弹和我方炮弹,用pr区分//但是不管敌方炮弹还是我方炮弹,显示的形状都//是一样的,都是(黄色)圆形,这里要注意区分//pr用于控制显示的形状pr = ((bomb->pr != PR_NULL) ? PR_BOMB : PR_NULL);tank_set_warmap(&(bomb->pnt), pr);return RTN_OK;
}//检查炮弹能否继续移动
int32 tank_check_abomb(bomb_t* bomb)
{if (bomb == NULL){return FALSE;}if ((bomb->pr == PR_NULL) || (bomb->valid == FALSE)){return FALSE;}if ((bomb->pnt.x < 0) || (bomb->pnt.x >= TankMaxX) ||(bomb->pnt.y < 0) || (bomb->pnt.y >= TankMaxY)){return FALSE;}return TRUE;
}//在弹药库中查照炮弹(根据坐标)
bomb_t* tank_search_abomb_inbox(point_t* point)
{int32 i = 0;if (point == NULL){return RTN_NULL;}if ((point->x < 0) || (point->x >= TankMaxX) ||(point->y < 0) || (point->y >= TankMaxY)){return RTN_NULL;}for (i = 0; i < TANK_BOMB_NUM_MAX; i++){if ((TANK_BOMB_BOX[i].pr == PR_NULL) ||(TANK_BOMB_BOX[i].valid == FALSE))//过滤无效的元素{continue;}if ((point->x == TANK_BOMB_BOX[i].pnt.x) &&(point->y == TANK_BOMB_BOX[i].pnt.y)){return &(TANK_BOMB_BOX[i]);}}return RTN_NULL;
}

装备系统

typedef struct equip_s
{int8        valid;//装备是否有效point_t     pnt;//装备的坐标点property_t  pr;//装备属性(武器还是生命) int32       tmr;//装备存活定时器,到期后装备消失
} equip_t;static equip_t TANK_EQUIP = { 0 };//坦克的装备//创建一个装备
equip_t* tank_create_aequip(equip_t* equip)
{point_t pnt = { 0 };int32 n = TankMaxX * TankMaxY;int32 i = 0;if (equip == RTN_NULL){return RTN_NULL;}equip->tmr = tank_get_rand(TANK_TIMER_EQUIP_MIN, TANK_TIMER_EQUIP_MAX);equip->pr = tank_get_rand(TANK_TIMER_MIN, TANK_TIMER_MAX);equip->valid = TRUE;if ((equip->pr % 2) == 0){equip->pr = PR_LIFE;}else{equip->pr = PR_WEAPON;}while (n--)//寻找可以放置装备的随机点{tank_get_randpnt(&(equip->pnt));//生成一个随机点pnt.x = equip->pnt.x;pnt.y = equip->pnt.y;if (tank_get_warmap(&pnt) == PR_NULL){equip->pnt.col = TANK_PR_COLOUR[equip->pr];return equip;}}memset(equip, 0, sizeof(equip_t));equip->valid = FALSE;equip->pr = PR_NULL;return RTN_NULL;
}//在地图上绘制装备
uint32 tank_draw_aequip(equip_t* equip)
{point_t pnt = { 0 };int32 n = TankMaxX * TankMaxY;int32 i = 0;if (equip == RTN_NULL){return RTN_ERR;}if (equip->valid == FALSE){return RTN_ERR;}tank_set_warmap(&(equip->pnt), equip->pr);return RTN_OK;
}//坦克大战装备系统
int32 tank_move_equip(void)
{equip_t equip = { 0 };//产生装备if (TANK_PR.create_equp_tmr > 0){TANK_PR.create_equp_tmr -= 1;}else{if (tank_create_aequip(&equip) != RTN_NULL){memcpy(&TANK_EQUIP, &equip, sizeof(equip_t));//产生新的}TANK_PR.create_equp_tmr = tank_get_rand(TANK_TIMER_EQUIP_MIN, TANK_TIMER_EQUIP_MAX);//重启生成定时器}if ((TANK_EQUIP.pr != PR_NULL) && (TANK_EQUIP.valid != FALSE)){if (TANK_EQUIP.tmr > 0){TANK_EQUIP.tmr -= 1;}else{TANK_EQUIP.pr = PR_NULL;TANK_EQUIP.valid = FALSE;TANK_PR.create_equp_tmr = tank_get_rand(TANK_TIMER_EQUIP_MIN, TANK_TIMER_EQUIP_MAX);//重启生成定时器}}return RTN_OK;
}

移动坦克和炮弹

//坦克移动
int32 tank_move_atank(dir_t dir)
{int32 i = 0;for (i = 0; i < TANK_WAR_NUM_MAX; i++){if ((TANK_WAR_BOX[i].pr == PR_NULL) || //过滤无效的元素(TANK_WAR_BOX[i].valid == FALSE)){continue;}if (TANK_WAR_BOX[i].pr == PR_ENMY)//敌军{if (TANK_WAR_BOX[i].mv > MV_STOP)//步数还未用完{//继续移动TANK_WAR_BOX[i].mv -= 1;if (tank_check_atank(&(TANK_WAR_BOX[i])) != FALSE){//还能继续移动tank_get_nextpnt(TANK_WAR_BOX[i].dir, &(TANK_WAR_BOX[i].pnt));//坦克移动一步}else{//不能继续移动,调转方向TANK_WAR_BOX[i].dir = tank_get_rand(DIR_MIN, DIR_MAX);//获取一个随机的方向}}else{//调转方向TANK_WAR_BOX[i].dir = tank_get_rand(DIR_MIN, DIR_MAX);//获取一个随机的方向TANK_WAR_BOX[i].mv = tank_get_rand(MV_MOVE, min(TankMaxX, TankMaxY));//产生一个随机的移动步数}}else if (TANK_WAR_BOX[i].pr == PR_MYSELF)//我军{if (dir < DIR_MAX){//移动if (TANK_WAR_BOX[i].dir == dir){//继续移动if (tank_check_atank(&(TANK_WAR_BOX[i])) != FALSE){//还能继续移动tank_get_nextpnt(TANK_WAR_BOX[i].dir, &(TANK_WAR_BOX[i].pnt));//坦克移动一步}}else{//调转方向TANK_WAR_BOX[i].dir = dir;}}}}return RTN_OK;
}//炮弹移动
int32 tank_move_abomb(void)
{int32 i = 0;//point_t pnt = { 0 };//tank_t* ptank = NULL;for (i = 0; i < TANK_BOMB_NUM_MAX; i++){//if (TANK_BOMB_BOX_VALID[i] == 0)//过滤无效的元素if ((TANK_BOMB_BOX[i].pr == PR_NULL) ||(TANK_BOMB_BOX[i].valid == FALSE)){continue;}if (tank_check_abomb(&(TANK_BOMB_BOX[i])) == FALSE){//不能继续移动TANK_BOMB_BOX[i].pr = PR_NULL;}else{//继续移动tank_get_nextpnt(TANK_BOMB_BOX[i].dir, &(TANK_BOMB_BOX[i].pnt));}}return RTN_OK;
}

碰撞检测

//坦克移动侦测(碰撞检测)
int32 tank_detect(tank_t* tank)
{int32 i = 0, pr = 0;point_t pnt = { 0 };bomb_t* bomb = NULL;if (tank == NULL){return RTN_ERR;}if ((tank->pr == PR_NULL) || (tank->valid == FALSE)){return RTN_OK;}for (i = 0; i < TANK_SHAPE_PNT_MAX; i++){pnt.x = tank->pnt.x + TANK_SHAPE_BOX[tank->dir][i].x;pnt.y = tank->pnt.y + TANK_SHAPE_BOX[tank->dir][i].y;pr = tank_get_warmap(&(pnt));switch (pr){case PR_WEAPON://吃到武器tank->wep += TANK_WEAPON_LIFE_MAX;//增加武器可以使用次数TANK_EQUIP.tmr = 0;//装备消失TANK_EQUIP.pr = PR_NULL;tank_sound(IDR_WAVE_WEAPON);break;case PR_LIFE://吃到装备tank->lf += 1;//吃到装备,生命值+1TANK_EQUIP.tmr = 0;//装备消失TANK_EQUIP.pr = PR_NULL;tank_sound(IDR_WAVE_LIFE);break;case PR_BOMB://吃到炮弹bomb = tank_search_abomb_inbox(&(pnt));if (bomb == RTN_NULL){break;}if (bomb->pr != tank->pr)//自己的炮弹不能炸自己{if (tank->lf > LF_LIVE){tank->lf -= 1;//生命值-1}if (tank->lf = LF_LIVE){tank->lf -= 1;//生命值-1bomb->ptank->kill += 1;//为这颗炮弹的主人增加一次击杀记录}bomb->pr = PR_NULL;//击中目标的炮弹失效tank->wep = WP_NONE;//坦克被击中之后其武器失效tank_sound(IDR_WAVE_BOMB);}break;case PR_NULL:case PR_WALL:case PR_ENMY:case PR_MYSELF:default:break;}}return RTN_OK;
}

清理战场

//清理战场
int32 tank_clean(tank_t* tank)
{int32 i = 0, j = 0, k = 0;static int8 war_num_flag = FALSE;tank_t ttank = { 0 };tank_t* ptank = NULL;if (tank == NULL){return RTN_ERR;}if ((tank->pr == PR_NULL) || (tank->valid == FALSE)){return RTN_OK;}//打扫战场if (tank->lf == LF_DIE){//清理掉已经炸毁的坦克if (tank->pr == PR_MYSELF){//我军战败tank->pr = PR_NULL;//DEBUG_LOG("");return RTN_ERR;}if (tank->pr == PR_ENMY){//敌军损毁一辆坦克//销毁一辆损毁的坦克tank->pr = PR_NULL;}}if (tank->lf == LF_BURN){//把正在燃烧的坦克标记为炸毁,下次清理tank->lf = LF_DIE;tank->mv = MV_STOP;//停止移动//tank->pr = PR_BOMB;//将其属性改为炮弹,表示即将爆炸,其他坦克撞到它也会失去一颗生命值tank->pnt.col = TANK_PR_COLOUR[PR_BOMB];//将其颜色改为和炮弹同色}//我军每击杀5辆坦克敌军坦克数量+1if ((tank->pr == PR_MYSELF) && (tank->kill > 0)){if ((tank->kill % 5) == 0){if (war_num_flag == FALSE){war_num_flag = TRUE;TANK_PR.war_tank_num += 1;}}else{war_num_flag = FALSE;}}tank_count();//统计坦克的数目//增援新坦克k = 0;for (i = TANK_PR.cur_tank_num; i < TANK_PR.war_tank_num; i++){ptank = tank_create_atank(&ttank, PR_ENMY);if (ptank != RTN_NULL){//寻找位置,把新坦克插入到队列中for (j = k; j < TANK_WAR_NUM_MAX; j++){if ((TANK_WAR_BOX[j].pr == PR_NULL) &&(TANK_WAR_BOX[j].valid == FALSE)){k = j + 1;memcpy(&(TANK_WAR_BOX[j]), ptank, sizeof(tank_t));break;}}}}return RTN_OK;
}

坦克开火

//坦克开火
int32 tank_fire(tank_t* tank, int32* fire)
{int32 m = 0, k = 0, j = 0;int32 bnum = 0;bomb_t bomb[3] = { 0 };if ((tank == NULL) || (fire == NULL)){return RTN_ERR;}if ((tank->pr == PR_NULL) || (tank->valid == FALSE)){return RTN_OK;}if (tank->pr == PR_MYSELF){//我军开火方式由手动控制if (*fire){*fire = FALSE;tank->fire = 0;}else{tank->fire = 1;}}//开火倒计时if (tank->fire > 0){tank->fire -= 1;}else{tank->fire = tank_get_rand(TANK_TIMER_FIRE_MIN, TANK_TIMER_FIRE_MAX);//坦克发射炮弹(先产生炮弹,放入队列中)if (tank_create_abomb(tank, bomb, &bnum) != RTN_NULL){m = 0;//把炮弹插入到炮弹队列中for (j = 0; j < bnum; j++){//寻找可以插入炮弹的位置for (k = m; k < TANK_BOMB_NUM_MAX; k++){if ((TANK_BOMB_BOX[k].pr == PR_NULL) ||(TANK_BOMB_BOX[k].valid == FALSE)){//插入炮弹m = k + 1;memcpy(&(TANK_BOMB_BOX[k]), &bomb[j], sizeof(bomb_t));break;}}}if (tank->pr == PR_MYSELF){tank_sound(IDR_WAVE_FIRE);}}}return RTN_OK;
}

显示系统

void tank_draw(void)
{int32 i = 0;tank_clear_warmap();//绘制坦克for (i = 0; i < TANK_WAR_NUM_MAX; i++){if (TANK_WAR_BOX[i].valid == FALSE){continue;}tank_draw_atank(&(TANK_WAR_BOX[i]));if (TANK_WAR_BOX[i].pr == PR_NULL){memset(&(TANK_WAR_BOX[i]), 0, sizeof(tank_t));TANK_WAR_BOX[i].pr = PR_NULL;TANK_WAR_BOX[i].valid = FALSE;}}//绘制装备if (TANK_EQUIP.valid != FALSE){tank_draw_aequip(&(TANK_EQUIP));if (TANK_EQUIP.pr == PR_NULL){memset(&(TANK_EQUIP), 0, sizeof(equip_t));TANK_EQUIP.pr = PR_NULL;TANK_EQUIP.valid = FALSE;}}//绘制炮弹for (i = 0; i < TANK_BOMB_NUM_MAX; i++){if (TANK_BOMB_BOX[i].valid == FALSE){continue;}tank_draw_abomb(&(TANK_BOMB_BOX[i]));if (TANK_BOMB_BOX[i].pr == PR_NULL){memset(&(TANK_BOMB_BOX[i]), 0, sizeof(bomb_t));TANK_BOMB_BOX[i].pr = PR_NULL;TANK_BOMB_BOX[i].valid = FALSE;}}tank_update_warmap();
}

坦克运行

int32 tank_run(dir_t* dir, int32* fire)
{int32 i = 0, j = 0, k = 0, m = 0;equip_t equip = { 0 };tank_t tank = { 0 };tank_t* ptank = NULL;bomb_t bomb[3] = { 0 };int32 bnum = 0;static int32 speed = 0;int32 pr = 0;int32 ret = RTN_OK;for (i = 0; i < TANK_WAR_NUM_MAX; i++){//打扫战场ret = tank_clean(&(TANK_WAR_BOX[i]));if (ret != RTN_OK){//我军被击败,游戏结束//DEBUG_LOG("ERR");return ret;}tank_fire(&(TANK_WAR_BOX[i]), fire);//坦克开火tank_detect(&(TANK_WAR_BOX[i]));//碰撞检测}//移动坦克,炮弹和装备if (speed < TANK_PR.speed){speed++;}else{speed = 0;tank_move_atank(*dir);*dir = DIR_MAX;}tank_move_equip();tank_move_abomb();tank_draw();//更新图像界面return RTN_OK;
}

程序运行截图

项目文件截图


坦克大战-C语言-详注版

代码地址如下:
http://www.demodashi.com/demo/14259.html

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

坦克大战-C语言-详注版相关推荐

  1. 俄罗斯方块-C语言-详注版

    代码地址如下: http://www.demodashi.com/demo/14818.html 俄罗斯方块-C语言-详注版 概述 本文详述了C语言版俄罗斯方块游戏的原理以及实现方法,对游戏代码进行了 ...

  2. 基于强化学习的坦克大战python语言实现

    这个项目是基于一个人工智能算法(基于全连接线性BP网络的增强学习模型)代理玩经典游戏坦克大战. 在个游戏中,机器控制的坦克主要目标是射击敌方坦克并保卫自家的根据地.         游戏中坦克的动作空 ...

  3. TanksWar(坦克大战三维、二维版以及90版)

    文章目录 1. 按 2. 代码下载 3. 三维版说明 4. 二维版说明 5. 90版说明 6. 游戏下载 7. 书籍和视频教程资源分享 1. 按 三款坦克大战,三维版和二维版以及90版的,目前对这三款 ...

  4. java之详解坦克大战_Java之详解坦克大战游戏(一)

    相信大家小时候一定玩过坦克大战游戏,躲避敌方坦克,炸毁敌方坦克,不断向前进攻直逼敌方基地-这次,我们来实现一个简单版的坦克大战,我想学Java的人都有想到以前的按键手机里那菜单点开"Java ...

  5. java 坦克大战暂停_【 java版坦克大战--事件处理】 坦克动起来了

    折腾了这么久,坦克总算能动了.只贴代码编辑不给上首页,花了半个小时的时间写了n多注释. 再顺便把绘图的原理发在这里: 绘图原理 Component类提供了两个和绘图有关的重要方法: ①   paint ...

  6. 坦克大战C语言实现(课程设计)

    #include <stdio.h> #include <windows.h> #include <time.h> //里规格:长39*2=78 (真坐标)(假坐标 ...

  7. 坦克大战c语言程序贴吧,坦克大战!

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 amy[i].fire.x=amy[i].x; amy[i].fire.y=amy[i].y; } break; } } if(lose)/*因为失败而跳 ...

  8. python选择排序算法图解_简单选择排序算法(C语言详解版)

    该算法的实现思想为:对于具有 n 个记录的无序表遍历 n-1 次,第 i 次从无序表中第 i 个记录开始,找出后序关键字中最小的记录,然后放置在第 i 的位置上. 例如对无序表{56,12,80,91 ...

  9. 坦克大战项目c语言代码,c语言 坦克大战 游戏源码下载

    [实例简介]坦克大战  C语言  源代码 [实例截图] [核心代码] void GamePlay()/*玩游戏的过程*/ { int i,j,lose=0;/*lose是1的时候表示失败*/ int ...

最新文章

  1. 0字符串 if mapper test_mybatis的if判断条件将字符串解析成了数字
  2. 皮一皮:好的团队合作比什么都有用!
  3. 用TextKit实现表情混排
  4. 02 | 系统可用性:没有故障,系统就一定是稳定的吗?
  5. 利用原生js做数据管理平台
  6. FZU - 2103 Bin Jing in wonderland
  7. 信息学奥赛一本通 1079:计算分数加减表达式的值 | OpenJudge NOI 1.5 33
  8. Jar运行的几个方法
  9. linux后台运行和关闭、查看后台任务(转)
  10. 你使用过哪些数据分析的方法?
  11. 全国计算机应用考试试卷,全国信息化计算机应用技术水平教育考试试卷
  12. 30种EMC标准电路
  13. vb access mysql数据库教程_vb操作access数据库的方法
  14. android笑脸切图,朋友圈小符号表情怎么弄?朋友圈文案小符号表情大全
  15. JavaScript零基础入门 1:JavaScript表格简介
  16. android word文件编辑器,AndroDOC Doc&Word的编辑器
  17. SDN与NFV技术在云数据中心的规模应用
  18. 页面5秒钟刷新一次(html,php)均可用
  19. 将图片内嵌到 exe 文件中
  20. Standardized QCI characteristics

热门文章

  1. 5006.c++类中使用static变量bug
  2. 基于modelsim的十个Verilog入门试验程序(1)(7人表决器+算术逻辑单元)—程序+测试代码+波形+结果分析
  3. Keil(MDK-ARM-STM32)系列教程(五)Configuration(Ⅰ)
  4. Linux-kernel 网桥代码分析(一)
  5. 生产者消费者模型、信号量、线程池以及单例模式的实现
  6. 国开计算机专业英语章节测试答案,国开大201x理工英语1第七单元网上测试答案...
  7. ML、DL、CNN学习记录5
  8. 【LeetCode】剑指 Offer 48. 最长不含重复字符的子字符串
  9. snmp4j介绍及api使用
  10. 关于Action模型驱动无法获取属性的问题