MFC-俄罗斯方块

项目地址:

https://yunpan.cn/cYkeA6gGbFrXD (提取码:a2a3)


一、定义了一些结构体和常量

// 每个俄罗斯方块(block)由4个正方形(pston)组成,一个正方形(pston)的定义如下:
typedef struct pston
{
	int row;
	int col;
} ty_pston;

// 定义一个俄罗斯方块(block),包含了4个正方形pst,及颜色属性color,距离左l与顶t的位置
typedef struct block
{
	ty_pston pst[4];
	COLORREF color;
	int t;
	int l;
} ty_block;

// 游戏区域(gamePst)的坐标(ps)是否有方块(bSet)
typedef struct gamePst
{
	BOOL bSet;
	COLORREF color;
	ty_pston ps;
} ty_gamepst;

// 随机颜色
#define COLOR_RAND RGB(rand()%256, rand() % 256, rand() % 256)

// 俄罗斯方块(block)的种类(pst_kind)有7种,每种有4个正方形(pston)
const ty_pston pst_kind[7][4] = {
	{ {0,-1}, {0,0}, {0,1}, {0,2} },
	{ { 0,-1},{ 0,0 },{ 0,1 },{ 1,1 } },
	{ { 0,-1 },{ 0,0 },{ 0,1 },{ -1,1 } },
	{ { 0,-1 },{ 0,0 },{ -1,0 },{ -1,1 } },
	{ { 0,-1 },{ 0,0 },{ 1,0 },{ 1,1 } },
	{ { 0,-1 },{ 0,0 },{ 1,0 },{ 1,-1 } },
	{ { 0,-1 },{ 0,0 },{ 0,1 },{ 1,0 } },
};

// 俄罗斯方块的移动方向(direction)
enum direction {LEFT = -1,DOWN = 0,RIGHT = 1};

// 每个正方形(pston)的宽度
const int PST_WIDTH = 24;

// 游戏区域相对于左边与上边的距离
const int GAME_SIDE_LEFT = 10; 
const int GAME_SIDE_TOP = 10;

二、俄罗斯方块头文件和cpp

#pragma once
class CStone
{
public:
	CStone();
	~CStone();
private:
	ty_block m_block;
	int curBlockType;
	ty_gamepst m_gPst[18][10];
	int score;
public:
	ty_block m_preBlock;
public:
	void drawBlock();
	void makeBlock(ty_block& block);
	void alterBlock();
	void MoveTo(int to);
	void reMakeBlock();
	BOOL canMove(ty_block block);
	CRect getPstRect(pston pst);
	CRect getGamePstRect(pston pst);
	CRect getPrePstRect(pston pst);
	BOOL IsTop();
};
------------

