C++小游戏——俄罗斯方块

记录一下我的成长历程,这是我学了C++之后,写的第一个程序,虽然比较简陋,但也不想改了,有兴趣的小伙伴可以参考一下。

#include<iostream>
#include<windows.h>
#include<conio.h>//使用getch()函数
#include<ctime>
#include <stdio.h>


using namespace std;
HANDLE handle;
COORD crd;//坐标
const short LMARGIN = 7;//方块掉落范围的左边界
const short TMARGIN = 5;//方块掉落范围的上边界
char type[19][17] =
{
	//方块种类
	"1100011000000000","0010011001000000",//Z型
	"0110110000000000","0100011000100000",//S型
	"0000111100000000","0100010001000100",//I型
	"0000011001100000",//方型
	"0100111000000000","0100110001000000","1110010000000000","1000110010000000",//T型
	"0010111000000000","0110001000100000","1110100000000000","0100010001100000",//L型
	"1000111000000000","0010001001100000","1110001000000000","0110010001000000"//J型
};
char rotate_hinder[19][17] =
{
	//方块旋转受阻位置
	"0010000001100000","1100000000100000",//Z型
	"0000001011100000","0010100011000000",//S型
	"1111000001110111","1011101100110011",//I型
	"0000000000000000",//方型
	"1010000011000000","1010001000100000","0000100010000000","0100001001100000",//T型
	"0100000011100000","1000110000000000","0000010011100000","1010101000000000",//L型
	"0110000011100000","1010101000100000","0000110011000000","1000101000100000"//J型
};
short Alter(short n)//旋转函数
{
	switch (n)
	{
	case 1:case 3:case 5:return n - 1; break;
	case 6:return 6; break;
	case 10:case 14:case 18:return n - 3; break;
	default:return n + 1; break;
	}
}
void SetPos(short x, short y)//坐标函数
{
	crd.X = x; crd.Y = y;
	SetConsoleCursorPosition(handle, crd);
}
void SetColor(short front, short back)//颜色
{
	SetConsoleTextAttribute(handle, front + back * 0x10);
}
void PrintGameOver()
{
	SetPos(12, 9);
	SetColor(5, 6);
	cout << "Game Over!";
	SetColor(7, 0);
}
class Score
{
private:
	short ln;//得分
public:
	void Reset() { ln = 0; }
	void Print()
	{
		SetPos(4, 1);
		SetColor(2, 7);
		cout << "Lines: " << ln << " ";
		SetColor(7, 0);
	}
	Score() { Reset(); }
	void Update(short n) { ln += n; }
};
class Shape//方块类
{
private:
	short present;//当前形状
	short color;
	short x;//左上角横坐标
	short y;//左上角纵坐标
public:
	Shape(short, short, short);//颜色、坐标
	void Draw(bool, bool);//第一个参数为真执行绘制操作,为假执行擦除;第二个参数为真按方块颜色画,为假画成白色
	friend class Board;
	bool Rotate(Board& brd);//判断方块能否旋转,如果能则旋转并返回真,如果不能旋转则返回假
	short Collision(Board& brd);//左侧,右侧,下方受阻时,返回值分别“位或”1,2,4
	bool Move(short, Board& brd);//左移动第一个参数为1,右移动第一个参数为2
	bool Fall(Board& brd);//能下落返回true,不能下落返回false
	void DrawElseWhere(short _x, short _y);//在操作区外面显示一个方块的初始形状
};
class Board//游戏框类
{
private:
	bool wall[22][14];
	short bottom_line;//保存当前可以消除的最下面一行的行号
public:
	void Clear()//重置数组元素的值
	{
		for (short i = 0; i < 22; i++)
			for (short j = 0; j < 14; j++)
				if (j > 1 && j < 12 && i < 20)
					wall[i][j] = 0;
				else
					wall[i][j] = 1;
	}
	Board() { Clear(); }
	void DrawBoard()
	{
		short i;
		SetColor(0xe, 0);//十进制的14
		for (i = 0; i < 21; i++)//绘制左边界
		{
			SetPos(LMARGIN - 2, TMARGIN + i - 1);
			cout << "■";
		}
		for (i = 0; i < 21; i++)
		{
			SetPos(LMARGIN + 20, TMARGIN + i - 1);
			cout << "■";
		}
		for (i = 0; i < 10; i++)
		{
			SetPos(LMARGIN + i * 2, TMARGIN + 19);
			cout << "■";
		}
		SetColor(7, 0);
	}
	short SetBlocValue(Shape& shp)//对wall数组的值更新
	{
		for (short i = 0; i < 16; i++)
			if (type[shp.present][i] == '1')
			{
				if (shp.y == 0)//检查游戏有没有结束
					return -1;
				wall[shp.y + i / 4][shp.x + i % 4] = true;
			}
		return 0;
	}
	short Drop(Shape& shp)//返回可以消除的行数
	{
		short i, n;//i用于遍历方块所在的16个字符,n用来保存检测过的被方块占用的格子数
		short j;//用于遍历一整行检查是否有wall数组的元素为false
		short k;//用于从上往下逐行往下覆盖
		short y, _y = -1;//y用于保存当前格子的行号,_y用于保存前一个格子的行号,初始化一个不合理值,确保第一轮对比不相等
		short lines = 0;//保存了消除多少排
		short top;//用于检测有没有遍历到堆积块的顶部
		short l;//在覆盖的操作当中从左到右遍历
		for (i = 0, n = 0; n < 4 && i < 16; i++)
		{
			if (type[shp.present][i] == '1')
			{
				y = shp.y + i / 4;
				n++;
				if (y == _y)//两个格子处于同一横排不必进行两次消除判断
					continue;
				_y = y;
				for (j = 2; j < 12; j++)
					if (!wall[y][j])
						break;
				if (j == 12)//一整排已经填满,应消除
				{
					lines++;
					for (k = y; k > 0; k--)
					{
						top = 0;
						for (l = 2; l < 12; l++)
						{
							wall[k][l] = wall[k - 1][l];
							top += wall[k - 1][l];
						}
						if (top == 0)
							break;//终止从上到下的遍历
					}
				}
			}
		}
		bottom_line = _y;
		return lines;
	}
	void DrowPile()//重绘堆积块,有bottom_line指定需要重绘的最下面一行
	{
		for (short i = 0; i <= bottom_line; i++)//行遍历
		{
			for (short j = 2; j < 12; j++)//列遍历
			{
				SetPos(LMARGIN - 4 + j * 2, TMARGIN - 1 + i);
				if (wall[i][j])
				{
					SetColor(7, 7);//白色用于绘出
					cout << "□";
				}
				else
				{
					SetColor(0, 0);//黑色用于擦除
					cout << "  ";
				}
			}
		}
	}
	friend class Shape;
};
Shape::Shape(short _color, short _x = 5, short _y = 0) :color(_color), x(_x), y(_y)
{
	switch (_color)
	{
	case 1:present = 0; break;
	case 2:present = 2; break;
	case 3:present = 4; break;
	case 4:present = 6; break;
	case 5:present = 7; break;
	case 6:present = 11; break;
	default:color = 8; present = 15; break;
	}
}
void Shape::Draw(bool flag = true, bool active = true)
{
	if (!flag)//擦除
		SetColor(0, 0);
	else
		if (active)
			SetColor(color, color);//按方块颜色画
		else
			SetColor(7, 7);//落地方块画成白色
	for (short i = 0; i < 16; i++)
		if (type[present][i] == '1')
		{
			SetPos(LMARGIN - 4 + (x + i % 4) * 2, TMARGIN + y + i / 4 - 1);
			cout << "□";
		}
	SetColor(7, 0);
}
bool Shape::Rotate(Board& brd)
{
	for (short i = 0; i < 16; i++)
	{
		if (brd.wall[y + i / 4][x + i % 4] && rotate_hinder[present][i] == '1')//如果格局中的一个位置被占用,并且这个位置能阻碍旋转
			return false;
	}
	Draw(false);
	present = Alter(present);
	Draw();
	return true;
}
short Shape::Collision(Board& brd)
{
	short result = 0;
	for (short i = 0; i < 16; i++)
	{
		if (type[present][i] == '1')//该格子被当前方块占用
		{
			if (brd.wall[y + i / 4][x + i % 4 - 1])
				result |= 1;//左受阻
			if (brd.wall[y + i / 4][x + i % 4 + 1])
				result |= 2;//右受阻
			if (brd.wall[y + i / 4 + 1][x + i % 4])
				result |= 4;//下受阻
		}
	}
	return result;
}
bool Shape::Move(short direction, Board& brd)
{
	if (direction == 1 && !(Collision(brd) & 1))//表达式Collision(brd)&1为0,说明左侧没有受阻,取反之后条件为true
	{
		Draw(false, true);
		x--;
		Draw();
		return true;
	}
	if (direction == 2 && !(Collision(brd) & 2))//表达式Collision(brd)&2为0,说明右侧没有受阻,取反之后条件为true
	{
		Draw(false, true);
		x++;
		Draw();
		return true;
	}
	return false;
}
bool Shape::Fall(Board& brd)
{
	if (Collision(brd) & 4)//已经着地
	{
		Draw(true, false);
		return false;
	}
	Draw(false, true);
	y++;
	Draw();
	return true;
}
void Shape::DrawElseWhere(short _x, short _y)
{
	short i;
	SetPos(_x, _y);
	SetColor(7, 0);
	cout << "Next: ";
	_x += 6;
	SetColor(0, 0);
	SetPos(_x, _y);
	for (i = 0; i < 12; i++)//擦除上一个方块,方块的初始形状不会占据最后一行,只需要遍历12格就行
	{
		SetPos(_x + i % 4 * 2, _y + i / 4);
		cout << "  ";
	}
	SetColor(color, color);
	_y -= (present == 6 || present == 4);//将方块和棍子形状向上提升一格
	for (i = 0; i < 12; i++)//绘制下一个方块
	{
		if (type[present][i] == '1')
		{
			SetPos(_x + i % 4 * 2, _y + i / 4);
			cout << "□";
		}
	}
}
short Menu()//菜单函数,选中开始游戏返回1,离开返回2
{
	short choice = 1;
	char c;//记录按键信息
	system("cls");
	SetPos(9, 12);
	cout << "┌────────────────┐";
	SetPos(9, 13);
	cout << "│ 俄 罗 斯 方 块 │";
	SetPos(9, 14);
	cout << "└────────────────┘";
	SetPos(7, 21);
	cout << "按下方向键控制方块";
	SetPos(7, 23);
	cout << "按空格键暂停/继续";
	while (1)
	{
		SetPos(15, 16);
		if (choice == 1)
			SetColor(2, 7);
		else
			SetColor(2, 0);
		cout << "开始游戏";
		SetPos(15, 18);
		if (choice == 2)
			SetColor(2, 7);
		else
			SetColor(2, 0);
		cout << "离    开";
		c = _getch();
		if (c == 13)
			break;
		else if (c == 0)//没有这条语句的话,按下方向键,循环体会执行两遍
			c = _getch();//方向键比较特殊,第一次获取c的值为0,第二次才是72,或者80
		if (c == 72 || c == 80)
			choice = choice == 1 ? 2 : 1;
	}
	SetColor(7, 0);
	return choice;
}


