之前一直在引擎上用脚本编程来制作游戏系统,总感觉自己写的代码太偏顶层,最近突然对在控制台上做经典小游戏产生了浓厚兴趣,昨天在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);
}
出来后的效果如下
由于时间紧凑,可能写的略粗糙,谢谢观看:)