#include "stdafx.h"
#include "Stone.h"
#include 
#include 
#include 
#include "TetrisDlg.h"
CStone::CStone()
{
	// 初始化时生成一个游戏区的俄罗斯方块
	makeBlock(m_block);
	// 把游戏区域18*10=180个正方形初始化状态与位置
	for (int i = 0; i < 18; ++i)
		for (int j = 0; j < 10; ++j)
			m_gPst[i][j] = { FALSE ,RGB(255,255,255),{ i,j } };
	// 初始化分数
	score = 0;
}
CStone::~CStone()
{
}
// 生成一个俄罗斯方块
void CStone::makeBlock(ty_block& block)
{
	srand((unsigned)time(0));
	curBlockType = rand() % 7;
	for (int i = 0; i < 4; ++i)
		block.pst[i] = pst_kind[curBlockType][i];
	block.color = COLOR_RAND;
	block.l = 4;
	block.t = 0;
}
// 绘制界面
void CStone::drawBlock()
{
	// 获取客户区的绘图设备
	CClientDC* p_dc = (CClientDC*)AfxGetMainWnd()->GetDC();
	// 获取兼容的绘图设备
	CDC mdc;
	mdc.CreateCompatibleDC(p_dc);
	// 获取兼容的位图
	CBitmap bmp_cpb;
	bmp_cpb.CreateCompatibleBitmap(p_dc, 10 * PST_WIDTH, 18 * PST_WIDTH);
	// 载入位图
	mdc.SelectObject(&bmp_cpb);
	// 填充背景色为白色
	mdc.FillSolidRect(CRect(0,0,10* PST_WIDTH,18* PST_WIDTH), RGB(255,255,255));
	// 绘制游戏区的俄罗斯方块
	for (int a = 0; a < 4;++a)
	{
		CBrush * bs_block = new CBrush(m_block.color);
		mdc.FillRect(getPstRect(m_block.pst[a]), bs_block);
		bs_block->DeleteObject();
		bs_block = nullptr;
	}
	// 绘制游戏区的底部正方形
	for (int i = 0; i < 18;++i)
	{
		for (int j = 0; j < 10;++j)
		{
			if (m_gPst[i][j].bSet) {
				CBrush * bs_gPst = new CBrush(m_gPst[i][j].color);
				mdc.FillRect(getGamePstRect(m_gPst[i][j].ps), bs_gPst);
				bs_gPst->DeleteObject();
				bs_gPst = nullptr;
			}
		}
	}
	// 将缓冲区的位图显示在游戏区
	p_dc->BitBlt(GAME_SIDE_LEFT, GAME_SIDE_TOP, 10 * PST_WIDTH, 18 * PST_WIDTH, &mdc, 0, 0, SRCCOPY);
	mdc.DeleteDC();
	// 获取兼容的绘图设备
	CDC premdc;
	premdc.CreateCompatibleDC(p_dc);
	// 绘制预览区的俄罗斯方块
	CBitmap bmp_precpb;
	bmp_precpb.CreateCompatibleBitmap(p_dc, 6 * PST_WIDTH, 18 * PST_WIDTH);
	// 载入位图
	premdc.SelectObject(&bmp_precpb);
	// 填充背景色为白色
	premdc.FillSolidRect(CRect(0, 0, 6 * PST_WIDTH, 18 * PST_WIDTH), RGB(255, 255, 255));
	for (int b = 0; b < 4; ++b)
	{
		CBrush * bs_preblock = new CBrush(m_preBlock.color);
		premdc.FillRect(getPrePstRect(m_preBlock.pst[b]), bs_preblock);
		bs_preblock->DeleteObject();
		bs_preblock = nullptr;
	}
	// 绘制游戏得分情况
	CString curScore;
	curScore.Format(_T("score:%d"), score);
	premdc.TextOut(2*PST_WIDTH, 300, curScore);
	// 将缓冲区的位图显示在预览区
	p_dc->BitBlt((10+1)*PST_WIDTH +GAME_SIDE_LEFT, GAME_SIDE_TOP, (10+1+6) * PST_WIDTH, 18 * PST_WIDTH, &premdc, 0, 0, SRCCOPY);
	premdc.DeleteDC();
	p_dc->DeleteDC();
}

