游戏开发(二)——控制台 俄罗斯方块

俄罗斯方块游戏设计中主要须要注意的几点:

1:依旧是坐标的定义:定义为左上角为(0,0),向右为x正方向,向下为y正方向


2:游戏画面是分两个区域的。左边是游戏区域,就是俄罗斯方块下落的区域。右边一个小的显示下一个方块是什么的区域。

可是,方块出现并開始下落时,并非一个方块直接出如今画面顶部,而是从最上面一行開始,一行一行的逐行落下来。

比方一个竖长条,并非一出现就直接占了4行,而是一格一格的出现,一格一格的落下来的,这里也用了一个取巧的做法,

就是在游戏画面顶部,多定义4行,仅仅是这4行不显示,一个新的方块出来,是放在不显示的那4行上,然后一行一行的下移,

使得游戏画面看起来像是方块一格一格的出现的效果


3:俄罗斯方块有7种,每种有4个方向,这里定义成一个常量数组,固定填好每一个格子的相对坐标。

/*
        □■■□    □■■□    □■■□    □■■□
        □■■□    □■■□    □■■□    □■■□
        □□□□    □□□□    □□□□    □□□□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □■□□    □■■□    □■□□    □■■□
        □■■□    ■■□□    □■■□    ■■□□
        □□■□    □□□□    □□■□    □□□□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □□■□    ■■□□    □□■□    ■■□□
        □■■□    □■■□    □■■□    □■■□
        □■□□    □□□□    □■□□    □□□□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □■□□    □□□□    ■■□□    □□■□
        □■□□    ■■■□    □■□□    ■■■□
        □■■□    ■□□□    □■□□    □□□□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □■□□    ■□□□    □■■□    □□□□
        □■□□    ■■■□    □■□□    ■■■□
        ■■□□    □□□□    □■□□    □□■□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □■□□    □■□□    □□□□    □■□□
        ■■■□    □■■□    ■■■□    ■■□□
        □□□□    □■□□    □■□□    □■□□
        □□□□    □□□□    □□□□    □□□□
*/
/*
        □■□□    □□□□    □■□□    □□□□
        □■□□    ■■■■    □■□□    ■■■■
        □■□□    □□□□    □■□□    □□□□
        □■□□    □□□□    □■□□    □□□□
*/

4:俄罗斯方块类的设计:

属性:

m_TypeIndex,表示当前方块是7种俄罗斯方块中的哪一种

m_TurnIndex,表示当前方块是4种朝向中的哪一种

m_Row_y,m_Column_x,表示以4*4范围为准的左上角当前坐标,转向时也做为对齐的基准点

m_Tetris,表示方块4个小格的当前位置,是相对坐标加上m_Row_y,m_Column_x算出来的坐标

动作:

Init,初始化这个方块

MoveTo,将当前方块移动到某个坐标

Turn,方块转向

Down,方块下移

Left,方块左移

Right,方块右移

GetTetris,获取当前4个小格,主要是用来获取方块的每一个小格坐标


5:地图场景的设计,和之前的贪吃蛇的场景类似

地图是个二维数组,在控制台下,每一个元素能够用一个字符表示:'  '表示空地,'□'表示落究竟不能再动的方块格子,'■';表示正在下落的方块格子,以及右边显示的下一个方块格子,'◆'表示边框

这个二维数组用到了之前的数组模板

这里採用两个二维数组m_map1,和m_map2作为双缓冲,能够比对游戏前后的变化,每次游戏画面的更新,仅仅重绘改变的部分,这样能够防止全屏重绘导致的闪屏。

地图上还包含三个成员,当前下落的方块m_CurrTetris,下一个方块m_NextTetris,以及用来測试方块下落的下一个位置能否够到达的測试方块m_TestTetris

地图场景提供的功能主要有:

RandNextTetris,随机生成下一个方块

RandCurrTetris,将下一个方块方块赋值给当前方块

IsShow,推断一个方块是否已经移出顶部不显示的那4行,未移出的方块不能做转向,左右移动的操作

IsDeath,推断场景中是否有随意一列方块累积到顶了,触顶则游戏结束

CanMove,推断方块能否够移动或转向,主要用到了上面的m_TestTetris

CanRemove,推断是否有一行方格全满,一行全满的则能够消除

