SimpleCG小游戏开发系列(3)--俄罗斯方块(方法1)

一、预览

         还是先来看看游戏预览效果。

二、游戏玩法介绍

        《俄罗斯方块》(Tetris,俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。

        游戏玩法是由四个小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

三、主要编写逻辑介绍

        俄罗斯方块涉及的主要难点是各种形状方块的表示,通过观察,可以发现所有形状都是由四块小方块组成。而且形状可以通过旋转变换。所以我们首先提取出方块的所有可能形状如下

要表示这种方块结构,我们可以有不同方式,其中一种我们可以发现方块横竖最多是4个方块,所以我们可以用一个4*4的数组点阵来表示。另外一种是直接用四个小方块坐标表示,将在下期讲解。那么先看看4*4的数组点阵如何表示。

直接定义4*4的整型数组

int nBlocks[4][4];

 有方块的值为1,没有方块的为0,即可表示如下图形。

代码如下

tagTeris g_TetrsTemplate[] = {
	{
		1, 1, 1, 1,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		1, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		0, 1, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		0, 0, 1, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		0, 1, 1, 0,
		0, 1, 1, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		1, 1, 0, 0,
		0, 1, 1, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		0, 1, 1, 0,
		1, 1, 0, 0,
		0, 0, 0, 0
	}
};

 第二个难点就是方块的旋转。有了4*4数组,旋转也就迎刃而解了,我们可以直接把整个数组按顺时针或逆时针方向旋转后设置对应值即可。

左转:

for(i=0;i<4;++i)
		for(j=0;j<4;++j)
			temp.nBlocks[i][j]=g_TetrsRunning.nBlocks[j][3-i];

右转:


	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
			temp.nBlocks[i][j]=g_TetrsRunning.nBlocks[3-j][i];

当方块表示方法有了,旋转问题也解决了,其他的代码逻辑就简单了,这里就不列出来了,有兴趣的同学可以在文末下载全部代码察看。

四、所有主要逻辑代码

        框架代码大同小异,有看之前的文章的话很容易对接起来,就不列出来了。以下就是俄罗斯方块的主要逻辑代码。当然为了教学需求,只是最简单的版本,有兴趣的同学可以适当增加难度和添加更多有趣的元素及界面美化。

Tetris.h

#ifndef _TETRIS_H
#define _TETRIS_H

#include "../import/include/SimpleCG.h"
#define C_IMAGE_BLOCK			20
#define C_MAP_WIDTH				10
#define C_MAP_HEIGHT			20
struct _tagTetris
{
	int nBlocks[4][4];
};
typedef struct _tagTetris tagTeris;
int GetSpeed();
//初始化
void NewBlock();
//绘制单个方块
void DrawBlock( int nX, int nY, COLORREF nColor );
//绘制背景
void DrawMapBack( );
//绘制界面
void RenderBack();
//绘制内容
void DrawMap( );
//绘制移动中方块
void DrawMovinggBlock( );
//转动左边
void TurnLeft();
//转动右边
void TurnRight();
//移动左边
void MoveLeft();
//移动右边
void MoveRight();
//获取方块最左最右位置
void GetCurLeftRight(tagTeris *pTest, int *pLeft, int *pRight);
//是否可以往左右移动
bool CanMovHor(int nStepX);
//是否可以往下移动
bool IsExisted(tagTeris *pTest);
//往下移动
bool MovingDown();
//清除
void ClearBlock();
//清除
void ClearLine(int nY);
#endif

Tetris.cpp

#include "Tetris.h"

enum ENUM_MAPTYPE
{
	  enumMAPTYPE_NULL
	, enumMAPTYPE_FIXBLOCK
	, enumMAPTYPE_DEADBLOCK
};
int g_nMap[ C_MAP_WIDTH * C_MAP_HEIGHT] = {0};
int g_nXMapPos = 100;
int g_nYMapPos = 50;
tagTeris g_TetrsTemplate[] = {
	{
		1, 1, 1, 1,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		1, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		0, 1, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		1, 1, 1, 0,
		0, 0, 1, 0,
		0, 0, 0, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		0, 1, 1, 0,
		0, 1, 1, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		1, 1, 0, 0,
		0, 1, 1, 0,
		0, 0, 0, 0
	},
	{
		0, 0, 0, 0,
		0, 1, 1, 0,
		1, 1, 0, 0,
		0, 0, 0, 0
	}
};

tagTeris g_TetrsRunning = {0};
int g_nMovingX = 0;
int g_nMovingY = 0;
tagTeris g_Next=g_TetrsTemplate[1];
int g_nColorBlockNext = RGB(rand()%255,rand()%255,rand()%255);
int g_nColorBlock = g_nColorBlockNext;
int g_nSpeed = 9;
int g_nScore = 0;
int GetSpeed()
{
	return g_nSpeed;
}
//创建新的方块
void NewBlock()
{
	g_TetrsRunning = g_Next;
	int nCount = sizeof(g_TetrsTemplate)/sizeof(g_TetrsTemplate[0]);
	g_Next = g_TetrsTemplate[rand()%nCount];
	g_nColorBlock = g_nColorBlockNext;

	g_nMovingY = 0;
	g_nMovingX = 4;
	g_nColorBlockNext = RGB(rand()%255,rand()%255,rand()%255);
	g_nScore += 10;
}
//绘制单个方块,绝对坐标
void DrawBlockPos( int nX, int nY, COLORREF nColor )
{
	setlinewidth(3);
	setfillcolor(nColor);
	setlinecolor(SCGSubColor(nColor,100));
	fillrectangle( nX, nY, nX + C_IMAGE_BLOCK-3, nY + C_IMAGE_BLOCK-3);

	setlinecolor(SCGAddColor(nColor,100));
	line(nX, nY, nX + C_IMAGE_BLOCK-5,nY);
	line(nX, nY, nX,nY + C_IMAGE_BLOCK-5);
}
//绘制单个方块,地图坐标
void DrawBlock( int nX, int nY, COLORREF nColor )
{
	int nXPos = g_nXMapPos + nX*C_IMAGE_BLOCK + 2;
	int nYPos = g_nYMapPos + nY*C_IMAGE_BLOCK;
	DrawBlockPos(nXPos, nYPos,nColor);
}
//绘制形状方块,地图坐标
void DrawTerisBlock( tagTeris *Tetris, int nX, int nY, COLORREF nColor )
{
	int i=0;
	int j=0;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
			if(Tetris->nBlocks[i][j])
			{
				DrawBlock( nX + j, i + nY, nColor );
			};
}
//绘制背景
void DrawMapBack(  )
{
	clearrectangle(g_nXMapPos+1,g_nYMapPos-1,g_nXMapPos+1+C_IMAGE_BLOCK*C_MAP_WIDTH, g_nYMapPos+C_IMAGE_BLOCK*C_MAP_HEIGHT);
	clearrectangle(320,90,350+C_IMAGE_BLOCK*4, 240);
	
	DrawTerisBlock(&g_Next, 12, 3, g_nColorBlockNext);
	outtextXY(350,80,_T("下一个"));
	outtextXY(320,180,_T("当前级别"));
	outtextXY(320,220,_T("当前分数"));
	printfXY( 400, 220, _T("%d"), g_nScore );
	printfXY( 400, 180, _T("%d"), 10 - g_nSpeed);
}
//绘制界面
void RenderBack()
{
	setline(0,	1, 0);
	line(g_nXMapPos,g_nYMapPos,g_nXMapPos,g_nYMapPos+C_IMAGE_BLOCK*C_MAP_HEIGHT);
	line(g_nXMapPos+C_IMAGE_BLOCK*C_MAP_WIDTH,g_nYMapPos,g_nXMapPos+C_IMAGE_BLOCK*C_MAP_WIDTH,g_nYMapPos+C_IMAGE_BLOCK*C_MAP_HEIGHT);
	line(g_nXMapPos,g_nYMapPos+C_IMAGE_BLOCK*C_MAP_HEIGHT,g_nXMapPos+C_IMAGE_BLOCK*C_MAP_WIDTH,g_nYMapPos+C_IMAGE_BLOCK*C_MAP_HEIGHT);

}
//绘制内容
void DrawMap(  )
{
	int i=0;
	int j=0;
	for(j=0;j<C_MAP_HEIGHT;++j)
	{
		for(i=0;i<C_MAP_WIDTH;++i)
		{
			switch( g_nMap[j*C_MAP_WIDTH+i] )
			{
			case enumMAPTYPE_FIXBLOCK:
				DrawBlock( i, j, RGB(30,60,235) );
				break;
			case enumMAPTYPE_DEADBLOCK:
				break;
			}
			
		}
	}
}
//绘制移动中方块
void DrawMovinggBlock(  )
{
	DrawTerisBlock(&g_TetrsRunning, g_nMovingX, g_nMovingY, g_nColorBlock);
}
//转动左边,00=03,01=13,02=23...10=02,11=12,12=22
void TurnLeft()
{
	tagTeris temp;
	int i=0;
	int j=0;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
			temp.nBlocks[i][j]=g_TetrsRunning.nBlocks[j][3-i];
	if(!IsExisted(&temp))
		return;
	int nLeft = 0;
	int nRight = 0;
	GetCurLeftRight(&temp, &nLeft, &nRight);
	if( nLeft<0 )
	{
		g_nMovingX -= nLeft;
	}
	if( nRight>=C_MAP_WIDTH )
	{
		g_nMovingX -= nRight - C_MAP_WIDTH + 1;
	}
	g_TetrsRunning = temp;
}
//转动右边00=30,01=20,02=10...10=31,11=21,12=11
void TurnRight()
{
	tagTeris temp;
	int i=0;
	int j=0;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
			temp.nBlocks[i][j]=g_TetrsRunning.nBlocks[3-j][i];
	if(!IsExisted(&temp))
		return;
	int nLeft = 0;
	int nRight = 0;
	GetCurLeftRight(&temp, &nLeft, &nRight);
	if( nLeft<0 )
	{
		g_nMovingX -= nLeft;
	}
	if( nRight>=C_MAP_WIDTH )
	{
		g_nMovingX -= nRight - C_MAP_WIDTH + 1;
	}
	g_TetrsRunning = temp;
}
//移动左边
void MoveLeft()
{
	if(CanMovHor(-1))
		--g_nMovingX;
}
//移动右边
void MoveRight()
{
	if(CanMovHor(1))
		++g_nMovingX;
}
//获取方块最左位置
void GetCurLeftRight(tagTeris *pTest, int *pLeft, int *pRight)
{
	int i=0;
	int j=0;
	*pLeft = 99;
	*pRight = -1;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
		{
			if(pTest->nBlocks[i][j])
			{
				if( *pLeft>j + g_nMovingX )
					*pLeft = j + g_nMovingX;
				if( *pRight<j + g_nMovingX )
					*pRight = j + g_nMovingX;
			};
		}
}
//是否可以往左右移动
bool CanMovHor(int nStepX)
{
	int i=0;
	int j=0;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
		{
			if(g_TetrsRunning.nBlocks[i][j])
			{
				int nX=j + g_nMovingX + nStepX;
				int nY=i + g_nMovingY;
				if(nX<0 || nX>=C_MAP_WIDTH )
					return false;
				if( g_nMap[nY*C_MAP_WIDTH+nX] != enumMAPTYPE_NULL )
					return false;
			};
		}
	return true;
}
//是否与原来方块冲突
bool IsExisted(tagTeris *pTest)
{
	int i=0;
	int j=0;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j)
		{
			if(pTest->nBlocks[i][j])
			{
				int nY=i + g_nMovingY + 1;
				if( nY>=C_MAP_HEIGHT )
					return false;
				if( g_nMap[nY*C_MAP_WIDTH+j+g_nMovingX] )
					return false;
			};
		}
	return true;
}
//往下移动
bool MovingDown()
{
	if(!IsExisted(&g_TetrsRunning))
	{
		int i=0;
		int j=0;
		for(i=0;i<4;++i)
			for(j=0;j<4;++j)
			{
				if(g_TetrsRunning.nBlocks[i][j])
				{
					int nY=i + g_nMovingY;
					g_nMap[nY*C_MAP_WIDTH+j+g_nMovingX] = enumMAPTYPE_FIXBLOCK;
					//测试是否结束
					if(nY<=0)
					{
						outtextXY(200,20,_T("游戏结束"));
						return false;
					}
				}
			}


		NewBlock();
		return false;
	}
	++g_nMovingY;
	return true;
}
//清除
void ClearBlock()
{
	int i=0;
	int j=0;
	bool bIsClear=true;
	for(j=C_MAP_HEIGHT-1;j>=0;--j)
	{
		bIsClear=true;
		for(i=0;i<C_MAP_WIDTH;++i)
		{
			if( g_nMap[j*C_MAP_WIDTH+i]==enumMAPTYPE_NULL )
			{
				bIsClear = false;
				break;
			}
			
		}
		if(bIsClear)
		{
			ClearLine(j);
			g_nScore += 100;
			if(g_nScore>=(10-g_nSpeed)*1000 && g_nSpeed>0)
				--g_nSpeed;
			++j;
		}
	}
}
//清除
void ClearLine(int nY)
{
	int i=0;
	int j=0;
	for(j=nY;j>0;--j)
	{
		for(i=0;i<C_MAP_WIDTH;++i)
		{
			g_nMap[j*C_MAP_WIDTH+i]=g_nMap[(j-1)*C_MAP_WIDTH+i];
		}
	}
}

五、相关代码即图形库下载

源码下载

GameRussiaTetris · master · b2b160 / SimpleCG_Demo · GitCode

执行程序下载

bin/GameRussiaTetris.zip · master · b2b160 / SimpleCG_Demo · GitCode

 编译此程序需安装SimpleCG库,安装方法如下: 

SimpleCG库安装方法

 

下一篇:另一种方法实现俄罗斯方块 

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

b2b160

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值