int main()
{
	system("mode con cols=35 lines=27");//设置控制台尺寸
	handle = GetStdHandle(STD_OUTPUT_HANDLE);
	Board board;
	short tmp;//用来保存一个方块消去的行数
	Shape* shp, * shp_next;
	Score score;
	short times;//用于保存执行Sleep函数的次数
	srand(time(0));
	char c;//用于记录按键信息
	while (Menu() == 1)//一次游戏的过程
	{
		system("cls");
		board.Clear();
		board.DrawBoard();
		score.Reset();//得分归零
		score.Print();//显示得分
		shp_next = new Shape(rand() % 7 + 1);
		while (1)//一个方块落地的过程
		{
			shp = shp_next;//将下一个方块赋给当前方块
			shp_next = new Shape(rand() % 7 + 1);//新产生下一个方块
			shp_next->DrawElseWhere(17, 1);//显示下一个方块
			times = 0;
			while (1)//捕捉一次按键的过程,循环10次下落一格
			{
				shp->Draw();
				if (GetAsyncKeyState(VK_LEFT) == -32767)//VK_LEFT是左方向键的虚拟键码
					shp->Move(1, board);
				if (GetAsyncKeyState(VK_RIGHT) == -32767)//VK_RIGHT是右方向键的虚拟键码
					shp->Move(2, board);
				if (GetAsyncKeyState(VK_UP) == -32767)//VK_UP是上方向键的虚拟键码
					shp->Rotate(board);
				if (GetAsyncKeyState(VK_DOWN) == -32767)//VK_DOWN是下方向键的虚拟键码
					shp->Fall(board);
				if (GetAsyncKeyState(VK_SPACE) == -32767)//空格键暂停
				{
					while (!(GetAsyncKeyState(VK_SPACE) == -32767))//没有按下空格键延时25毫秒继续循环,再次按下空格键继续
						Sleep(25);
				}
				Sleep(25);//延时
				times++;
				if (times == 10)
				{
					times = 0;
					if (!shp->Fall(board))
						break;
				}
			}
			if (board.SetBlocValue(*shp) == -1)
			{
				PrintGameOver();
				fflush(stdin);//清空输入流
				while ((c = getchar()) != '\n' && c != EOF);
				delete shp_next;
				break;
			}
			if (tmp = board.Drop(*shp))
			{
				board.DrowPile();
				score.Update(tmp);//累加得分
				score.Print();//显示得分
			}
			//.........
			delete shp;
		}
	}


	return 0;
}

