C++练习实例———控制台代码实现俄罗斯方块小游戏

    之前一直在引擎上用脚本编程来制作游戏系统,总感觉自己写的代码太偏顶层,最近突然对在控制台上做经典小游戏产生了浓厚兴趣,昨天在vs上写了一个俄罗斯方块的小游戏,输出画面就靠windows.h,简单又有趣。下面上代码。

    首先写一个坐标类,方便后面的操作

#ifndef POINT_H
#define POINT_H
//坐标类
class Point
{
public:
	Point(int x = 0, int y = 0) : m_x(x), m_y(y){};
	~Point() {};
	Point& operator=(const Point &p)
	{
		m_x = p.m_x;
		m_y = p.m_y;
		return *this;
	}
	void Set(const int x,const int y) {m_x = x;m_y = y;}
	void SetX(const int x) { m_x = x; }
	void SetY(const int y) { m_y = y; }
	int GetX() const { return m_x; }
	int GetY()const { return m_y; }
private:
	int m_x;
	int m_y;
};


#endif

    再创建一个设置类,用来管理游戏中的数值。因为这些数值(分数、游戏速度等)是伴随着整个游戏过程的,所以设为静态

#include <list>
#ifndef SETTING_H
#define SETTING_H
using namespace std;
//数值设定类,用于计算分数和速度等
class Setting
{
public:
	static int GetSumScore() { return m_SumScore; }
	static void AddSpeed() {  m_GameSpeed -= 3; }
	static void AddScore() { m_SumScore++; }
	static int GetGameSpeed() { return m_GameSpeed; }
	static void HalfSpeed() { m_GameSpeed /= 2; }
	static void DoubleSpeed(){ m_GameSpeed *= 2; }
private:
	static int m_GameSpeed;     //游戏速度,数字越小速度越快
	static int m_SumScore;		// 分数
};
#endif
#include "Setting.h"
int Setting::m_GameSpeed = 500;
int Setting::m_SumScore = 0;

    工程虽小,我还是用了继承,创建一个Object类,是所有游戏对象都要继承的父类

#ifndef OBJECT
#define OBJECT
#include <list>
#include"Point.h"
#include<iostream>
#include<windows.h>
#include<time.h>
#include<conio.h>
using namespace std;
//物体类,一切游戏对象的基类
class Object
{
public:
	virtual void Display() = 0;//输出画面
	virtual void SetDisappear() = 0;//清除画面
protected:
	Point m_Position;
};
#endif

    然后是方块类,是指组成一个整体形状的每一个小方块

#ifndef SINGLE_BOX
#define SINGLE_BOX
#include"Object.h"
//小方块类
class SingleBox :Object
{
private:
	int m_color;
public:
	SingleBox() {}
	SingleBox(Point pos, int color)
	{
		m_Position = pos;
		m_color = color;
	}
	~SingleBox(){}
	void Display();//输出小方块
	void SetDisappear();//删除小方块
	int  GetBoxX() const{ return m_Position.GetX(); }
	int  GetBoxY()const{ return m_Position.GetY(); }
};
#endif
#include"SingleBox.h"
void SingleBox:: Display()
{
	COORD pos;
	pos.X = m_Position.GetX();
	pos.Y = m_Position.GetY();
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), m_color);
	cout << "■";
}
void SingleBox::SetDisappear()
{
	COORD pos;
	pos.X = m_Position.GetX();
	pos.Y = m_Position.GetY();
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	cout << " ";
}

    小方块类写好后,就可以写组合形状的类了,它包含一个存储小方块的数组,因为每个形状由四个小方块组成