Remove,消除满的方格行

ProcessLogic,处理用户按下上、下、左、右时,方块的移动和转向,以及能否移动的推断,能否消除行的推断,落底之后的新方块的生成


6:控制台的程序,怎样将字符绘制到指定的位置上去,和上一篇贪吃蛇类似

用到了FillConsoleOutputCharacter,以及检測按键的_kbhit,和_getch

详细函数功能就不介绍了,能够google


7:定时下落的控制,计时器


8:游戏主循环逻辑,与上一篇贪吃蛇一致,能够直接套用

    //初始化  
    while(true)  
    {  
        if (玩家有键盘操作)  
        {  
            if (ESC键)  
            {  
                //跳出循环  
            }  
            else if (方向键)  
            {  
                if (游戏正在进行中没有结束)  
                {  
                    //方块按玩家操作的方向移动  
                }  
            }  
            else //其它按键  
            {  
                //不处理  
            }  
        }  
      
        //假定300毫秒处理一次游戏逻辑  
        //就是无论人是否控制的情况下,每300毫秒方块固定移动一步  
        if (300毫秒间隔时间到)  
        {  
            if (游戏正在进行中没有结束)  
            {  
                //方块按默认方向移动  
            }  
        }  
          
        if (游戏结束)  
        {  
            //显示游戏结束  
        }  
      
        sleep  
    }  

游戏截图:


以下先给出代码


Tetris.h:

#ifndef _Tetris_
#define _Tetris_

typedef struct TetrisGrid
{
	int row_y;
	int column_x;

	TetrisGrid& operator= (const TetrisGrid& temp);
}Grid;

const int TETRIS_UP = 1;
const int TETRIS_DOWN = 2;
const int TETRIS_LEFT = 3;
const int TETRIS_RIGHT = 4;

const int MAX_GRID = 4;//每一个俄罗斯方块4个小格
typedef struct Tetris
{
	TetrisGrid grids[MAX_GRID];

	Tetris& operator= (const Tetris& temp);

	void MoveTo(int row_y, int column_x);

	void Left();

	void Right();

	void Down();
}Tetris;

const int MAX_TETRIS_FORWARD = 4;//俄罗斯方块4种朝向
const int MAX_TETRIS_COUNT = 7;//7种俄罗斯方块
const Tetris g_tetris[MAX_TETRIS_COUNT][MAX_TETRIS_FORWARD] = 
{
	{
		/*
		□■■□	□■■□	□■■□	□■■□
		□■■□	□■■□	□■■□	□■■□
		□□□□	□□□□	□□□□	□□□□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,1},{0,2},{1,1},{1,2}}},
		{{{0,1},{0,2},{1,1},{1,2}}},
		{{{0,1},{0,2},{1,1},{1,2}}},
		{{{0,1},{0,2},{1,1},{1,2}}},
	},
	{
		/*
		□■□□	□■■□	□■□□	□■■□
		□■■□	■■□□	□■■□	■■□□
		□□■□	□□□□	□□■□	□□□□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,1},{1,1},{1,2},{2,2}}},
		{{{0,1},{0,2},{1,0},{1,1}}},
		{{{0,1},{1,1},{1,2},{2,2}}},
		{{{0,1},{0,2},{1,0},{1,1}}},
	},
	{
		/*
		□□■□	■■□□	□□■□	■■□□
		□■■□	□■■□	□■■□	□■■□
		□■□□	□□□□	□■□□	□□□□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,2},{1,1},{1,2},{2,1}}},
		{{{0,0},{0,1},{1,1},{1,2}}},
		{{{0,2},{1,1},{1,2},{2,1}}},
		{{{0,0},{0,1},{1,1},{1,2}}},
	},
	{
		/*
		□■□□	□□□□	■■□□	□□■□
		□■□□	■■■□	□■□□	■■■□
		□■■□	■□□□	□■□□	□□□□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,1},{1,1},{2,1},{2,2}}},
		{{{1,0},{1,1},{1,2},{2,0}}},
		{{{0,0},{0,1},{1,1},{2,1}}},
		{{{0,2},{1,0},{1,1},{1,2}}},
	},
	{
		/*
		□■□□	■□□□	□■■□	□□□□
		□■□□	■■■□	□■□□	■■■□
		■■□□	□□□□	□■□□	□□■□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,1},{1,1},{2,0},{2,1}}},
		{{{0,0},{1,0},{1,1},{1,2}}},
		{{{0,1},{0,2},{1,1},{2,1}}},
		{{{1,0},{1,1},{1,2},{2,2}}},
	},
	{
		/*
		□■□□	□■□□	□□□□	□■□□
		■■■□	□■■□	■■■□	■■□□
		□□□□	□■□□	□■□□	□■□□
		□□□□	□□□□	□□□□	□□□□
		*/
		{{{0,1},{1,0},{1,1},{1,2}}},
		{{{0,1},{1,1},{1,2},{2,1}}},
		{{{1,0},{1,1},{1,2},{2,1}}},
		{{{0,1},{1,0},{1,1},{2,1}}},
	},
	{
		/*
		□■□□	□□□□	□■□□	□□□□
		□■□□	■■■■	□■□□	■■■■
		□■□□	□□□□	□■□□	□□□□
		□■□□	□□□□	□■□□	□□□□
		*/
		{{{0,1},{1,1},{2,1},{3,1}}},
		{{{1,0},{1,1},{1,2},{1,3}}},
		{{{0,1},{1,1},{2,1},{3,1}}},
		{{{1,0},{1,1},{1,2},{1,3}}},
	}
};

