简单贪吃蛇游戏

 VS2010/MFC/对话框程序

一、  定义snake类。初始长度为5,定义枚举DIRECTION表示蛇要移动的方向,初始值为LEFT,CPOINT型数组,表示蛇的组成,每个点都是一个矩形的左上角点的坐标,蛇由snakelength个宽高为10的小正方形方块连接组成。

构造函数中带有两个参数宽和高,用于表明游戏区域的大小,用来确定生成食物的范围和判断出界的条件。

#ifndef _CSNAKE_
#define _CSNAKE_

#include "stdafx.h"

//蛇最大长度
#define MAXSIZE 5000
//蛇初始长度
#define INITSIZE 5

enum DIRECTION{
    RIGHT  = 0,
    UP,
    LEFT,
    DOWN
};

class CSnake{
public:
    CSnake(int width, int height);
    ~CSnake();
    //随机生成食物
    void GenerateFood();
    //出界,回绕检测
    bool CollisionDetect();
    //吃到食物
    bool EatFood();
    //移动蛇
    void Move();

public:
    //snake的长度
    int snakelength;
    //snake的组成
    CPoint snake[MAXSIZE];
    //食物
    CPoint food;
    //尾巴
    CPoint snaketail;
    //方向
    DIRECTION direction;
    //边届宽
    int width;
    //边界高
    int height;
};

#endif

详细:

  1. 初始化。

  

CSnake::CSnake(int width, int height)
{
    snakelength = INITSIZE;
    for (int i = 0; i < snakelength; i++)
    {
        snake[i].x = 150 + i*10;
        snake[i].y = 150;
    }
    snaketail = snake[snakelength-1];
    direction = LEFT;

    this->width = width;
    this->height = height;
}

 2. 食物生成

  随机数产生食物的位置,要注意的是由于蛇上每个点的坐标都为10的倍数,所有产生的食物的坐标也应该是10的倍数,可以先整除10,然后乘以10。

void CSnake::GenerateFood()
{
    for (;;)
    {
        food.x = rand() % width;
        food.y = rand() % height;
        food.x /= 10;
        food.y /= 10;
        food.x *= 10;
        food.y *= 10;

        for (int i = 0; i < snakelength; i++)
        {
            if (food.x == snake[i].x && food.y == snake[i].y)
                continue;
        }
        break;
    }
}

3. 碰撞检测

  

bool CSnake::CollisionDetect()
{
    //出界
    if (snake[0].x < 0 || snake[0].x >= width || snake[0].y < 0 || snake[0].y >= height)
    {
        return true;
    }
    //回绕
    for (int i = 1; i < snakelength; i++)
    {
        if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
        {
            return true;
        }
    }
    return false;
}

4. 吃到食物

    判断碰到食物后,应该将蛇的长度加1,然后在蛇的尾部加上一个方块,位置为之前尾部上一次的坐标,保存在snaketail中。

bool CSnake::EatFood()
{
    if (snake[0] == food)
    {
        snakelength++;
        snake[snakelength-1] = snaketail;
        return true;
    }
    return false;
}

5. 蛇的移动

  蛇的移动先移动蛇的身体, 将snake数组所有点(除snake[0])前移一位,采用从后往前移动的方式,所以是倒着写的循环。同时更新蛇尾snaketail的值。

然后再移动蛇头,根据蛇的移动方向direction,改变snake[0]的值。