传送门:
在这里插入图片描述

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
罗斯方块是一款经典的游戏,它的实现可以使用C++语言。游戏的实现需要用到图形界面库和计时器等功能。游戏的主要逻辑是方块的移动和旋转,以及方块与地图的碰撞检测。下面是一个简单的俄罗斯方块游戏实现的代码框架: ``` #include <iostream> #include <windows.h> #include <conio.h> #include <time.h> using namespace std; const int WIDTH = 10; // 地图宽度 const int HEIGHT = 20; // 地图高度 const int BLOCK_SIZE = 4; // 方块大小 int map[HEIGHT][WIDTH]; // 地图数组 int block[BLOCK_SIZE][BLOCK_SIZE]; // 方块数组 int curX, curY; // 当前方块的位置 int curBlock; // 当前方块的形状 int nextBlock; // 下一个方块的形状 int score; // 得分 int level; // 等级 // 初始化游戏 void initGame(); // 绘制游戏界面 void drawGame(); // 生成新的方块 void newBlock(); // 方块下落 void blockDown(); // 方块左移 void blockLeft(); // 方块右移 void blockRight(); // 方块旋转 void blockRotate(); // 消除满行 void clearLine(); // 判断游戏是否结束 bool isGameOver(); // 主函数 int main() { initGame(); while (true) { drawGame(); blockDown(); if (isGameOver()) { break; } } return 0; } ``` 其,initGame()函数用于初始化游戏数据,drawGame()函数用于绘制游戏界面,newBlock()函数用于生成新的方块,blockDown()函数用于方块下落,blockLeft()和blockRight()函数用于方块左右移动,blockRotate()函数用于方块旋转,clearLine()函数用于消除满行,isGameOver()函数用于判断游戏是否结束。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值