zoukankan      html  css  js  c++  java
  • 简单贪吃蛇游戏

     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);
    }

    (完)

  • 相关阅读:
    HTML_表单
    HTML_列表、表格与媒体元素
    HTML_HTML5基础
    使用java理解程序逻辑 试题分析
    字符串
    带参数的方法
    人机猜拳
    类的无参方法
    类和对象
    vue cli+axios踩坑记录+拦截器使用,代理跨域proxy(更新)
  • 原文地址:https://www.cnblogs.com/fwst/p/3715608.html
Copyright © 2011-2022 走看看