设为首页收藏本站

编程十万个为什么,属于程序员的编程论坛

 找回密码
 5秒快速注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 1094|回复: 1

[原创] MFC实现贪吃蛇游戏之蛇的移动

[复制链接]
发表于 2016-9-19 22:17:35 | 显示全部楼层 |阅读模式
PStatus"> 本帖最后由 夜影 于 2016-9-19 22:19 编辑

本来这个文章周日准备更新的,但是有点别的事,就推迟了一天~
给文章下方增加了打赏功能,大家要是喜欢我们的文章的话可以打赏我们,也可以通过微信公众号或者本站留言,给我们建议和意见!3Q

通过上两次的文章,我们已经能把游戏框框和蛇给画出来了,现在我们要做的就是,移动我们的蛇,老规矩,我们先来整理下思路,我们蛇是存在链表里面的,链表的每个节点就是蛇的身体的每一节,所谓移动,就是算一下蛇头的位置,然后蛇身体往上一节点移动,第一节身体的值等于蛇头,第二节身体的值等于第一节身体…………然后再重绘下,看起来就像是再移动了,然后我们需要的是,没过一段时间,就要让蛇移动一下,这样就看起来像是在动,然后,键盘按W S A D分别上 下 左 右 移动,

我们来看下面这张图, 左边是我们认为的蛇头,右边是蛇尾,我们定义两个指针,分别指向蛇的最后一个元素,和倒数第二个元素,分别是BODY和HEAD,我们用Head的值来覆盖BODY的值,然后把body和head分别往前移动一个节点,之所以不从前往后覆盖是因为 当你第一个元素的值覆盖了第二个元素,再用第二个元素覆盖第三个元素的时候,其实是拿第一个元素的值去覆盖的~
1.png
2.png
上面两个图思路理清楚了,我们就可以开始写代码了~

我们先在头文件 定义一个枚举
[C++] 纯文本查看 复制代码
enum MOVEENUM
{
        LEFT,
        RIGHT,
        UP,
        DOWN,
};
用来表示当前蛇朝那个方向移动,然后定义一个成员变量
MOVEENUM m_snakeMove;
在构造函数给他初始化成向左移动,代码如下
[C++] 纯文本查看 复制代码
CSnakeDlg::CSnakeDlg(CWnd* pParent /*=NULL*/)
        : CDialogEx(IDD_SNAKE_DIALOG, pParent)
        , m_snakeMove(LEFT)//默认向左移动
{
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
我们只加了, m_snakeMove(LEFT)//默认向左移动这已经,好了 继续下一步

上面说了,需要蛇看起来像自己在动,那么,我们就需要一个定时器,不停的算蛇的位置,我们先定义一个定时器,加在InitGameData()里面,我们用SetTimer来设置一个定时器,这个API有3个参数
MSDN的解释如下
nIDEvent
指定一个非零计时器标识符。 如果计时器标识符是唯一的,则此相同的值将由 SetTimer 返回。 否则,
SetTimer 确定一个新的唯一值并返回它。 针对窗口计时器(具备 NULL 回调功能),该值必须仅对与当前窗口相关的窗口计时器是唯一的。 针对回调计时器,该值对于全部过程中的所有计时器必须是唯一的。 因此,创建回调计时器时,很有可能返回的值不同于您指定的值。
nElapse
指定超时值或间隔(以毫秒为单位)。
lpfnTimer
指定应用程序提供的 TimerProc 处理 WM_TIMER 消息的回调函数的地址。 如果此参数为 NULL, WM_TIMER
消息将置于应用程序的消息队列中,并由 CWnd
对象处理。

第一个参数是一个定时器ID,我们照样在头文件定义一个宏,方便以后使用和修改
#define BEGINGAME 1000 //定时器宏,控制蛇移动的
然后

SetTimer(BEGINGAME, 500, NULL);
这样调用这个API,我们希望500毫秒刷新一次,回调函数我们传的NULL,我们只要响应WM_TIME消息 就能处理定时器回调了,
3.png
我们切换到类视图,然后找到我们的对话框类,点右键->类向导
4.png
添加好WM_TIMER消息以后,我们代码里面会多一个OnTimer函数
我们把代码改成
[C++] 纯文本查看 复制代码
void CSnakeDlg::OnTimer(UINT_PTR nIDEvent)
{
        if (nIDEvent == BEGINGAME)
        {
                list<SNAKE>::iterator itHead = m_listSnake.end();
                list<SNAKE>::iterator itBody = m_listSnake.end();
                itBody--;
                itHead--;

                while (itHead != m_listSnake.begin())
                {
                        itHead--;
                        itBody->nCol = itHead->nCol;
                        itBody->nRow = itHead->nRow;
                        itBody--;
                }
                itHead = m_listSnake.begin();
                switch (m_snakeMove)
                {
                case LEFT:
                        itHead->nCol -= 1;
                        break;
                case RIGHT:
                        itHead->nCol += 1;
                        break;
                case UP:
                        itHead->nRow -= 1;
                        break;
                case DOWN:
                        itHead->nRow += 1;
                        break;
                }

                Invalidate();
        }

        CDialogEx::OnTimer(nIDEvent);
}
http://www.dbgpro.com/archives/322.html
Invalidate();是系统API 用来强制重绘当前窗口的,因为我们在OnPaint里面画的蛇,所以只有重绘,蛇就动了
上面的while循环,就是我们之前所说的从后往前遍历,赋值的循环
我们运行起来看看 蛇是不是会动了
5.png
蛇已经会动了,只会往左边走(因为我们初值是往左边走),再加个键盘控制移动,今天的任务就算完成了~
我们按刚才添加WM_TIMER消息的步骤来添加一个WM_KEYDOWN消息, 添加好了以后 会多一个void CSnakeDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)函数
我们在OnKeyDwon里面添加如下代码
[C++] 纯文本查看 复制代码
void CSnakeDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
        switch (nChar)
        {
        case 'W':
                if (m_snakeMove != DOWN)
                {
                        m_snakeMove = UP;
                }
                break;
        case 'S':
                if (m_snakeMove != UP)
                {
                        m_snakeMove = DOWN;
                }
                break;
        case 'A':
                if (m_snakeMove != RIGHT)
                {
                        m_snakeMove = LEFT;
                }
                break;
        case 'D':
                if (m_snakeMove != LEFT)
                {
                        m_snakeMove = RIGHT;
                }
                break;
        default:
                break;
        }

        CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);
}
Char是表示哪个键被按下了,这里我们判断WSAD,如果按下了,W,我们就上移动,但是 上移动之前,我们要判断之前是不是下移动,之前已经在往下走,就不能往上走,要不然蛇头会画在蛇身上~



您需要登录后才可以回帖 登录 | 5秒快速注册

本版积分规则

关闭

BcWhy推荐上一条 /1 下一条

QQ|关于我们|最新帖子|小黑屋|手机版|编程十万个为什么 ( 粤ICP备16108587号-2  

GMT+8, 2017-3-26 13:19 , Processed in 0.224238 second(s), 31 queries , File On.

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表