MFC实现贪吃蛇游戏之蛇的移动

时间:2021-05-06 15:00:01
本来这个文章周日准备更新的,但是有点别的事,就推迟了一天~
给文章下方增加了打赏功能,大家要是喜欢我们的文章的话可以打赏我们,也可以通过微信公众号或者本站留言,给我们建议和意见!3Q
http://www.bcwhy.com/thread-27783-1-1.html
通过上两次的文章,我们已经能把游戏框框和蛇给画出来了,现在我们要做的就是,移动我们的蛇,老规矩,我们先来整理下思路,我们蛇是存在链表里面的,链表的每个节点就是蛇的身体的每一节,所谓移动,就是算一下蛇头的位置,然后蛇身体往上一节点移动,第一节身体的值等于蛇头,第二节身体的值等于第一节身体…………然后再重绘下,看起来就像是再移动了,然后我们需要的是,没过一段时间,就要让蛇移动一下,这样就看起来像是在动,然后,键盘按W S A D分别上 下 左 右 移动,

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

我们先在头文件 定义一个枚举
[ C++纯文本查看  复制代码
?
1
2
3
4
5
6
7
enum
MOVEENUM
{
         LEFT,
         RIGHT,
         UP,
         DOWN,
};
用来表示当前蛇朝那个方向移动,然后定义一个成员变量
MOVEENUM m_snakeMove;
在构造函数给他初始化成向左移动,代码如下
[C++]  纯文本查看  复制代码
?
1
2
3
4
5
6
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消息 就能处理定时器回调了,
MFC实现贪吃蛇游戏之蛇的移动
我们切换到类视图,然后找到我们的对话框类,点右键->类向导
MFC实现贪吃蛇游戏之蛇的移动
添加好WM_TIMER消息以后,我们代码里面会多一个OnTimer函数
我们把代码改成
[C++]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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);
}
Invalidate();是系统API 用来强制重绘当前窗口的,因为我们在OnPaint里面画的蛇,所以只有重绘,蛇就动了
上面的while循环,就是我们之前所说的从后往前遍历,赋值的循环
我们运行起来看看 蛇是不是会动了
MFC实现贪吃蛇游戏之蛇的移动
蛇已经会动了,只会往左边走(因为我们初值是往左边走),再加个键盘控制移动,今天的任务就算完成了~
我们按刚才添加WM_TIMER消息的步骤来添加一个WM_KEYDOWN消息, 添加好了以后 会多一个void CSnakeDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)函数
我们在OnKeyDwon里面添加如下代码
[C++]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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,我们就上移动,但是 上移动之前,我们要判断之前是不是下移动,之前已经在往下走,就不能往上走,要不然蛇头会画在蛇身上~