class MyTetris
{
public:
	void Init(int row_y, int column_x, int typeIndex, int turnIndex);

	void MoveTo(int row_y, int column_x);

	void Turn();
	
	void Down();
	
	void Left();
	
	void Right();

	MyTetris& operator= (const MyTetris& temp);

	Tetris& GetTetris();

private:
	int m_TypeIndex;//7种俄罗斯方块中的哪一种
	int m_TurnIndex;//4种朝向中的哪一种

	int m_Row_y;//以4*4范围为准的左上角当前坐标,转向时也做为对齐的基准点
	int m_Column_x;

	Tetris m_Tetris;//当前位置
};

#endif


Tetris.cpp:

#include "Tetris.h"

TetrisGrid& TetrisGrid::operator= (const TetrisGrid& temp)
{
	row_y = temp.row_y;
	column_x = temp.column_x;
	return *this;
}

Tetris& Tetris::operator= (const Tetris& temp)
{
	for (int i = 0; i < MAX_GRID; i++)
	{
		grids[i] = temp.grids[i];
	}
	return *this;
}

void Tetris::MoveTo(int row_y, int column_x)
{
	for (int i = 0; i < MAX_GRID; i++)
	{
		grids[i].row_y = grids[i].row_y + row_y;
		grids[i].column_x = grids[i].column_x + column_x;
	}
}

void Tetris::Left()
{
	for (int i = 0; i < MAX_GRID; i++)
	{
		grids[i].column_x--;
	}
}

void Tetris::Right()
{
	for (int i = 0; i < MAX_GRID; i++)
	{
		grids[i].column_x++;
	}
}

void Tetris::Down()
{
	for (int i = 0; i < MAX_GRID; i++)
	{
		grids[i].row_y++;
	}
}

void MyTetris::Init(int row_y, int column_x, int typeIndex, int turnIndex)
{
	if (typeIndex < MAX_TETRIS_COUNT && turnIndex < MAX_TETRIS_FORWARD)
	{
		m_TypeIndex = typeIndex;
		m_TurnIndex = turnIndex;
	}
	else
	{
		m_TypeIndex = 0;
		m_TurnIndex = 0;
	}
	m_Tetris = g_tetris[m_TypeIndex][m_TurnIndex];

	m_Row_y = row_y;
	m_Column_x = column_x;
	m_Tetris.MoveTo(m_Row_y, m_Column_x);
}

void MyTetris::MoveTo(int row_y, int column_x)
{
	m_Tetris = g_tetris[m_TypeIndex][m_TurnIndex];

	m_Row_y = row_y;
	m_Column_x = column_x;
	m_Tetris.MoveTo(m_Row_y, m_Column_x);
}

void MyTetris::Turn()
{
	m_TurnIndex++;
	if (MAX_TETRIS_FORWARD == m_TurnIndex)
	{
		m_TurnIndex = 0;
	}
	m_Tetris = g_tetris[m_TypeIndex][m_TurnIndex];

	m_Tetris.MoveTo(m_Row_y, m_Column_x);
}

