首页 > 代码库 > 游戏开发(二)——控制台 俄罗斯方块
游戏开发(二)——控制台 俄罗斯方块
俄罗斯方块游戏设计中主要需要注意的几点:
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; }