草庐IT

Visual C++实现俄罗斯方块游戏实战三:核心算法设计与实现(附源码和资源)

showswoller 2023-04-22 原文

需要源码和资源请点赞关注收藏后评论区留言私信~~~

一、主游戏类的设计

俄罗斯方块的主游戏类负责显示游戏界面,方块游戏等级等内容,同时还要管理游戏的输入操作

可以分为以下几个模块

1:游戏界面和方块的显示

2:游戏操作输入的处理

3:游戏计分的处理

4:游戏升级的处理

下面是主游戏类的实现代码

主游戏类的声明中包括消行处理,方块移动,碰撞判断,方块随机出现等成员函数

#ifndef __RUSSIA_H__
#define __RUSSIA_H__

#include "Rule.h"

#define KEY_LEFT 1
#define KEY_RIGHT 2
#define KEY_DOWN 3
#define KEY_UP 4

class CRussia  
{
public:
	void HeroWrite();
	CRussia();
	virtual ~CRussia();
	void LineDelete();
	void Move(int direction);
	bool Change(int a[][4],CPoint p,int  b[][100]);
	bool Meet(int a[][4],int direction,CPoint p);//判碰撞
	void DrawWill();//绘将要出现方块
	void DrawBK(CDC*pDC);//绘界面
	void DrawScore(CDC*pDC);//绘分数
	void GameStart();//游戏开始
	
	int Russia[100][100];//游戏数组
	int Now[4][4];	// 当前图形
	int Will[4][4];	//上一图形
	int After[4][4];	//变换后的图形
	CPoint NowPosition;	//当前图形的左上角位置
	int Count;		//当前可能出现的图形形状数,
	bool end;  //游戏结束
	int m_Level;	//级别
	int m_Speed;	//速度
	int m_Score;    //分数
	int m_CountLine;//合计消除行数
	int m_RowCount,m_ColCount;//行列数
	CBitmap fkMap;//方块
	CBitmap bkMap;//界面
	CRule rule;//游戏规则对象
};

#endif

主游戏类的基本功能函数中包括构造函数,析构函数以及绘制方块函数等,代码如下

#include "stdafx.h"
#include "Tetris.h"
#include "Russia.h"

#include "HeroDlg.h"

//
//构造函数
//
CRussia::CRussia()
{
	bkMap.LoadBitmap(IDB_BACK);
	fkMap.LoadBitmap(IDB_FANGKUAI);
}

//
//析构函数
//
CRussia::~CRussia()
{
}
//
//行消除函数
//
void CRussia::LineDelete()
{
	int m=0;		//本次共消去的行数
	bool flag=0;
	for(int i=0;i<m_RowCount;i++)
	{
		//检查要不要消行
		flag=true;
		for(int j=0;j<m_ColCount;j++)
		{
			if(Russia[i][j]==0)
			{
				flag=false;
			}
		}

		//如果要
		if(flag==true)
		{
			m++;
			for(int k=i;k>0;k--)
			{
				//上行给下行
				for(int l=0;l<m_ColCount;l++)
				{
					Russia[k][l]=Russia[k-1][l];
				}
			}
			//第一行为零
			for(int l=0;l<m_ColCount;l++)
			{
				Russia[0][l]=0;
			}
		}
	}

	DrawWill();
	//加分
	switch(m)
	{
	case 1:
		m_Score= m_Score + 10 + m_Level * 10;
		break;
	case 2:
		m_Score= m_Score + 30 + m_Level * 10;
		break;
	case 3:
		m_Score= m_Score + 50 + m_Level * 10;
		break;
	case 4:
		m_Score= m_Score + 100 + m_Level * 10;
		break;
	default:
		break;
	}
	
	m_CountLine+=m;

	m_Level = rule.UpLevel(m_CountLine);

	end = rule.Win(Now, Russia, NowPosition);

	//速度
	m_Speed=320 - m_Level * 20;
	
	if(end)
	{
		HeroWrite();
	}

}
//
//移动方块
//
void CRussia::Move(int direction)
{
	if(end) return;
	
	switch(direction)
	{
		//左
	case KEY_LEFT:
		if(Meet(Now,KEY_LEFT,NowPosition)) break;
		NowPosition.y--;
		break;
		//右
	case KEY_RIGHT:
		if(Meet(Now,KEY_RIGHT,NowPosition)) break;
		NowPosition.y++;
		break;
		//下
	case KEY_DOWN:
		if(Meet(Now,KEY_DOWN,NowPosition))
		{
			LineDelete();			
			break;
		}
		NowPosition.x++;
		break;
		//上
	case KEY_UP:
		Meet(Now,KEY_UP,NowPosition);
		break;
	default:
		break;
	}
}
//
//方块旋转
//
bool CRussia::Change(int a[][4],CPoint p,int  b[][100])
{
	int tmp[4][4];
	int i,j;
	int k=4,l=4;
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			tmp[i][j]=a[j][3-i];
			After[i][j]=0;	//存放变换后的方块矩阵
		}
	}
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(tmp[i][j]==1)
			{
				if(k>i) k=i;
				if(l>j) l=j;
			}
		}
	}
	for(i=k;i<4;i++)
	{
		for(j=l;j<4;j++)
		{
			After[i-k][j-l]=tmp[i][j];
		}	//把变换后的矩阵移到左上角
	}
	//判断是否接触,是:返回失败
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{	
			if(After[i][j]==0)
			{
				continue;
			}
			if(((p.x+i)>=m_RowCount)||((p.y+j)<0)||((p.y+j)>=m_ColCount))
			{
				return false;
			}
			if(b[p.x+i][p.y+j]==1)
			{
				return false;
			}
		}
	}
	return true;
}
//
//判碰撞,遇到了边界或者有其他方块档住
//
bool CRussia::Meet(int a[][4],int direction,CPoint p)
{
	int i,j;
	//先把原位置清0 
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(a[i][j]==1)
			{
				Russia[p.x+i][p.y+j]=0;
			}
		}
	}
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(a[i][j]==1)
			{
				switch(direction)
				{
				case 1:	//左移
					if((p.y+j-1)<0) goto exit;
					if(Russia[p.x+i][p.y+j-1]==1) goto exit;
					break;
				case 2://右移
					if((p.y+j+1)>=m_ColCount) goto exit;
					if(Russia[p.x+i][p.y+j+1]==1) goto exit;
					break;
				case 3://下移
					if((p.x+i+1)>=m_RowCount) goto exit;
					if(Russia[p.x+i+1][p.y+j]==1) goto exit;
					break;
				case 4://变换
					if(!Change(a,p,Russia)) goto exit;				
					for(i=0;i<4;i++)
					{
						for(j=0;j<4;j++)
						{
							Now[i][j]=After[i][j];
							a[i][j]=Now[i][j];
						}
					}
					return false;
					break;
				}
			}
		}
	}
			
	int x,y;
	x=p.x;
	y=p.y;
	//移动位置,重新给数组赋值
	switch(direction)
	{
	case 1:
		y--;break;
	case 2:
		y++;break;
	case 3:
		x++;break;
	case 4:
		break;
	}
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(a[i][j]==1)
			{
				Russia[x+i][y+j]=1;
			}
		}
	}
			
	return false;