#ifndef COMBO_BOX
#define COMBO_BOX
#include"SingleBox.h"
enum Type { A, B, C, D, E};//一共五种组合方块
enum Form { a, b, c, d };//每个组合方块最多四种形态
enum Dir { LEFT, RIGHT ,EMPTY};
//组合方块类
class ComboBox :Object
{
private:
	SingleBox m_ComBox[4];//每个组合方块由四个小方块组成
	Type m_Type;
	Form m_Form;
	Dir m_Direction;
	int m_XDis;//每输出一个方块,x轴占两个坐标(y轴只占一个)
public:
	ComboBox(Point pos, Type);
	~ComboBox(){}
	Type GetType()const{return m_Type;}
	void SetDirection(const Dir dir) { m_Direction = dir; }
	void Display();
	void Transform();//确定每个小方块的坐标
	void SetDisappear();//删除组合方块
	void Move();
	void Rotate();
	bool JudgeMove(const bool mark[100][100]);//判断是否可以移动
	bool JudgeIllegal(const bool mark[100][100]);//判断当前位置是否违法
	void SetMark(bool mark[100][100]);//将停止的方块位置在Mark数组中记为true,与边界等效
	bool IsExit();
};
#endif
#include "ComboBox.h"

ComboBox::ComboBox(Point pos, Type type)
{
	m_XDis = 2;
	m_Position = pos;
	m_Type = type;
	m_Form = a;
	m_Direction = EMPTY;
}
bool ComboBox::JudgeIllegal(const bool mark[100][100])
{
	for (int i = 0; i < 4; i++)
	{
		if (mark[m_ComBox[i].GetBoxX()][m_ComBox[i].GetBoxY()] == true)//方块旋转后触碰了边界
		{
			//反旋转
			if (m_Form == a) { m_Form = d;  return false; }
			if (m_Form == b) { m_Form = a;   return false; }
			if (m_Form == c) { m_Form = b;   return false; }
			if (m_Form == d) { m_Form = c;  return false; }
		}
	}
	return true;
}
void ComboBox::Transform()
{
	//旋转皆为逆时针
	if (m_Type == A)//L形
	{
		if (m_Form == a)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 3);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 3);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 2), 3);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 2), 3);
		}
		if (m_Form == b)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 3);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 3);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 3);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY()), 3);
		}
		if (m_Form == c)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY()), 3);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 3);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 3);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 2), 3);
		}
		if (m_Form == d)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 2), 3);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 3);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 3);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 3);
		}
	}
	if (m_Type == B)//直线形
	{
		if (m_Form == a || m_Form == c)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY()), 4);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 4);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY()), 4);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 2 * m_XDis, this->m_Position.GetY()), 4);
		}
		if (m_Form == b || m_Form == d)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() - 2), 4);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() - 1), 4);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 4);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 4);
		}
	}
	if (m_Type == C)//方形
	{
		m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 5);
		m_ComBox[1] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY()), 5);
		m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 5);
		m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 5);
	}
	if (m_Type == D)//闪电形
	{
		if (m_Form == a || m_Form == c)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY()), 6);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 6);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 6);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 6);
		}
		if (m_Form == b || m_Form == d)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() - 1), 6);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 6);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY()), 6);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 6);
		}
	}
	if (m_Type == E)//凸字形
	{
		if (m_Form == a)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 9);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 9);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 9);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 9);
		}
		if (m_Form == b)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 9);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 9);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 2), 9);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 9);
		}
		if (m_Form == c)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX() - 1 * m_XDis, this->m_Position.GetY() + 1), 9);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 9);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 9);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 2), 9);
		}
		if (m_Form == d)
		{
			m_ComBox[0] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY()), 9);
			m_ComBox[1] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 1), 9);
			m_ComBox[2] = SingleBox(Point(this->m_Position.GetX(), this->m_Position.GetY() + 2), 9);
			m_ComBox[3] = SingleBox(Point(this->m_Position.GetX() + 1 * m_XDis, this->m_Position.GetY() + 1), 9);
		}
	}
}
void ComboBox::Display()//输出方块
{
	for (int i = 0; i < 4; i++)
		m_ComBox[i].Display();
}
void ComboBox::SetDisappear()//删除方块
{
	for (int i = 0; i < 4; i++)
		m_ComBox[i].SetDisappear();
}
void ComboBox::Move()
{
	m_Position.SetY(m_Position.GetY() + 1);
	if (m_Direction == LEFT)m_Position.SetX(m_Position.GetX() - 1 * m_XDis);
	if (m_Direction == RIGHT)m_Position.SetX(m_Position.GetX() + 1 * m_XDis);
	m_Direction = EMPTY;
}
void ComboBox::Rotate()
{
	if (m_Form == a) { m_Form = b; return; }
	if (m_Form == b) { m_Form = c; return; }
	if (m_Form == c) { m_Form = d; return; }
	if (m_Form == d) { m_Form = a; return; }
}
bool ComboBox::JudgeMove(const bool mark[100][100])//
{
	if (this->m_Direction == LEFT)
	{
		if (mark[m_ComBox[0].GetBoxX() - m_XDis][m_ComBox[0].GetBoxY()] == true ||
			mark[m_ComBox[1].GetBoxX() - m_XDis][m_ComBox[1].GetBoxY()] == true ||
			mark[m_ComBox[2].GetBoxX() - m_XDis][m_ComBox[2].GetBoxY()] == true ||
			mark[m_ComBox[3].GetBoxX() - m_XDis][m_ComBox[3].GetBoxY()] == true)
			//即将碰到左边界
			m_Direction = EMPTY;
	}
	if (this->m_Direction == RIGHT)
	{
		if (mark[m_ComBox[0].GetBoxX() + m_XDis][m_ComBox[0].GetBoxY()] == true ||
			mark[m_ComBox[1].GetBoxX() + m_XDis][m_ComBox[1].GetBoxY()] == true ||
			mark[m_ComBox[2].GetBoxX() + m_XDis][m_ComBox[2].GetBoxY()] == true ||
			mark[m_ComBox[3].GetBoxX() + m_XDis][m_ComBox[3].GetBoxY()] == true)
			//即将碰到右边界
			m_Direction = EMPTY;
	}
	if (mark[m_ComBox[0].GetBoxX()][m_ComBox[0].GetBoxY() + 1] == true ||
		mark[m_ComBox[1].GetBoxX()][m_ComBox[1].GetBoxY() + 1] == true ||
		mark[m_ComBox[2].GetBoxX()][m_ComBox[2].GetBoxY() + 1] == true ||
		mark[m_ComBox[3].GetBoxX()][m_ComBox[3].GetBoxY() + 1] == true)
		//即将停止
		return false;
	else return true;
}
void ComboBox::SetMark(bool mark[100][100])
{
	for (int i = 0; i < 4; i++)
		mark[m_ComBox[i].GetBoxX()][m_ComBox[i].GetBoxY()] = true;
}
bool ComboBox::IsExit()
{
	if (m_Position.GetY() == 3)return true;
	else return false;
}

    最后是主函数,主要逻辑就是先初始化场景然后进入循环,这里我坐标值设置的略随意,场景的形状是照着网上的一个游戏画面画的(懒的自己画。。)

