今天的任务 和代码量都非常少,我们每天一小步的慢慢积累~

昨天的代码中,我们把游戏区域的代码 直接写在OnPaint里面的,以后产生食物,画蛇 都要在OnPaint里面调用,所以会照成OnPaint里面代码特别多,不好维护,我们现在先来解决下个这个问题,我们创建一个函数void DrawGameRange(CDC& dc),把昨天画框的代码移到这里面来~

void CSnakeDlg::DrawGameRange(CDC& dc)
{
	CBrush brushGame(RGB(0, 0, 0));//创建黑色画刷 用来画游戏区域
	CBrush brushBorder(RGB(34, 176, 76));//创建绿色画刷 用来画边框

										 //遍历行、列来画不同的颜色
	for (int i = 0; i < GAME_COL; i++)
	{
		for (int j = 0; j < GAME_ROW; j++)
		{
			//根据行列来计算一个矩形大小,每个矩形占20个像素
			CRect rt;
			rt.left = i * 20;
			rt.top = j * 20;
			rt.right = rt.left + 20;
			rt.bottom = rt.top + 20;
			if (m_arrGameRange[i][j] == 1)//需要画边框
			{
				//填充矩形的函数,
				//第一个参数是要填充的矩形
				//第二个参数是什么什么画刷填充矩形
				dc.FillRect(rt, &brushBorder);
			}
			else
			{
				dc.FillRect(rt, &brushGame);
			}
		}
	}
}

然后再在OnPaint里调下DrawGameRange函数

void CSnakeDlg::OnPaint()
{
	CPaintDC dc(this); // 用于绘制的设备上下文

	DrawGameRange(dc);
	CDialogEx::OnPaint();
}

好的,昨天的代码已经优化的差不多了(双缓冲不急,我准备写完再弄),开始我们今天的任务吧


我们先看看要实现的效果


我们在中间画出一条蛇,蛇头比其他地方小一点就行了(先不管美观的事,我们能区分蛇头就行了)

我们昨天建立了一个游戏区域的数组,1是墙壁,0是游戏区域,那么,我们蛇也可以放里面,2表示蛇,这样完全没问题,可以显示出来,但是想想,要移动蛇就比较蛋疼了,得遍历整个数组不说,移动起来还麻烦,放弃这个想法,换个思路,

我们来分析分析,蛇身其实也是界面上的一个点,跟墙壁一样,也就是说,我们知道它身体每个节在哪个点(行、列)就行了,至于我们怎么存,那就是怎么移动方便怎么存,还要考虑蛇变长的问题,这里我考虑用链表来存,链表里面的每一个元素就是蛇的身体,如果蛇有5节,那么list里面就有5个元素,每个元素是个结构体,表示蛇在界面的哪一行 那一列

好了,有目标就好干了,首先我们得有个结构体,存行、列

typedef struct SNAKE
{
	int nRow; //蛇身在界面的哪行
	int nCol; //蛇身在界面的哪列
}SNAKE;

然后我们创建一个list 由于c++stl库里面有list 就不自己实现了,

我们先包含list的头文件和命名空间

#include <list>
using namespace std;

定义一个list

list<SNAKE> m_listSnake;

上面步骤都在snakedlg.h里面做的哦~

接下来,我们在OnPaint里面画蛇身吧,我们上面说了,都写在OnPaint里面不方便维护和管理, 我们定义一个函数,专门用来画蛇身~函数原型如下

void CSnakeDlg::DrawSnake(CDC& dc);

好了,头文件的事搞完了,现在我的Snakedlg.h文件是这样的

#include <list>
using namespace std;

#define GAME_ROW 18 //游戏区域的行
#define GAME_COL 28 //游戏区域的列

typedef struct SNAKE
{
	int nRow; //蛇身在界面的哪行
	int nCol; //蛇身在界面的哪列
}SNAKE;

// CSnakeDlg 对话框
class CSnakeDlg : public CDialogEx
{
// 构造
public:
	CSnakeDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_SNAKE_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
private:
	void InitGameData();
	void DrawGameRange(CDC& dc);
	void CSnakeDlg::DrawSnake(CDC& dc);
private:
	int m_arrGameRange[GAME_COL][GAME_ROW];//定义一个18行 20列的数组,用来表示游戏区域
	list<SNAKE> m_listSnake; //存放蛇身每一个节点
};

接下来,我们在InitGameData()里面初始化一条蛇出来,在昨天的代码后面,我们添加一条有5节身体的蛇,代码如下

