贪吃蛇详解Windows编程(一)

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


贪吃蛇可以用很多种语言写,但是基本的思路都是一样的,在这里我就用C/C++写,但不是纯C或者纯C++写,因为那样太没效率了,用Windows封装的API写会节省很多代码而且是有界面的。既然是详解,所以一篇文章肯定讲不完的,所以我会分开几篇讲。

一个小建议:如果只有C或C++基础而没学过Windows程序设计,那么只建议看看思路即可,如果简单的学过一些Windows可以看完全篇.

先说一下思路,不管做什么东西首先都先要有整体的大概布局以及思路,然后再一小步一小步的去完成,去堆积,到后面就完成了。
(建议看了思路有想法的先去自己写,实在不会再看效果会加倍。)
思路如下:
1.画游戏区和控制区
也许有人会问为什么要分两个区域,理由很简单啊,难道你想程序已运行就自动开始,然后没有暂停按钮没有加速按钮没有减速按钮没有分数显示吗?当然了也可以全部设置成键盘快捷键控制,但是那样有些人就不太会用,导致这个游戏只能你自己玩。

2.画出蛇身
区域画出来了,第二部当然先要去画蛇了,千万别一步登天的想马上让蛇动起来,那样你会很困难,新手就基本就不能完成了。画蛇身的时候只要随便给它几节就行,因为程序都是由特殊性到一般性。

3.让蛇动起来
蛇身画出来后就可以让它动了,我估计很多人包括我都在怎么让蛇身动起来这里卡住了,方法各种各样,但就是千万别用Sleep函数。在这里我们用SetTimer函数设置一个定时器,每隔多少ms就移动一次蛇身(这里我用整体移动而不是一节节移动,就是隔多少ms重画一次蛇身)这里的间隔就是速度了。

4.制作食物
既然蛇都动起来了,那肯定要有食物给它吃吧?先别管吃了食物蛇会干嘛,我们就只管制作食物先,这就是物理的分离法分析,各个部分独立出来分析,然后最后再合并。

5.让蛇吃食物
这里只是判断蛇吃到食物没有,还没让它增加长度

6.吃食物后让蛇身增长
这里只需要调用第5个步骤判断,然后让它是否增长就行

下面这些都是控制区的了,控制区可以随意设置,并无什么技术含量,只是一些个性化。
7.开始和暂停按钮

8.蛇的速度按钮

9.游戏等级

10时间计时

11.分数显示


我会采用片段讲解,最后会贴出全部代码,如果没兴趣看片段详解可以跳到最后一篇文章直接看全部代码。
实战讲解:

1.画游戏区和控制区
首先游戏区和控制区是相互独立的区域,所以要有边界宽度来分割它们,然后游戏区和控制区和父窗口之间也要有分隔,这样整体感才强。
代码片段如下:

//游戏区和控制区宽度高度,这里*10是蛇的Size,后面会懂的。
#define  GAME_WIDE         70        //游戏区宽度70*10
#define  GAME_HEIGHT       60       //游戏区高度60*10
#define  CTRL_WIDE         30       //控制区宽度30*10
#define  CTRL_HEIGHT       60        //控制区高度60*10
#define  BOUNDSIZE         10       //边界厚度10   
//[其它代码]
static int  ixWind = 0, iyWind = 0;//窗口大小参数
static int ixClient = 0, iyClient = 0;//客户区大小参数
//[其它代码]
case WM_CREATE:
	   {
		   
	     //获取窗口大小
		   GetWindowRect(hWnd, &rect);
		    ixWind = rect.right - rect.left;
			iyWind = rect.bottom - rect.top;
         //获取客户区大小
			GetClientRect(hWnd, &rect);
			ixClient= rect.right - rect.left;
			iyClient= rect.bottom - rect.top;
	     //修改窗口大小
			int nWideth = BOUNDSIZE * 3 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE + (ixWind - ixClient);
			int nHeight = BOUNDSIZE * 2 + GAME_HEIGHT*SNAKESIZE + (iyWind - iyClient);
			MoveWindow(hWnd, 50, 50,nWideth ,nHeight ,TRUE);
	      
    }		
		break;