#include"ComboBox.h"
#include"Setting.h"

const Point LeftUpPos(13, 3);
bool Mark[100][100]; // 用于标记坐标,若为true说明该坐标有方块存在
void init();//初始化框架函数
void SetColor(const int a);
void SetCOORD(const int x,const int y);
void DeleteLine();

int main() {
	srand(time(0)); // 取系统时间
	//隐藏光标
	CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
	//创建两个方块
	ComboBox nowbox(Point(33, 3), Type(rand() % (sizeof(Type) + 1)));
	ComboBox nextbox(Point(60, 6), Type(rand() % (sizeof(Type) + 1)));
	
	init();//初始化框架

	char ch(NULL);
	while (1)//当用户按回车开始游戏
	{
		Sleep(100);
		ch = _getch();
		if (ch == 13)break;
	}
	while (1) //开始游戏
	{
		SetColor(15); SetCOORD(63, 19); cout << Setting::GetSumScore();//输出分数
		 //计算两个方块中每个小方块的坐标并输出
		nowbox.Transform();
		nextbox.Transform();
		nextbox.Display();
		nowbox.Display();
		//控制游戏速度
		Sleep(Setting::GetGameSpeed());
		//如果用户刚刚加速,现在将速度恢复
		if (ch == 's')Setting::DoubleSpeed();
		ch = NULL;
		//删除上一时刻输出的方块
		nowbox.SetDisappear();
		if (_kbhit())//响应键盘
		{
			ch = _getch();
			if (ch == 'p')//暂停
			{
				ch = _getch();
				if (ch == 'p')continue;
			}
			if (ch == 'q')exit(0);//退出
			if (ch == 's')Setting::HalfSpeed();//加速
			if (ch == 'a')nowbox.SetDirection(LEFT);//左移
			if (ch == 'd')nowbox.SetDirection(RIGHT);//右移
			if (ch == 'w')nowbox.Rotate();//旋转
		}
		nowbox.Transform();//更新坐标位置
		if (!nowbox.JudgeIllegal(Mark))nowbox.Transform();//若更新后物体位置违规,进行恢复旋转

		if (nowbox.JudgeMove(Mark))//判断是否继续移动
			nowbox.Move();

		else//若不能继续移动,则应当生成下一个方块
		{
			nowbox.Display();
			if (nowbox.IsExit())exit(0);//判断该方块是否停在出生点,若是则游戏结束
			nowbox.SetMark(Mark);//将停止移动的方块坐标在MARK数组里设置为true
			DeleteLine();//判断是否有可消除的行,有则消除
			Setting::AddSpeed();
			nowbox = ComboBox(Point(33, 3), nextbox.GetType());//新的方块
			nextbox.SetDisappear();
			nextbox = ComboBox(Point(60, 6), Type(rand() % (sizeof(Type) + 1)));
		}
	}
}
void init()
{
	SetColor(10);
	int x = LeftUpPos.GetX(), y = LeftUpPos.GetY();
	//画出大方框
	for (int i = 0; i < 30; ++i)
	{
		SetCOORD(x, 2);  Mark[x][2] = true; cout << "■";
		SetCOORD(x, 23); Mark[x][23] = true; cout << "■";
		x += 2;
	}
	for (int i = 0; i < 21; ++i)
	{
		SetCOORD(LeftUpPos.GetX(), y);
		Mark[LeftUpPos.GetX()][y] = true;
		cout << "■";
		SetCOORD(71, y);
		Mark[71][y] = true;
		cout << "■";
		SetCOORD(51, y);
		Mark[51][y] = true;
		cout << "■";
		y += 1;
	}
	//小框架内容
	for (int i = 53; i <= 69; i += 2)
	{
		SetCOORD(i, 11);
		Mark[i][11] = true;
		cout << "■";
	}
	SetColor(11);
	SetCOORD(53, 3);  cout << " Next Box : ";
	SetCOORD(54, 13); cout << "Start :  Enter ";
	SetCOORD(54, 15); cout << "Pause :   P ";
	SetCOORD(54, 17); cout << "Exit  :   Q ";
	SetCOORD(54, 19);  cout << "Score :";
}

