游戏编程笔记-起步(三)-贪吃蛇游戏实现

游戏编程笔记-起步


3.贪吃蛇游戏实现



游戏编程笔记-起步(三)-贪吃蛇游戏实现_第1张图片


代码下载:http://download.csdn.net/detail/you_lan_hai/3738025

核心代码实现如下,算法描述见 “起步(一)”:

#include "app.h"
#include <string.h>
#include <stdlib.h>

int g_map[100][100];//2维地图

int g_nWidth = 610; //窗口宽度
int g_nHeight = 514;//窗口高度

int g_rows = 0;     //地图行数
int g_cols = 0;     //地图列数
int g_nSize = 30;   //地图方格尺寸

struct Point//二维点
{
    int r;//行
    int c;//列
};

Point   g_snake[1000];  //蛇
int     g_nLength = 0;  //蛇的长度
int     g_nSpeed;
bool    g_bLive = true; //是否存活
int     g_nSnakeDir = 0;    //蛇的当前移动方向。0-左,1-右,2-上,3-下

const Point g_direction[4] = {{0,-1},{0,1},{-1,0},{1,0}};//四个方向运动增量。

Point g_prizePos;       //奖子坐标

bool g_bPause = true;   //是否暂停

HBRUSH g_brushs[4];     //背景画刷


void setSnakeDir(int dir)//设置蛇的运动方向。
{
    if ((dir==0 && g_nSnakeDir==1)
        || (dir==1 && g_nSnakeDir==0)
        || (dir==2 && g_nSnakeDir==3)
        || (dir==3 && g_nSnakeDir==2))//反方向移动则令他暂停
    {
        g_bPause = true;
        return;//直接返回,什么也不做。
    }
    else
    {
        g_bPause = false;
        g_nSnakeDir = dir;
    }
}

void resetLevel()//重置关卡
{
    /*初始化地图。将四边初始为障碍,即不可通过。
    * 0-可通过,1-障碍,2-奖子,3-蛇
    */
    memset(g_map,0,sizeof(g_map));
    for (int r=0; r<g_rows; ++r)
    {
        g_map[r][0] = 1;
        g_map[r][g_cols-1] = 1;
    }
    for (int c=0; c<g_cols; ++c)
    {
        g_map[0][c] = 1;
        g_map[g_rows-1][c] = 1;
    }

    g_nSpeed = 500;

    g_bPause = true;//暂停
    g_nSnakeDir = 1;//向右。
    g_bLive = true;
    g_nLength = 3;//蛇初始有3个结点
    g_snake[0].r = 1;//蛇头
    g_snake[0].c = 3;
    g_snake[1].r = 1;
    g_snake[1].c = 2;
    g_snake[2].r = 1;
    g_snake[2].c = 1;
    //把初始状态写入地图
    for (int i=0; i<g_nLength; ++i)
    {
        g_map[g_snake[i].r][g_snake[i].c] = 3;
    }

    //初始化奖子坐标
    g_prizePos.r = 1;
    g_prizePos.c = g_cols-2;
}

void init()//游戏初始化
{
    g_brushs[0] = CreateSolidBrush(RGB(255,255,255));//白色画刷
    g_brushs[1] = CreateSolidBrush(RGB(0,0,255));//绘制障碍画刷
    g_brushs[2] = CreateSolidBrush(RGB(255,0,0));//绘制奖子画刷
    g_brushs[3] = CreateSolidBrush(RGB(0,255,0));//绘制蛇画刷

    srand(GetTickCount());

    //矫正窗口高度、宽度数据
    RECT rc;
    GetClientRect(getHWnd(),&rc);
    g_nWidth = rc.right - rc.left;
    g_nHeight = rc.bottom - rc.top;

    //计算行列值
    g_rows = g_nHeight / g_nSize;
    g_cols = g_nWidth / g_nSize;

    resetLevel();
}

