本来这个文章周日准备更新的,但是有点别的事,就推迟了一天~
给文章下方增加了打赏功能,大家要是喜欢我们的文章的话可以打赏我们,也可以通过微信公众号或者本站留言,给我们建议和意见!3Q
http://www.bcwhy.com/thread-27783-1-1.html
通过上两次的文章,我们已经能把游戏框框和蛇给画出来了,现在我们要做的就是,移动我们的蛇,老规矩,我们先来整理下思路,我们蛇是存在链表里面的,链表的每个节点就是蛇的身体的每一节,所谓移动,就是算一下蛇头的位置,然后蛇身体往上一节点移动,第一节身体的值等于蛇头,第二节身体的值等于第一节身体…………然后再重绘下,看起来就像是再移动了,然后我们需要的是,没过一段时间,就要让蛇移动一下,这样就看起来像是在动,然后,键盘按W S A D分别上 下 左 右 移动,
我们来看下面这张图, 左边是我们认为的蛇头,右边是蛇尾,我们定义两个指针,分别指向蛇的最后一个元素,和倒数第二个元素,分别是BODY和HEAD,我们用Head的值来覆盖BODY的值,然后把body和head分别往前移动一个节点,之所以不从前往后覆盖是因为 当你第一个元素的值覆盖了第二个元素,再用第二个元素覆盖第三个元素的时候,其实是拿第一个元素的值去覆盖的~
上面两个图思路理清楚了,我们就可以开始写代码了~
我们先在头文件 定义一个枚举
1
2
3
4
5
6
7
|
enum
{
LEFT,
RIGHT,
UP,
DOWN,
};
|
用来表示当前蛇朝那个方向移动,然后定义一个成员变量
MOVEENUM m_snakeMove;
在构造函数给他初始化成向左移动,代码如下
[C++]
纯文本查看
复制代码
1
2
3
4
5
6
|
CSnakeDlg::CSnakeDlg(CWnd*
/*=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 回调功能),该值必须仅对与当前窗口相关的窗口计时器是唯一的。 针对回调计时器,该值对于全部过程中的所有计时器必须是唯一的。 因此,创建回调计时器时,很有可能返回的值不同于您指定的值。
SetTimer 确定一个新的唯一值并返回它。 针对窗口计时器(具备 NULL 回调功能),该值必须仅对与当前窗口相关的窗口计时器是唯一的。 针对回调计时器,该值对于全部过程中的所有计时器必须是唯一的。 因此,创建回调计时器时,很有可能返回的值不同于您指定的值。
nElapse
指定超时值或间隔(以毫秒为单位)。
lpfnTimer
指定应用程序提供的 TimerProc 处理 WM_TIMER 消息的回调函数的地址。 如果此参数为 NULL, WM_TIMER
消息将置于应用程序的消息队列中,并由 CWnd
对象处理。
消息将置于应用程序的消息队列中,并由 CWnd
对象处理。
第一个参数是一个定时器ID,我们照样在头文件定义一个宏,方便以后使用和修改
#define BEGINGAME 1000 //定时器宏,控制蛇移动的
然后
SetTimer(BEGINGAME, 500, NULL);
这样调用这个API,我们希望500毫秒刷新一次,回调函数我们传的NULL,我们只要响应WM_TIME消息 就能处理定时器回调了,
我们切换到类视图,然后找到我们的对话框类,点右键->类向导
添加好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
UINT_PTR
{
if
{
list<SNAKE>::iterator itHead = m_listSnake.end();
list<SNAKE>::iterator itBody = m_listSnake.end();
itBody--;
itHead--;
while
{
itHead--;
itBody->nCol = itHead->nCol;
itBody->nRow = itHead->nRow;
itBody--;
}
itHead = m_listSnake.begin();
switch
{
case
itHead->nCol -= 1;
break
;
case
itHead->nCol += 1;
break
;
case
itHead->nRow -= 1;
break
;
case
itHead->nRow += 1;
break
;
}
Invalidate();
}
CDialogEx::OnTimer(nIDEvent);
}
|
Invalidate();是系统API 用来强制重绘当前窗口的,因为我们在OnPaint里面画的蛇,所以只有重绘,蛇就动了
上面的while循环,就是我们之前所说的从后往前遍历,赋值的循环
我们运行起来看看 蛇是不是会动了
蛇已经会动了,只会往左边走(因为我们初值是往左边走),再加个键盘控制移动,今天的任务就算完成了~
我们按刚才添加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
UINT
UINT
UINT
{
switch
{
case
:
if
{
m_snakeMove = UP;
}
break
;
case
:
if
{
m_snakeMove = DOWN;
}
break
;
case
:
if
{
m_snakeMove = LEFT;
}
break
;
case
:
if
{
m_snakeMove = RIGHT;
}
break
;
default
:
break
;
}
CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);
}
|
Char是表示哪个键被按下了,这里我们判断WSAD,如果按下了,W,我们就上移动,但是 上移动之前,我们要判断之前是不是下移动,之前已经在往下走,就不能往上走,要不然蛇头会画在蛇身上~