void MyTetris::Down()
{
	m_Row_y++;
	m_Tetris.Down();
}

void MyTetris::Left()
{
	m_Column_x--;
	m_Tetris.Left();
}

void MyTetris::Right()
{
	m_Column_x++;
	m_Tetris.Right();
}

MyTetris& MyTetris::operator= (const MyTetris& temp)
{
	m_TypeIndex = temp.m_TypeIndex;
	m_TurnIndex = temp.m_TurnIndex;
	m_Tetris = g_tetris[m_TypeIndex][m_TurnIndex];

	m_Row_y = temp.m_Row_y;
	m_Column_x = temp.m_Column_x;
	m_Tetris.MoveTo(m_Row_y, m_Column_x);

	return *this;
}

Tetris& MyTetris::GetTetris()
{
	return m_Tetris;
}


TetrisScene.h:

#ifndef _Tetris_Scene_
#define _Tetris_Scene_

#include "TArray.h"

#include "Tetris.h"

//用于显示当前方块的区域在Map中的初始位置,行数,列数
const int TETRIS_CURR_INIT_ROW_Y = 1;
const int TETRIS_CURR_INIT_COLUMN_X = 7;
const int TETRIS_CURR_MAX_ROW = 4;
const int TETRIS_CURR_MAX_COLUMN = 4;

//游戏区域在Map中的位置,行数,列数
const int TETRIS_GAME_ROW_Y = 5;
const int TETRIS_GAME_COLUMN_X = 1;
const int TETRIS_GAME_MAX_ROW = 20;
const int TETRIS_GAME_MAX_COLUMN = 16;

//用于显示下一个方块的区域在Map中的位置,行数,列数
const int TETRIS_NEXT_ROW_Y = 6;
//1列是左边框,1列是游戏区域和下一个方块的区域的间隔
const int TETRIS_NEXT_COLUMN_X = 1 + TETRIS_GAME_MAX_COLUMN + 1;
const int TETRIS_NEXT_MAX_ROW = 4;
const int TETRIS_NEXT_MAX_COLUMN = 4;

//整个Map的行数,列数
//多2行是上下边框
//多3列是左右边框,以及游戏区域和下一个方块的区域的间隔
const int TETRIS_SCENE_MAX_ROW = 1 + TETRIS_CURR_MAX_ROW + TETRIS_GAME_MAX_ROW + 1;
const int TETRIS_SCENE_MAX_COLUMN = 1 + TETRIS_GAME_MAX_COLUMN + 1 + TETRIS_NEXT_MAX_COLUMN + 1;

//空白场景格
const WCHAR WS_TETRIS_SCENE_GRID = L' ';
//已经落下不能再动的方块
const WCHAR WS_TETRIS_OLD = L'□';
//新生成正在往下移动的方块,以及下一个方块
const WCHAR WS_TETRIS_NEW = L'■';
//场景边框格
const WCHAR WS_TETRIS_SCENE_FRAME = L'◆';


class TetrisScene 
{
public:
	TetrisScene();

	void InitMap();

	void RandNextTetris();

	void RandCurrTetris();

	bool CanMove(int forward);

	bool IsShow(MyTetris& mytetris);

	bool IsDeath();

	bool CanRemove(int row);

	void Remove();
	void Remove(int row);

	TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW>& GetMap1();
	TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW>& GetMap2();

	bool ProcessLogic();
	bool ProcessLogic(int forward);

	void SetSceneGrid(MyTetris& mytetris);
	void SetTetrisOld(MyTetris& mytetris);
	void SetTetrisNew(MyTetris& mytetris);


private:
	TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW> m_map1;
	TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW> m_map2;//双缓冲,用于对照改变了的位置,防止闪屏
	
	MyTetris m_CurrTetris;
	MyTetris m_TestTetris;//CurrTetris的下一步位置,用于推断下一个位置能否移动
	MyTetris m_NextTetris;
};

#endif


TetrisScene.cpp:

#include "TetrisScene.h"

#include <time.h>

TetrisScene::TetrisScene()
{
	srand((unsigned)time(NULL));

	InitMap();
	RandNextTetris();
	RandCurrTetris();
	RandNextTetris();
}