[其它代码]
 case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            //游戏区画刷
			static HBRUSH hBrush1 = CreateSolidBrush(RGB(58, 206, 66));
			//控制区画刷
			static HBRUSH hBrush2 = CreateSolidBrush(RGB(80, 87, 196));
			
			//画游戏区
			Rectangle(hdc, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE+GAME_WIDE*SNAKESIZE,
			BOUNDSIZE+GAME_HEIGHT*SNAKESIZE);
			//填充游戏区颜色
			SetRect(&rect, BOUNDSIZE, BOUNDSIZE, BOUNDSIZE + GAME_WIDE*SNAKESIZE,
				BOUNDSIZE + GAME_HEIGHT*SNAKESIZE);
			FillRect(hdc, &rect,hBrush1);
			//画控制区
			Rectangle(hdc, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE,
				BOUNDSIZE*2+(GAME_WIDE+CTRL_WIDE)*SNAKESIZE, BOUNDSIZE+CTRL_HEIGHT*SNAKESIZE);
			//填充控制区颜色
			SetRect(&rect, BOUNDSIZE * 2 + GAME_WIDE*SNAKESIZE, BOUNDSIZE, 
				BOUNDSIZE * 2 + (GAME_WIDE + CTRL_WIDE)*SNAKESIZE,
				BOUNDSIZE + CTRL_HEIGHT*SNAKESIZE);
			FillRect(hdc, &rect,hBrush2);
            EndPaint(hWnd, &ps);
        }
        break;

也许有人会问为什么可以在case里面定义变量,因为你在case到break;之间加了一对大括号,学过C的都知道块作用域吧?道理就是那样。里面的一些x,y坐标和宽度高度,你自己算一下就可以懂了,如果你懒的话,你就OVER了。
效果如图:
贪吃蛇详解Windows编程(一)_第1张图片

2.画出蛇身
画蛇身之前还要初始化蛇身
初始化要在WM_CREATE消息中初始化.
画蛇要在WM_PAINT消息中调用
代码片段如下:

#include
//蛇的信息
#define   SNAKESIZE      10     //蛇的宽高都为10的小方块
#define   SNAKEMAX       100     //蛇身长度最大值为100
std::vector vSnakePoint;//声明蛇的坐标

UCHAR ucSnakeHead = 4;//初始化蛇头为4,实际上是vSnakePoint[3];
UCHAR ucSnakeTail = 0;//初始化蛇尾为0
UCHAR ucSnakekLen = 4;//初始化蛇身长度为4

[其它代码]
//初始化蛇
VOID InitSnake()
{
	//先清空坐标
	vSnakePoint.clear();
	//给蛇坐标分配空间
	vSnakePoint.resize(SNAKEMAX);
	//游戏结束后再次初始化
	ucSnakeHead = 4;//初始化蛇头为4,实际上是vSnakePoint[3];
	ucSnakeTail = 0;//初始化蛇尾为0
	ucSnakekLen = 4;//初始化蛇身长度为4
	//初始化每一节的蛇身坐标
	for (int i = 0;i < ucSnakekLen;i++)
	{
		vSnakePoint[i].x = i;
		vSnakePoint[i].y = 0;
	}
}
//画蛇身,hdc是在WM_PAINT消息中传进来的,所以画蛇要在那里调用这个函数
VOID DrawSnake(HDC hdc)
{
   //蛇的颜色画刷
	static HBRUSH hBrush = CreateSolidBrush(RGB(0,243,222));
	SelectObject(hdc, hBrush);
	//画蛇
	for (int i = 0;i < ucSnakekLen;i++)
    {
		Rectangle(hdc, vSnakePoint[i].x*SNAKESIZE+BOUNDSIZE, vSnakePoint[i].y*SNAKESIZE+BOUNDSIZE,
			(vSnakePoint[i].x+1)*SNAKESIZE + BOUNDSIZE, (vSnakePoint[i].y+1)*SNAKESIZE + BOUNDSIZE);
    }
	
}

千万不要看到这些长长的宏定义就怕,其实它们就是一个常数而已,你们都可以自己动手算算的,如果封装成类的话就不会有那么多宏定义出现在函数里了。
效果如图:
贪吃蛇详解Windows编程(一)_第2张图片
区域和蛇身都画完了,下一篇就讲解如何移动它!

你可能感兴趣的:(Windows程序设计)