zoukankan      html  css  js  c++  java
  • 坦克大战-C语言-详注版

    代码地址如下:
    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大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    51-maven私有库神坑之:“Downloading: http://repo.maven.apache.org/maven2/”
    50-maven 仓库配置
    49-2017年第八届蓝桥杯国赛试题及详解(Java本科B组)
    21-matlab 迷宫题
    20-matlab全排列-函数调用
    52-python 画图二维
    51-python3 pandas读写excel
    48-设置tomcat虚拟路径的两种方法(Eclipse、tomcat、IDEA)
    19-matlab知识点复习二
    【剑指offer】二叉搜索树的后序遍历序列
  • 原文地址:https://www.cnblogs.com/demodashi/p/9802037.html
Copyright © 2011-2022 走看看