void update()//逻辑更新
{
    //时钟控制
    static int oldTime = 0;
    int curTime = GetTickCount();
    if (curTime - oldTime < g_nSpeed)
    {
        return ;
    }
    oldTime = curTime;
    //////////////////////////////////////////////////////////////////////////
    static int lastOcurTime = 0;//奖子上次出现的时间

    if(curTime - lastOcurTime > 10000)//每隔10s更新一次位置。
    {
        //lastOcurTime不为-1表示,没有被吃掉,则将原来位置的奖子从地图擦掉。
        if (lastOcurTime != -1)
        {
            g_map[g_prizePos.r][g_prizePos.c] = 0;
        }
        lastOcurTime = curTime;

        //随机产生奖子位置。
        bool flag = true;
        while(flag)
        {
            g_prizePos.r = rand()%(g_rows-2)+1;
            g_prizePos.c = rand()%(g_cols-2)+1;
            if (g_map[g_prizePos.r][g_prizePos.c] == 0)//符合条件
            {
                flag = false;
                g_map[g_prizePos.r][g_prizePos.c] = 2;
            }
        }
    }

    if (!g_bLive || g_bPause)//死亡或暂停
    {
        return ;
    }

    //先判断是否可走,即是否撞死。
    Point newHead;
    newHead.r = g_snake[0].r + g_direction[g_nSnakeDir].r;
    newHead.c = g_snake[0].c + g_direction[g_nSnakeDir].c;
    if (g_map[newHead.r][newHead.c]==1 || g_map[newHead.r][newHead.c]==3)
    {
        g_bLive = false;
        if(IDYES == MessageBox(getHWnd(),L"你撞死啦!是否重新来过?",
            L"撞死提示",MB_YESNO))
        {
            resetLevel();
        }
        else
        {
            PostQuitMessage(0);
        }
        return ;
    }
    else if (g_map[newHead.r][newHead.c] == 2)//遇到奖子
    {
        ++g_nLength ;
        lastOcurTime = -1;
        g_nSpeed -= 10;
    }
    else
    {
        //撤销尾部在地图上遗留数据
        g_map[g_snake[g_nLength-1].r][g_snake[g_nLength-1].c] = 0;
    }
    
    memmove(g_snake+1,g_snake,sizeof(Point)*(g_nLength-1));//移动蛇的中间数据
    g_snake[0].r = newHead.r;
    g_snake[0].c = newHead.c;
    
    //添加蛇头数据
    g_map[g_snake[0].r][g_snake[0].c] = 3;
    //////////////////////////////////////////////////////////////////////////
}

void render()//画面渲染
{
    HDC hDC = GetDC(getHWnd());		//获得系统绘图设备

    HDC memDC = CreateCompatibleDC(0);	//创建辅助绘图设备

    HBITMAP bmpBack = CreateCompatibleBitmap(hDC,g_nWidth,g_nHeight);//创建掩码位图(画布)
    SelectObject(memDC,bmpBack);	//将画布贴到绘图设备上

    HPEN penBack = CreatePen(PS_SOLID,1,RGB(255,0,255));//创建画笔
    SelectObject(memDC,penBack);    //将画笔选到绘图设备上

    //擦除背景
    RECT rcClient;//区域结构
    GetClientRect(getHWnd(),&rcClient);//获得客户区域
    HBRUSH brushTemp = (HBRUSH)GetStockObject(WHITE_BRUSH);//获得库存物体,白色画刷。
    FillRect(memDC,&rcClient,brushTemp);//填充客户区域。
    //////////////////////////////////////////////////////////////////////////

    //绘制2维网格,矩形画法。
    for (int r=0; r<g_rows; ++ r)
    {
        for (int c=0; c<g_cols; ++c)
        {
            SelectObject(memDC,g_brushs[g_map[r][c]]);
            Rectangle(memDC,c*g_nSize,r*g_nSize,(c+1)*g_nSize,(r+1)*g_nSize);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    BitBlt(hDC,0,0,g_nWidth,g_nHeight,memDC,0,0,SRCCOPY);//复制到系统设备上显示
    DeleteObject(penBack);  //释放画笔资源
    DeleteObject(bmpBack);  //释放位图资源
    DeleteDC(memDC);	    //释放辅助绘图设备
    ReleaseDC(getHWnd(),hDC);	//归还系统绘图设备

    Sleep(10);
}

void clear()//资源释放
{
    //释放画刷资源
    for (int i=0; i<4; ++i)
    {
        if(g_brushs != NULL)
        {
            DeleteObject(g_brushs[i]);
            g_brushs[i] = NULL;
        }
    }
}

/*窗口过程。如果没有处理消息请返回0,否则返回1。*/
LRESULT wndProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam )
{
    switch(uMsg)
    {
    case WM_KEYDOWN:
        {
            switch(wParam)
            {
            case VK_LEFT:  setSnakeDir(0); break; //向左
            case VK_RIGHT: setSnakeDir(1); break; //向右
            case VK_UP  :  setSnakeDir(2); break; //向上
            case VK_DOWN:  setSnakeDir(3); break; //向下
            case VK_ESCAPE: DestroyWindow(hwnd);break;
            }
        }
        break;
    default:
        return 0;
    }
    return 1;
}

//主函数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE , LPSTR , int  )
{
    if(!initApp(hInstance,L"贪吃蛇游戏",g_nWidth,g_nHeight))
    {
        return 0;
    }

    //初始化游戏
    init();

    //游戏循环
    mainLoop();

    //释放资源
    clear();

    return 0;
}




你可能感兴趣的:(游戏编程笔记-起步(三)-贪吃蛇游戏实现)