exit:
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(a[i][j]==1)
			{
				Russia[p.x+i][p.y+j]=1;
			}
		}
	}
	return true;	
}
//
//绘将出现的方块图
//
void CRussia::DrawWill()
{
	int i,j;
	int k=4,l=4;
	
    //把将要出现的方块给当前数组,并把将要出现数组赋值为零
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			Now[i][j]=Will[i][j];
			Will[i][j]=0;
		}
	}
	//初始化随即数种子
	srand(GetTickCount());
	int nTemp=rand()%Count;
	//各种图形
	switch(nTemp)
	{
	case 0:
		Will[0][0]=1;
		Will[0][1]=1;
		Will[1][0]=1;
		Will[1][1]=1;
		break;
	case 1:
		Will[0][0]=1;
		Will[0][1]=1;
		Will[1][0]=1;
		Will[2][0]=1;
		break;
	case 2:
		Will[0][0]=1;
		Will[0][1]=1;
		Will[1][1]=1;
		Will[2][1]=1;
		break;
	case 3:
		Will[0][1]=1;
		Will[1][0]=1;
		Will[1][1]=1;
		Will[2][0]=1;
		break;
	case 4:
		Will[0][0]=1;
		Will[1][0]=1;
		Will[1][1]=1;
		Will[2][1]=1;
		break;
	case 5:
		Will[0][0]=1;
		Will[1][0]=1;
		Will[1][1]=1;
		Will[2][0]=1;
		break;
	case 6:
		Will[0][0]=1;
		Will[1][0]=1;
		Will[2][0]=1;
		Will[3][0]=1;
		break;
	default:
		break;
	}
	
	int tmp[4][4];
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			tmp[i][j]=Will[j][3-i];
		}
	}
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(tmp[i][j]==1)
			{
				if(k>i) k=i;
				if(l>j) l=j;
			}
		}
	}
	
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			Will[i][j]=0;
		}
	}
	//把变换后的矩阵移到左上角
	for(i=k;i<4;i++)
	{
		for(j=l;j<4;j++)
		{
			Will[i-k][j-l]=tmp[i][j];
		}
	}
	//开始位置
	NowPosition.x=0;
	NowPosition.y=m_ColCount/2;
}
//
//绘游戏界面
//
void CRussia::DrawBK(CDC*pDC)
{
	CDC Dc;
	if(Dc.CreateCompatibleDC(pDC)==FALSE)
	{
		AfxMessageBox("Can't create DC");
	}
	//画背景
    Dc.SelectObject(bkMap);
	pDC->BitBlt(0,0,540,550,&Dc,0,0,SRCCOPY);
    //画分数,速度,难度
	DrawScore(pDC);
    //如果有方块,显示方块
	//游戏区
	for(int i=0;i<m_RowCount;i++)
	{
		for(int j=0;j<m_ColCount;j++)
		{
			if(Russia[i][j]==1)
			{
				Dc.SelectObject(fkMap);
				pDC->BitBlt(j*30,i*30,30,30,&Dc,0,0,SRCCOPY);
			}
		}
	}
	//预先图形
	for(int n=0;n<4;n++)
	{
		for(int m=0;m<4;m++)
		{
			if(Will[n][m]==1)
			{	
				Dc.SelectObject(fkMap);
				pDC->BitBlt(365+m*30,240+n*30,30,30,&Dc,0,0,SRCCOPY);
			}
		}
	}
}
//
//绘分数和等级
//
void CRussia::DrawScore(CDC*pDC)
{
	int nOldDC=pDC->SaveDC();	
	//设置字体
	CFont font;    
	if(0==font.CreatePointFont(300,"Comic Sans MS"))
	{
		AfxMessageBox("Can't Create Font");
	}
	pDC->SelectObject(&font);
    //设置字体颜色及其背景颜色
	CString str;
	pDC->SetTextColor(RGB(39,244,10));
	pDC->SetBkColor(RGB(255,255,0));
    //输出数字
	str.Format("%d",m_Level);
	if(m_Level>=0)
		pDC->TextOut(420,120,str);

	str.Format("%d",m_CountLine);	
	if(m_Speed>=0)	
		pDC->TextOut(420,64,str);
	
	str.Format("%d",m_Score);	
	if(m_Score>=0)
		pDC->TextOut(420,2,str);
	
	pDC->RestoreDC(nOldDC);
}
//
//游戏开始
//
void CRussia::GameStart()
{
	end=false;//运行结束标志
    m_Score=0;		//初始分数
	m_RowCount=18;	//行数
	m_ColCount=12;	//列数
	Count=7;		//方块种类
	m_CountLine = 0;//合计消除行数为0

	char pszTmp[128] = {0};
					//读取当前游戏等级
	GetPrivateProfileString("SETUP", "level", "1", 
			pszTmp, 127, ".\\setup.ini");

	m_Level = atoi(pszTmp);		//初始等级
	m_Speed=320 - m_Level * 20;	//初始速度
	rule.SetLevel(m_Level);

	for(int i=0;i<m_RowCount;i++)
	{
		for(int j=0;j<m_ColCount;j++)
		{
			Russia[i][j]=0;
		}
	}	
	for(i=0;i<4;i++)
	{
		for(int j=0;j<4;j++)
		{
			Now[i][j]=0;
			Will[i][j]=0;
		}
	}
	//开始时将要出现方块没有生成,其不能赋值给当前方块数组,所以连续调用两次
	DrawWill();	
	DrawWill();
}