void DeleteLine()
{
	int x = LeftUpPos.GetX() + 2, y = LeftUpPos.GetY();
	bool flag(false);
	for (int j = 0; j < 20; j++)
	{
		for (int i = 0; i < 18; i++)
		{
			if (!Mark[x][y])
			{
				flag = true;//这一行不完整,不应该删除
				break;
			}
			x += 2;
		}
		if (!flag)//这一行应该消除
		{
			for (int j = 22; j > 3; j--)
			{
				x = LeftUpPos.GetX() + 2;
				for (int i = 0; i < 18; i++)
				{
					if (j <= y) // 应该删除的行以上的所有行往下错一个位置
						Mark[x][j] = Mark[x][j - 1];
					if (Mark[x][j])
					{
						SetColor(7);
						SetCOORD(x, j);
						cout << "■";
					}
					else
					{
						SetCOORD(x, j);
						cout << " ";
					}
					x += 2;
				}
			}
			Setting::AddScore();
		}
		y++;
		x = LeftUpPos.GetX() + 2;
		flag = false;
	}
}
void SetCOORD(int x, int y)
{
	COORD pos;
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
void SetColor(int a)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), a);
}

    出来后的效果如下

    由于时间紧凑,可能写的略粗糙,谢谢观看:)

猜你喜欢

转载自blog.csdn.net/qq_37553152/article/details/81136733