// 获取俄罗斯方块中的正方形区域
CRect CStone::getPstRect(pston pst)
{
	if ((pst.row + m_block.t) < 0 || (pst.col+ m_block.l) < 0) {
		return CRect(0,0,0,0);
	}
	return CRect(
		(pst.col+m_block.l)*PST_WIDTH, 
		(pst.row+m_block.t)*PST_WIDTH, 
		(pst.col + m_block.l+1) *PST_WIDTH, 
		(pst.row + m_block.t+1) *PST_WIDTH
		);
}
// 获取游戏区域中的每个正方形的区域
CRect CStone::getGamePstRect(pston pst)
{
	return CRect(
		(pst.col)*PST_WIDTH,
		(pst.row)*PST_WIDTH,
		(pst.col + 1) *PST_WIDTH,
		(pst.row + 1) *PST_WIDTH
		);
}
// 获取预览区的俄罗斯方块的区域
CRect CStone::getPrePstRect(pston pst)
{
	return CRect(
		(pst.col+m_preBlock.l-1)*PST_WIDTH,
		(pst.row + m_preBlock.t+3)*PST_WIDTH,
		(pst.col + m_preBlock.l)*PST_WIDTH,
		(pst.row + m_preBlock.t +4)*PST_WIDTH
		);
}
// 变形
void CStone::alterBlock()
{
	ty_block tempcopy = m_block;
	auto swapBlock = [](ty_pston & ps) {
		int temp = ps.row;
		ps.row = ps.col*(-1);
		ps.col = temp;
	};
	for (ty_pston &ps : tempcopy.pst)
	{
		swapBlock(ps);
	}
	if (canMove(tempcopy)) {
		m_block = tempcopy;
	}
}
// 移动
void CStone::MoveTo(int to)
{
	if (to == DOWN) {
		++m_block.t;
		if (!canMove(m_block))
		{
			--m_block.t;
			reMakeBlock();
		}
	}
	else {
		m_block.l += to;
		if (!canMove(m_block))
		{
			m_block.l -= to;
		}
	}
}
// 判断能否移动
BOOL CStone::canMove(ty_block block)
{
	std::vector vcol,vrow;
	for (ty_pston e : block.pst)
	{
		if (m_gPst[e.row + block.t][e.col + block.l].bSet)
			return FALSE;
		vcol.push_back(e.col);
		vrow.push_back(e.row);
	}
	int colMax = *std::max_element(vcol.begin(), vcol.end());
	int colMin = *std::min_element(vcol.begin(), vcol.end());
	int rowMax = *std::max_element(vrow.begin(),vrow.end());
	return (colMin + block.l) >= 0 && (colMax + block.l) < 10 && (rowMax + block.t) < 18;
}
// 重新生成俄罗斯方块
void CStone::reMakeBlock()
{
	for (ty_pston e : m_block.pst)
	{
		m_gPst[e.row + m_block.t][e.col + m_block.l].bSet = TRUE;
		m_gPst[e.row + m_block.t][e.col + m_block.l].color = m_block.color;
	}
	auto isfull = [=](int row) {
		for (int j=0; j < 10; ++j)
			if (!m_gPst[row][j].bSet)
				return FALSE;
		return TRUE;
	};
	for (int i=0; i < 18; )
	{
		if (isfull(i)) {
			for (int k = i; k > 0;--k)
			{
				for (int t = 0; t < 10;++t)
				{
					m_gPst[k][t].bSet  = m_gPst[k - 1][t].bSet;
					m_gPst[k][t].color = m_gPst[k - 1][t].color;
				}
			}
			score += 100;
			continue;
		}
		++i;
	}
	m_block = m_preBlock;
	makeBlock(m_preBlock);
	IsTop();
}


BOOL CStone::IsTop()
{
	++m_block.t;
	if (!canMove(m_block))
	{
		KillTimer(AfxGetMainWnd()->m_hWnd, 1002);
		((CTetrisDlg*)AfxGetMainWnd())->m_status = FALSE;
		AfxMessageBox(_T("游戏已结束"));
	}
	--m_block.t;
	return 0;
}

三、对话框

void CTetrisDlg::OnPaint()
{
	// 游戏区域
	CRect rc(9, 9, 251, 443);
	// 预览区
	CRect preRc(273, 9, 420, 445);
	CClientDC* p_dc = (CClientDC*)GetDC();
	CPen * pen = new CPen(PS_SOLID, 1, RGB(255, 0, 0));
	CPen * oldpen = p_dc->SelectObject(pen);
	p_dc->Rectangle(rc);
	p_dc->Rectangle(preRc);
	p_dc->SelectObject(oldpen);
	pen->DeleteObject();
	pen = nullptr;
	p_dc->Detach();
}


void CTetrisDlg::OnBnClickedOk()
{
	if (!m_status) {
		m_status = TRUE;
		m_stone->makeBlock(m_stone->m_preBlock);
		m_stone->drawBlock();
		SetTimer(1002, 300, 0);
	}
	else {
		m_status = FALSE;
		KillTimer(1002);
	}
}




BOOL CTetrisDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	if (pMsg->message == WM_KEYDOWN) {
		if (!m_status)
		{
			return TRUE;
		}
		switch (pMsg->wParam)
		{
		case VK_LEFT:
			m_stone->MoveTo(LEFT);
			break;
		case VK_RIGHT:
			m_stone->MoveTo(RIGHT);
			break;
		case VK_DOWN:
			m_stone->MoveTo(DOWN);
			break;
		case VK_UP:
			m_stone->alterBlock();
			break;
		}
		m_stone->drawBlock();
		return TRUE;
	}
	return CDialog::PreTranslateMessage(pMsg);
}


void CTetrisDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nIDEvent) {
	case 1002:
	{
		m_stone->MoveTo(DOWN);
		break;
	}
	default:
		break;
	}
	m_stone->drawBlock();
	CDialog::OnTimer(nIDEvent);
}




你可能感兴趣的:(CC++)