代碼位址如下:
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://Enter鍵開火
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大師代發,拒絕轉載,轉載需要作者授權