void TetrisScene::InitMap()
{
	for (int y = 0; y < TETRIS_SCENE_MAX_ROW; y++)
	{
		for (int x = 0; x< TETRIS_SCENE_MAX_COLUMN; x++)
		{
			m_map1[y][x] = WS_TETRIS_SCENE_FRAME;
		}
	}

	for (int y = TETRIS_CURR_INIT_ROW_Y; y < TETRIS_CURR_INIT_ROW_Y + TETRIS_CURR_MAX_ROW; y++)
	{
		for (int x = TETRIS_CURR_INIT_COLUMN_X; x< TETRIS_CURR_INIT_COLUMN_X + TETRIS_CURR_MAX_COLUMN; x++)
		{
			m_map1[y][x] = WS_TETRIS_SCENE_GRID;
		}
	}

	for (int y = TETRIS_GAME_ROW_Y; y < TETRIS_GAME_ROW_Y + TETRIS_GAME_MAX_ROW; y++)
	{
		for (int x = TETRIS_GAME_COLUMN_X; x< TETRIS_GAME_COLUMN_X + TETRIS_GAME_MAX_COLUMN; x++)
		{
			m_map1[y][x] = WS_TETRIS_SCENE_GRID;
		}
	}

	for (int y = TETRIS_NEXT_ROW_Y; y < TETRIS_NEXT_ROW_Y + TETRIS_NEXT_MAX_ROW; y++)
	{
		for (int x = TETRIS_NEXT_COLUMN_X; x< TETRIS_NEXT_COLUMN_X + TETRIS_NEXT_MAX_COLUMN; x++)
		{
			m_map1[y][x] = WS_TETRIS_SCENE_GRID;
		}
	}
}

void TetrisScene::RandNextTetris()
{
	int typeIndex = rand() % MAX_TETRIS_COUNT;
	int turnIndex = rand() % MAX_TETRIS_FORWARD;
	m_NextTetris.Init(TETRIS_NEXT_ROW_Y, TETRIS_NEXT_COLUMN_X, typeIndex, turnIndex);

	SetTetrisNew(m_NextTetris);
}

void TetrisScene::RandCurrTetris()
{
	m_CurrTetris = m_NextTetris;
	SetSceneGrid(m_NextTetris);

	m_CurrTetris.MoveTo(TETRIS_CURR_INIT_ROW_Y, TETRIS_CURR_INIT_COLUMN_X);
	SetTetrisNew(m_CurrTetris);
}

bool TetrisScene::CanMove(int forward)
{
	m_TestTetris = m_CurrTetris;
	switch (forward)
	{
	case TETRIS_UP:
		m_TestTetris.Turn();
		break;
	case TETRIS_DOWN:
		m_TestTetris.Down();
		break;
	case TETRIS_LEFT:
		m_TestTetris.Left();
		break;
	case TETRIS_RIGHT:
		m_TestTetris.Right();
		break;
	default:
		m_TestTetris.Down();
		break;
	}

	Tetris& tetris = m_TestTetris.GetTetris();
	for (int i = 0; i < MAX_GRID; i++)
	{
		if (m_map1[tetris.grids[i].row_y][tetris.grids[i].column_x] == WS_TETRIS_SCENE_FRAME)
		{
			return false;
		}
		if (m_map1[tetris.grids[i].row_y][tetris.grids[i].column_x] == WS_TETRIS_OLD)
		{
			return false;
		}
	}
	return true;
}

bool TetrisScene::IsShow(MyTetris& mytetris)
{
	Tetris& tetris = mytetris.GetTetris();
	for (int i = 0; i < MAX_GRID; i++)
	{
		if (tetris.grids[i].row_y < TETRIS_GAME_ROW_Y)
		{
			return false;
		}
	}

	return true;
}

bool TetrisScene::IsDeath()
{
	for (int i = TETRIS_GAME_COLUMN_X; i < TETRIS_GAME_COLUMN_X + TETRIS_GAME_MAX_COLUMN; i++)
	{
		if (m_map1[TETRIS_GAME_ROW_Y][i] == WS_TETRIS_OLD)
		{
			return true;
		}
	}
	
	return false;
}

