代码地址如下:
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_Y static 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;//吃到装备,生命值+1
TANK_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;//生命值-1
bomb->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辆坦克敌军坦克数量+1
if ((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. TanksWar(坦克大战三维、二维版以及90版)

    本文已迁移至:https://coco56.blog.csdn.net/article/details/103198945

  3. 简易坦克大战python版

      #! /usr/bin/env python # -*- coding:utf8 -*- ''' *author:wasua *purpose:学习python语言,其中的类以及pygame应用 ...

  4. Java坦克大战 (七) 之图片版

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  5. Unity3D_(游戏)2D坦克大战 像素版

    2D坦克大战    像素版 游戏规则: 玩家通过上.下.左.右移动坦克,空格键发射子弹 敌人AI出身时朝向己方大本营(未防止游戏快速结束,心脏上方三个单位障碍物设为刚体) 当玩家被击杀次数>=3 ...

  6. 【java版坦克大战---准备篇】 java 绘图

    要写坦克大战当然要先画出坦克.java画图是基础. package com.game; import java.awt.*; import javax.swing.*; public class Pr ...

  7. 例子:web版坦克大战1.0

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. C 语言设计坦克大战(未完成)

    //坦克大战 //0.提示界面 //1.边框 //2.指定位置显示自己的坦克 //3.己方坦克随着方向键动起来 //getasynkeustae //Sleep(毫秒) //减少闪烁 //不闪烁Set ...

  9. 【blade04】用面向对象的方法写javascript坦克大战

    前言 javascript与程序的语言比如C#或者java不一样,他并没有“类”的概念,虽然最新的ECMAScript提出了Class的概念,我们却没有怎么用 就单以C#与Java来说,要到真正理解面 ...

随机推荐

  1. pytest文档16-用例a失败,跳过测试用例b和c并标记失败xfail

    前言 当用例a失败的时候,如果用例b和用例c都是依赖于第一个用例的结果,那可以直接跳过用例b和c的测试,直接给他标记失败xfail 用到的场景,登录是第一个用例,登录之后的操作b是第二个用例,登录之后 ...

  2. UVC调试

    USB video class(又称为USB video device class or UVC)就是USB device class视频产品在不需要安装任何的驱动程序下即插即用,包括摄像头.数字摄影 ...

  3. 用Spark查询HBase中的表数据

    java代码如下: package db.query; import org.apache.commons.logging.Log; import org.apache.commons.logging ...

  4. json的好处-新一代数据传输利器

    JSON是一种轻量级的数据交换格式!和xml一样. 为什么不XML XML的冗余太大,不过XML阅读起来比较方面,所以并没有被json完全取代,很多时候都是并存.比如sina微博的开发平台有一个JSO ...

  5. @JVM垃圾回收调优方法

    JVM调优工具 Jconsole,jProfile,VisualVM Jconsole:jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用.对垃圾回收算法有很详细的跟踪. JProfiler: ...

  6. opencv2.4中SVD分解的几种调用方法

    原帖地址: http://blog.sina.com.cn/s/blog_6109b5d00101ag7a.html       在摄影测量和计算机视觉中,考虑最优解问题时,经常要用到SVD分解.奇异 ...

  7. python 中面向对象编程的几个概念

    Python super() 函数 super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会 ...

  8. SCSI contrller的几种类型的区别

    在VMware vSphere Web Client中, 可以为虚拟机添加一个新的SCSI controller, 选项中包含如下的类型, 那么他们有什么区别呢? 如何选择呢?   BusLogic ...

  9. STL sort 函数实现详解 ZZ

    前几天阿里电话一面,被问到STL中sort函数的实现.以前没有仔细探究过,听人说是快速排序,于是回答说用快速排序实现的,但听电话另一端面试官的声音,感觉不对劲,知道自己回答错了.这几天特意看了一下,在 ...

  10. Scala中_(下划线)的常见用法

    Scala中_(下划线)的常见用法 地址:https://www.jianshu.com/p/0497583ec538