void CSnake::Move()
{
    //移动身体
    for (int i = snakelength-1 ; i > 0; i--)
    {
        snake[i].x = snake[i-1].x;
        snake[i].y = snake[i-1].y;
    }
    snaketail = snake[snakelength-1];
    //移动头
    switch (direction)
    {
    case RIGHT:
        snake[0].x += 10;
        break;
    case LEFT:
        snake[0].x -= 10;
        break;
    case UP:
        snake[0].y -= 10;
        break;
    case DOWN:
        snake[0].y += 10;
        break;
    default:
        break;
    }
}

 二、 界面部分

  1. 定义 

  //定时器ID
  #define TIMERMOVE 100
  //蛇移动速度(越大越慢)
  #define SNAKESPEED 200

  SNAKESPEED是定时器的周期,单位毫秒,可以控制蛇的移动速度。

    2. OnInitDialog()中

  有两个尺寸,一个是窗口的,一个是客户区的,两个尺寸间差一个边框的,由于创建窗口时按照窗口大小,而蛇的运动区域为客户区,所以需要创建时需要算好边框的距离。

    //获取窗口大小
    CRect wRect;
    GetWindowRect(&wRect);
    int wWidth = wRect.Width();
    int wHeight = wRect.Height();

    //获取客户区大小
    CRect rect;
    GetClientRect(&rect);
    int cWidth = rect.Width();
    int cHeight = rect.Height();

    //计算边缘大小
    int borderWidth = wWidth - cWidth;
    int borderHeight = wHeight - cHeight;

     然后是其他初始化,注意到在SetWindowPos中的两个参数,在400的基础上加上了边框的大小。

    //设置窗口位置大小
    SetWindowText("snake");
    HWND hWnd = ::FindWindow(NULL, "snake");
    ::SetWindowPos(hWnd, NULL, 0, 0, 400+borderWidth, 400+borderHeight, SWP_NOZORDER);
    CenterWindow();

    //装载菜单,用来控制游戏的重新开始
    m_Menu.LoadMenu(IDR_MENU1);
    SetMenu(&m_Menu);

    //创建蛇对象
    csnake = new CSnake(rect.Width(), rect.Height());
    //生成食物
    csnake->GenerateFood();
    //开启定时器
    SetTimer(TIMERMOVE, SNAKESPEED, 0);

3.  OnPaint()中。负责失败,吃到食物的业务逻辑处理和绘制蛇及食物。

    CClientDC dc(this);
    CRect crect;
    GetClientRect(&crect);

    //绘制前逻辑处理
    if (csnake->CollisionDetect() == true)
    {
        KillTimer(TIMERMOVE);
        MessageBox("游戏失败!");    
    }
    if (csnake->EatFood() == true)
    {
        csnake->GenerateFood();
    }
    
    //填充客户区背景
    dc.FillRect(crect, &CBrush(RGB(0, 255, 255)));
    //画蛇
    for (int i = 0; i < csnake->snakelength; i++)
    {
        CRect rect(csnake->snake[i].x, csnake->snake[i].y, csnake->snake[i].x+10, csnake->snake[i].y+10);
        dc.Rectangle(rect);
        //可以给蛇填充背景
        //dc.FillRect(rect, &CBrush(RGB(0, 0, 255)));
    }

    //画食物
    CRect rect(csnake->food.x,csnake->food.y,csnake->food.x+10, csnake->food.y+10);
    dc.Rectangle(rect);
    //填充食物背景
    dc.FillRect(rect, &CBrush(RGB(255,0,0)));

4. 定时器处理。没到一次时钟,调用一次Move()。然后用Invalidate(true)让窗口重绘。

void CsnakeDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    switch (nIDEvent)
    {
    case TIMERMOVE:
        csnake->Move();
        break;
    default:
        break;
    }
    
    Invalidate(TRUE);
    CDialogEx::OnTimer(nIDEvent);
}

5.  键盘响应。

void CsnakeDlg::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    switch (nChar)
    {
    case VK_DOWN:
        if (csnake->direction != UP)
            csnake->direction = DOWN;
        break;
    case VK_LEFT:
        if (csnake->direction != RIGHT)
            csnake->direction = LEFT;
        break;
    case VK_UP:
        if (csnake->direction != DOWN)
            csnake->direction = UP;
        break;
    case VK_RIGHT:
        if (csnake->direction != LEFT)
            csnake->direction = RIGHT;
        break;
    default:
        break;
    }

    CDialogEx::OnKeyUp(nChar, nRepCnt, nFlags);
}

6. 添加菜单项控制游戏失败后重新开始。

  a. 在资源视图中在下面的文件上右击选“添加资源”,选择Menu,然后“新建”,新建一个“开始”-“新游戏”两级菜单,从属性中读到子菜单的ID,然后在类向导中在命令中搜到ID,添加处理函数,编辑代码,添加代码如下,最后在OnInitDialog()中添加m_Menu.LoadMenu(IDR_MENU1);SetMenu(&m_Menu);

void CsnakeDlg::OnNewgame()
{
    // TODO: 在此添加命令处理程序代码
    CRect rect;
    GetClientRect(&rect);
    delete csnake;
    csnake = new CSnake(rect.Width(), rect.Height());
    csnake->GenerateFood();
    SetTimer(TIMERMOVE, SNAKESPEED, 0);
}

(完)

原文地址:https://www.cnblogs.com/fwst/p/3715608.html