bool TetrisScene::CanRemove(int row)
{
	for (int i = TETRIS_GAME_COLUMN_X; i < TETRIS_GAME_COLUMN_X + TETRIS_GAME_MAX_COLUMN; i++)
	{
		if (m_map1[row][i] != WS_TETRIS_OLD)
		{
			return false;
		}
	}
	return true;
}

void TetrisScene::Remove()
{
    //TETRIS_SCENE_MAX_ROW - 1 是最后一行的下标
    //再-1,是去掉最后一行的边框
    for (int i = TETRIS_SCENE_MAX_ROW - 1 - 1; i > TETRIS_GAME_ROW_Y;)
    {
        if (CanRemove(i))
        {
            Remove(i);
        }
        else
        {
            i--;
        }
    }
}

void TetrisScene::Remove(int row)
{
	for (int i = row; i > TETRIS_GAME_ROW_Y; i--)
	{
		for (int j = TETRIS_GAME_COLUMN_X; j < TETRIS_GAME_COLUMN_X + TETRIS_GAME_MAX_COLUMN; j++)
		{
			m_map1[i][j] = m_map1[i - 1][j];
		}
	}
	for (int j = TETRIS_GAME_COLUMN_X; j < TETRIS_GAME_COLUMN_X + TETRIS_GAME_MAX_COLUMN; j++)
	{
		m_map1[TETRIS_GAME_ROW_Y][j] = WS_TETRIS_SCENE_GRID;
	}
}

TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW>& TetrisScene::GetMap1()
{
	return m_map1;
}

TArray1<TArray1<WCHAR, TETRIS_SCENE_MAX_COLUMN>, TETRIS_SCENE_MAX_ROW>& TetrisScene::GetMap2()
{
	return m_map2;
}

bool TetrisScene::ProcessLogic()
{
	return ProcessLogic(TETRIS_DOWN);
};

bool TetrisScene::ProcessLogic(int forward)
{
	if (IsShow(m_CurrTetris))
	{
		switch (forward)
		{
		case TETRIS_UP:
			{
				if (CanMove(TETRIS_UP))
				{
					SetSceneGrid(m_CurrTetris);
					m_CurrTetris.Turn();
					SetTetrisNew(m_CurrTetris);
				}
			}
			break;
		case TETRIS_DOWN:
			{
				if (CanMove(TETRIS_DOWN))
				{
					SetSceneGrid(m_CurrTetris);
					m_CurrTetris.Down();
					SetTetrisNew(m_CurrTetris);
				}
				else
				{
					SetTetrisOld(m_CurrTetris);

					if (!IsDeath())
					{
						Remove();

						RandCurrTetris();
						RandNextTetris();
					}
					else
					{
						return false;
					}
				}
			}
			break;
		case TETRIS_LEFT:
			{
				if (CanMove(TETRIS_LEFT))
				{
					SetSceneGrid(m_CurrTetris);
					m_CurrTetris.Left();
					SetTetrisNew(m_CurrTetris);
				}
			}
			break;
		case TETRIS_RIGHT:
			{
				if (CanMove(TETRIS_RIGHT))
				{
					SetSceneGrid(m_CurrTetris);
					m_CurrTetris.Right();
					SetTetrisNew(m_CurrTetris);
				}
			}
			break;
		}
	}
	else
	{
		if (CanMove(TETRIS_DOWN))
		{
			SetSceneGrid(m_CurrTetris);
			m_CurrTetris.Down();
			SetTetrisNew(m_CurrTetris);
		}
		else
		{
			SetTetrisOld(m_CurrTetris);

			if (!IsDeath())
			{
				Remove();
				
				RandCurrTetris();
				RandNextTetris();
			}
			else
			{
				return false;
			}
		}
	}

	return true;
}

void TetrisScene::SetSceneGrid(MyTetris& mytetris)
{
	Tetris& tetris = mytetris.GetTetris();
	for (int i = 0; i < MAX_GRID; i++)
	{
		m_map1[tetris.grids[i].row_y][tetris.grids[i].column_x] = WS_TETRIS_SCENE_GRID;
	}
}
void TetrisScene::SetTetrisOld(MyTetris& mytetris)
{
	Tetris& tetris = mytetris.GetTetris();
	for (int i = 0; i < MAX_GRID; i++)
	{
		m_map1[tetris.grids[i].row_y][tetris.grids[i].column_x] = WS_TETRIS_OLD;
	}
}
void TetrisScene::SetTetrisNew(MyTetris& mytetris)
{
	Tetris& tetris = mytetris.GetTetris();
	for (int i = 0; i < MAX_GRID; i++)
	{
		m_map1[tetris.grids[i].row_y][tetris.grids[i].column_x] = WS_TETRIS_NEW;
	}
}


