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

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

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;
}
时间: 2024-12-23 08:16:42

游戏开发(二)——控制台 俄罗斯方块的相关文章

unity3D游戏开发二之unity编辑器一

转:http://blog.csdn.net/kuloveyouwei/article/details/22726611 通过官网我们可以下载最新的unity版本,这里我们是基于Mac OS系统的,安装步骤很简单,这里就略过了.安装完成后,打开unity, 主界面如下: 下面我们来看看它的界面布局,Unity界面主要包括菜单栏.工具栏以及相关的视图等内容. 1.Scene视图 2.Game视图 3.Hierarchy视图(显示的都是游戏对象) 4.Project视图(可以看到各个文件) 5.In

游戏开发(一)——控制台 贪吃蛇

贪吃蛇游戏设计中主要需要注意的几点: 1:坐标的定义:定义为左上角为(0,0),向右为x正方向,向下为y正方向 2:蛇的设计, 蛇身:m_body,这里用的是链表(是之前写好的双链表),一个节点就是蛇身的一节 每节蛇身的属性包括x,y坐标:column_x,row_y,x表示在地图上的第几列,y表示在地图上的第几行 蛇有一个属性叫朝向,也就是当前在往上.下.左.右的哪个方向移动:m_forward 蛇的动作有:Turn,转向.转向有个判断,就是不能向相反的方向转,比如本来向上运动,按向下键,是无

游戏音频技术备忘 (二) 关于游戏开发

千里之行始于足下,一款游戏如何从无到有?诗人构思许久后动笔写作,音乐家在乐谱与乐器间来回修改,画家调节颜料比例涂抹在画布上,文学音乐绘画都要经历如上所述大致相同的创作流程.游戏不同与往,游戏杂糅了几乎所有艺术形式的特征,一方面我们还要进行传统的 文学音乐绘画创作,另一方面我们要额外考虑如何把这些各自分隔的内容糅合在一起,并且提供一套交互系统用以产生变化,这时候我们就需要一个工程师来实现这一目标. 作为电子游戏,建构虚拟世界的根基是各类的计算机硬件,我们需要一块屏幕用以显示图像,一只喇叭用以播放声

[游戏开发-学习笔记]菜鸟慢慢飞(二)-迷宫

简介:练手Demo,<走出迷宫>,文章主要说说如何创建迷宫. 学习Unity3D有一段时间了,自己想了一个项目来练手手.然后就有了这篇. 一.固定的格数,开局后随机生成. 说明:这个迷宫10*10,开始后随机生成,四周留下一个空做出口. 先说如何实现: 主要准备了三个Prefab:横墙,竖墙,柱子,墙高度是10,宽度是10,厚度是1,柱子高度是10.宽度和厚度都是10. 手动按照10*10排列(参考下图) 脚本 #region //初始化游戏 #endregion using System.C

cocos2dx游戏开发——微信打飞机学习笔记(二)——游戏框架

一.游戏的基本框架: WelcomeScene    ——>    GameScene   ——>   GameOverScene ||                                       ||                                    || ∨                                      ∨                                   ∨ WelcomeLayer            

应用程序开发之模仿史上最牛游戏(二)

声明:转载请注明http://www.cnblogs.com/letougaozao/p/3708887.html 新建关卡控制器 自定义UIScrollView 增加UIPageView 每个关卡的View 整体效果展示 一.新建关卡控制器 1??拖线 -修改控制器class -修改控制器的View的class(方便设置背景) -装资源文件 -返回按钮 二.自定义UIScrollView 1??在初始化方法里面做一些事情 -添加四张背景图片 1.创建四个FullView 2.将这四个view加

【cocos2dx游戏开发】之 坐标系(二) convertToNodeSpace和convertToWorldSpace

游戏中经常会用到一些变换: 游戏中武器和角色在一个layer上,为了效率,会考虑将bullet, effect和 PhysicsParticle分别放到不用的层上,对应的层上使用batchnode来提高效率 武器和PhysicsParticleLauncher(粒子发射器)绑定,发射的时候,会向PhysicsParticleLayer的武器相同的位置上生成一个物理粒子特效 会经常用到convertToNodeSpace和convertToWorldSpace转换坐标 我们加三个sprite,r0

java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码

原文:java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码 源代码下载地址:http://www.zuidaima.com/share/1550463495146496.htm java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码, 很久以前找到的一个Swing实现的俄罗斯方块,短线精悍,算法值得一看 经验证代码可用,确实短小精悍,值得下载. package com.zuidaima.swing.game; import java.awt.*; i

游戏开发(三)——WIN32 黑白棋(二)——AI

今天是第二部分:玩家和AI 玩家主要是实现悔棋的功能 AI主要是搜索.最大最小算法,枝剪算法 1.每一步落子的步骤,为了可以悔棋 typedef struct ReversiStep {     ReversiBitBoard m_LastMap;     ReversiStep& operator= (const ReversiStep& temp)     {         m_LastMap = temp.m_LastMap;         return *this;     }