//在界面上增加一条蛇
	SNAKE s;
	s.nCol = 8;
	s.nRow = 8;
	for (int i=0;i<5;i++)
	{
		m_listSnake.push_back(s);
		s.nCol++;//因为我们得蛇是横向的,所以有列一直加,行不变
	}

整个InitGameData()的代码如下

void CSnakeDlg::InitGameData()
{
	//初始化游戏界面
	for (int i=0; i<GAME_COL; i++)
	{
		for (int j=0; j<GAME_ROW;j++)
		{
			if ((i==0||i==GAME_COL-1)||(j==0||j==GAME_ROW-1))
			{
				m_arrGameRange[i][j] = 1;
			}
			else
			{
				m_arrGameRange[i][j] = 0;
			}
		}
	}
	//在界面上增加一条蛇
	SNAKE s;
	s.nCol = 8;
	s.nRow = 8;
	for (int i=0;i<5;i++)
	{
		m_listSnake.push_back(s);
		s.nCol++;//因为我们得蛇是横向的,所以有列一直加,行不变
	}
}

上面代码比较简单,我们初始化的时候蛇头在8行8列,然后第二节在8行9列,以此类推

接下里,我们实现画蛇的函数DrawSnake(CDC& dc)

void CSnakeDlg::DrawSnake(CDC& dc)
{
	CBrush brushSnake(RGB(0, 162, 232));//创建蓝色画刷 用来画蛇

	list<SNAKE>::iterator it = m_listSnake.begin();
	while (it != m_listSnake.end())//遍历list取出每个元素
	{
		CRect rt;
		/*
		蛇身体的每个节的矩形大小都是20个像素
		跟之前计算游戏区域矩形大小一样,大家也可以把这个数字改小
		*/
		rt.left = it->nCol * 20;
		rt.top = it->nRow * 20;
		rt.right = rt.left + 20;
		rt.bottom = rt.top + 20;

		dc.FillRect(rt,&brushSnake);
		it++;
	}
}

我们只要遍历存放蛇身的list,然后根据他存放的x y 来算出要画的矩形,然后涂上蓝色即可,

然后我们在OnPaint里面调用我们得函数,

void CSnakeDlg::OnPaint()
{
	CPaintDC dc(this); // 用于绘制的设备上下文

	DrawGameRange(dc);
	DrawSnake(dc);
	CDialogEx::OnPaint();
}

运行起来,看看效果


果然画出蛇了,等等,怎么跟我们之前说的不一样,蛇头并没有区分出来啊~别急,还有最后一点点代码需要完善,我们在DrawSnake函数里面判断下,是第一个元素,用不同的方法画即可, 还记得我们昨天代码调试的时候画出来的都是黑边那个吗?我们就用那个方法画~

void CSnakeDlg::DrawSnake(CDC& dc)
{
	CBrush brushSnake(RGB(0, 162, 232));//创建蓝色画刷 用来画蛇

	list<SNAKE>::iterator it = m_listSnake.begin();
	while (it != m_listSnake.end())//遍历list的每个元素
	{
		CRect rt;
		rt.left = it->nCol * 20;
		rt.top = it->nRow * 20;
		rt.right = rt.left + 20;
		rt.bottom = rt.top + 20;
		/*
		蛇身体的每个节的矩形大小都是20个像素
		跟之前计算游戏区域矩形大小一样,大家也可以把这个数字改小
		*/
		if (it == m_listSnake.begin())//判断是不是第一个节点 也就是 “蛇头”
		{
			HBRUSH hBrush = (HBRUSH)dc.SelectObject(brushSnake);//把画刷选入我们得DC
			dc.Rectangle(rt);//画个有边框的矩形,因为边框是黑色,我们的背景也是黑色,所以看起来就小了点
			/*
			如果你背景不是黑色也没关系,你可以反正有个黑色的框框,也能看出蛇头,当然,你可以
			创建一个别的颜色的画笔,,然后选入这样边框就不是黑色,
			也可以画个圆dc.Ellipse(rt);
			*/
			
			dc.SelectObject(hBrush);
		}
		else
		{
			dc.FillRect(rt,&brushSnake);
		}
		it++;
	}
}

我们加了个if判断,判断是不是头结点,如果是,就画了一个带黑色边框的“头”,这样给人的感觉就是头比身体小了,当然,大家也可以把尾巴做个标志什么的,希望大家多修改,多增加自己的想法,这样才能有进步,有问题也欢迎大家留言,我都会一一回复的

打赏