用C语言实现贪吃蛇游戏,虽然整体实现的逻辑比较简单,但是初写者可能会遇到一些比较棘手的问题,不知道是什么原因造成的,也不知道该怎么处理,这篇文章将列出我在编写贪吃蛇游戏时遇到的sheng去解决这些问题的方法
问题解决
问题1 :蛇在吃完食物的瞬间,窗口的最左上角产生了一个身体块
为什么会产生这样的结果呢?原因并不复杂,我们在定义蛇的身体时,是用数组直接给定大小的,除了我们在初始化的时候给初始化的几节身体坐标赋了初始值,该身体数组的其余节身体的坐标都是处于初始值为0的状态。
造成这种情况的原因是蛇在吃完一个食物后,此时程序判断身体数目加一,但此时,新添加的这个身体的坐标没有被赋值,此时仍然是初始的0值,然后绘制函数又将这节身体绘制出来,就跑到了左上角,解决办法就是将坐标赋值函数放到身体绘制函数之前,让新增的这一节身体的坐标先被赋值,然后再将它绘制出来,这样就恢复了正常。
问题2:食物生成之后,蛇吃不了食物
造成这个问题的主要原因是存在坐标偏差,我们开创的窗口大小一般是640,480。如果我们将蛇移动的速度控制在每循环一次走10个坐标点,而随机生成食物的坐标值不是10的倍数的话,那么蛇的身体的坐标就会与食物的坐标产生偏差,即使看着像吃到了,但实际坐标不是完全重合,吃到食物的判断逻辑就是蛇头坐标与食物坐标完全重合,所以是无法吃到的。假设蛇头的初始坐标是(320,240),食物被生成的坐标是(329,240)那么无论蛇怎么走,都吃不到这个食物,除非蛇每移动一次走九个坐标点,才能做到坐标完全覆盖
解决办法就是食物生成的坐标值必须是蛇的身体移动速度的整数倍,且蛇头的初始位置也要是移动速度的整数倍,这里还是推荐速度为10,使用起来很方便
//创造食物 void Creatfood() { while (1) { food.x = rand() % 62 * 10; food.y = rand() % 46 * 10; if (food.x >= 20 && food.y >= 20) { food.state = false; break; } } } //我这里加循环的原因是不想食物生成在边界
问题3:蛇走起来时略有闪烁,不是很流畅
略有闪烁是因为绘制图形时一个一个绘制,然后清空,再重新一个一个图案绘制,在不断绘制清空的过程中,留下的一些清空的痕迹。
解决这个问题的方法就是换一种绘图方式,用批量绘图来绘制游戏画面。批量绘制就是把需要绘制的图案先放到缓冲区里,然后逐步绘制一张画面的内容,把需要绘制的都绘制完成后就呈现在一张画面上,同时第二个缓冲区开始绘制下一次循环后所产生的画面,把绘制完的画面以人眼适应的频率(即帧率)播放到窗口,然后不断刷新,放置,这样我们看到的画面就很流畅了
批量绘图需要两个函数 BeginBatchDraw() //开始批量绘图 ....... //需要绘制的图案放中间 ....... EedBatchDraw() //结束批量绘图
这里需要注意的是如果设置了游戏结束的画面,不要将游戏结束画面放到批量绘图函数中间,因为批量绘制结束后会刷新屏幕,游戏结束界面无法显示出来。
功能扩展
扩展1:实现蛇的穿墙
蛇的穿墙就是蛇走到窗口某边界后,再将蛇从该边界的对面边界传过来,这样可以防止蛇跑没了,需要注意的是边界传送的判定要小于边界值,不能等于,不然会出现一半身体传到对面去,另一半还在这边的bug
//穿墙 void Throughwell() { if (snake.szb[0].x < 0 || snake.szb[0].x > LENGTH) { if (snake.szb[0].x < 0) { snake.szb[0].x = LENGTH; } else if (snake.szb[0].x > LENGTH) { snake.szb[0].x = 0; } } else if (snake.szb[0].y > WIDTH || snake.szb[0].y < 0) { if (snake.szb[0].y > WIDTH) { snake.szb[0].y = 0; } else if (snake.szb[0].y < 0) { snake.szb[0].y = WIDTH; } } }
扩展2:音乐播放
玩游戏没有音乐,总会感觉少了点什么,那必须加上音乐,音乐素材大家自行寻找
添加音乐的方法首先要包含库
#pragma comment(lib,"winmm.lib")
在输入播放音乐的指令就可 mciSendString("open xxx.mp3 alias BGM", 0, 0, 0); mciSendString("play BGM repeat", 0, 0, 0);