main.cpp:(事实上主函数和之前的贪吃蛇游戏全然一致,仅仅是改了几个坐标、和方向的常量)

#include <stdio.h>
#include <windows.h>
#include <conio.h>

#include "TArray.h"
#include "TRandPool.h"

#include "Tetris.h"
#include "TetrisScene.h"

#include "CPerformance.h"

const unsigned char KEY_FORWARD = 224;
const unsigned char KEY_UP = 72;
const unsigned char KEY_DOWN = 80;
const unsigned char KEY_LEFT = 75;
const unsigned char KEY_RIGHT = 77;
const unsigned char KEY_ESC = 27;

TetrisScene g_TetrisScene;

const int SCENE_ROW_Y = -5;
const int SCENE_COLUMN_X = 20;
const int SCENE_MAX_ROW = TETRIS_SCENE_MAX_ROW;
const int SCENE_MAX_COLUMN = TETRIS_SCENE_MAX_COLUMN;

const int LOGIC_UP = TETRIS_UP;
const int LOGIC_DOWN = TETRIS_DOWN;
const int LOGIC_LEFT = TETRIS_LEFT;
const int LOGIC_RIGHT = TETRIS_RIGHT;

void DrawMap(HANDLE hOut)
{
	COORD pos = {0, 0};
	for (int y = 0; y < SCENE_MAX_ROW; y++)
	{
		for (int x = 0; x< SCENE_MAX_COLUMN; x++)
		{
			if (g_TetrisScene.GetMap1()[y][x] != g_TetrisScene.GetMap2()[y][x])
			{
				pos.X = x * 2 + SCENE_COLUMN_X;
				pos.Y= y + SCENE_ROW_Y;
				FillConsoleOutputCharacter(hOut, g_TetrisScene.GetMap1()[y][x], 2, pos, NULL);

				g_TetrisScene.GetMap2()[y][x] = g_TetrisScene.GetMap1()[y][x];
			}
		}
	}
}

int main()
{
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

	CONSOLE_SCREEN_BUFFER_INFO bInfo;
	GetConsoleScreenBufferInfo(hOut, &bInfo );

	DrawMap(hOut);

	CPerformance perf;//计时器
	perf.Start();
	float times = 0.0f;

	bool bRes = true;//游戏是否结束的标志,true为进行中,false为结束

	while (true)
	{
		if(_kbhit())
		{
			int ch = _getch();
			if (KEY_ESC == ch)
			{
				break;
			}
			else if (KEY_FORWARD == ch)
			{
				if (bRes)
				{
					ch = _getch();
					switch (ch)
					{
					case KEY_UP:
						bRes = g_TetrisScene.ProcessLogic(LOGIC_UP);
						break;
					case KEY_DOWN:
						bRes = g_TetrisScene.ProcessLogic(LOGIC_DOWN);
						break;
					case KEY_LEFT:
						bRes = g_TetrisScene.ProcessLogic(LOGIC_LEFT);
						break;
					case KEY_RIGHT:
						bRes = g_TetrisScene.ProcessLogic(LOGIC_RIGHT);
						break;
					}
					DrawMap(hOut);
				}
			}
		}

		times = perf.End();
		if (times > 300)
		{
			perf.Start();

			if (bRes)
			{
				bRes = g_TetrisScene.ProcessLogic();
				DrawMap(hOut);
			}
		}

		if (!bRes)
		{
			WCHAR info[100] = {0};
			wcscpy_s(info, 100, L"game over, press ESC key to exit.");
			for (size_t i = 0; i < wcslen(info); i++)
			{
				COORD pos = {i, SCENE_MAX_ROW + SCENE_ROW_Y + 2};
				FillConsoleOutputCharacter(hOut, info[i], 1, pos, NULL);
			}
		}
		
		Sleep(1);
	}

	CloseHandle(hOut);

	return 0;
}


原文地址:https://www.cnblogs.com/bhlsheji/p/4008040.html