void CRussia::HeroWrite()
{
	CHeroDlg dlg;

	char pszTmp[128] = {0};

	int nHighScore = 0;
	
	GetPrivateProfileString("HERO", "score", "0", 
		pszTmp, 127, ".\\hero.ini");

	nHighScore = atoi(pszTmp);

	if(m_Score>nHighScore)
	{
		
		dlg.SetWriteFlg(TRUE);		//设置可写入标志
		
		dlg.m_level = m_Level;		//设置等级
		
		dlg.m_score = m_Score;		//设置分数
		
		dlg.DoModal();				//弹出对话框
	}
	else
	{
		AfxMessageBox("游戏结束,您未能进入英雄榜!");
	}

}

三、游戏规则类的设计与实现

包括对游戏胜负判断处理与游戏升级处理,实现如下

游戏规则声明类代码如下,包括构造函数,析构函数,升级判断函数以及胜负判断函数等等

#ifndef __RULE_H__
#define __RULE_H__

class CRule
{

public:
	CRule();
	~CRule();
	void SetLevel(int nLevel);//设置当前等级
	int UpLevel(int nLine);	//升级判断
	bool Win(int Now[4][4], int Russia [100][100], 	CPoint NowPosition);	//胜负判断
private:
	int m_nLevel;				//当前等级
};

#endif

实现游戏规则类中的升级判断函数和胜负判断函数,实现代码如下 

#include "stdafx.h"
#include "Rule.h"

CRule::CRule()
{
}

CRule::~CRule()
{
}

void CRule::SetLevel(int nLevel)
{
	m_nLevel = nLevel;
}

int CRule::UpLevel(int nLine)
{
	if(nLine / 30)
	{
		m_nLevel++;
	}
	return m_nLevel;
}

bool CRule::Win(int Now[4][4], int Russia [100][100], CPoint NowPosition)
{
	if(m_nLevel == 11)
	{//消除行数已经超过10级,游戏结束
		return true;
	}

	for(int i=0;i<4;i++)
	{
		for(int j=0;j<4;j++)
		{
			if(Now[i][j]==1)
			{//到了顶点
				if(Russia[i+NowPosition.x][j+NowPosition.y]==1)
				{
					return true;
				}
			}
		}
	}

	return false;
}

 

 

 

创作不易 觉得有帮助请点赞关注收藏~~~

有关Visual C++实现俄罗斯方块游戏实战三:核心算法设计与实现(附源码和资源)的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  9. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  10. ruby - 我需要从 facebook 游戏中抓取数据——使用 ruby - 2

    修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它​​是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为

随机推荐