C++ 萌新试手—俄罗斯方块
第二个编程作品——俄罗斯方块
借鉴了很多大神的程序算法,最终完成了这个
用一个4*4数组表示每个类型方块
并用4位十六进制 每一位表示每个数组的一行
然后运用一个二维数组旋转的算法 运用到四位十六进制数上形成方块的旋转,详见BlockRotate()函数
学生一枚 拿着俄罗斯方块练练手
game_data.h
#pragma once
//界面相关参数
//游戏板方块数
#define x_GAMEBOARD_NUM 15
#define y_GAMEBOARD_NUM 25
//方块大小
#define BLOCK_SQUARE 20
//游戏板长宽
#define GAMEBOARD_WIDTH x_GAMEBOARD_NUM*20
#define GAMEBOARD_HEIGHT y_GAMEBOARD_NUM*20
//帮助栏长宽
#define HELPBOARD_WIDTH 150
#define HELPBOARD_HEIGHT GAMEBOARD_HEIGHT
//界面窗口大小
#define WINDOWS_WIDTH GAMEBOARD_WIDTH + HELPBOARD_WIDTH + 60
#define WINDOWS_HEIGHT GAMEBOARD_HEIGHT + 40
//控制按键
#define KB_UP 'w'
#define KB_DOWN 's'
#define KB_LEFT 'a'
#define KB_RIGHT 'd'
#define KB_PAUSE ' '
typedef int BlockTypeIdx;
int game_score = 0;//游戏分数
int BlockShapeType = 0;//方块类型数
bool Game_Board[y_GAMEBOARD_NUM][x_GAMEBOARD_NUM] = { false };//游戏板 false为无方块 true为有
char Key_Hit;//键盘输入
bool HighSpeed = false;
#define MaxTypeNum 10 //最大类型数存储数量
//方块形状表示
typedef struct BlockShape
{
unsigned short BlockShapeBits;
}BlockArray;
BlockArray BlockShapeArray[MaxTypeNum];
//4*4方块左上角的坐标
typedef struct LOCATE
{
int left;
int top;
}Block_Location;
//方块位置初始化
Block_Location InitBlockLocation = { 20 + 7 * BLOCK_SQUARE,-40 };
game_init.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<easyx.h>
#include<graphics.h>
#include<iostream>
#include"game_data.h"
//帮助栏显示
void Init_Help()
{
setbkcolor(BLACK);
settextstyle(20, 0, _T("宋体"));
outtextxy(WINDOWS_WIDTH - 40 - HELPBOARD_WIDTH / 2, 50, _T("预览"));
outtextxy(WINDOWS_WIDTH - 40 - HELPBOARD_WIDTH / 2, 200, _T("分数"));
outtextxy(WINDOWS_WIDTH - 60 - HELPBOARD_WIDTH / 2, 300, _T("操作说明"));
outtextxy(WINDOWS_WIDTH - 60 - HELPBOARD_WIDTH / 2, 340, _T("wsad方向"));
outtextxy(WINDOWS_WIDTH - 60 - HELPBOARD_WIDTH / 2,380, _T("空格暂停"));
TCHAR s[5];
_stprintf(s, _T("%d"), game_score);
settextcolor(LIGHTBLUE);
outtextxy(WINDOWS_WIDTH - 30 - HELPBOARD_WIDTH / 2, 240, s);
}
//方块形状存入
/*
0100 0110 0110 0100 0010 0000 0000
0100 0100 0010 0110 0110 0110 0010
0100 0100 0010 0010 0100 0110 0111
0100 0000 0000 0000 0000 0000 0000
*/
void BlockShape_Recorder()
{
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x4444;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x6440;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x6220;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x4620;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x2640;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x0660;
BlockShapeArray[BlockShapeType++].BlockShapeBits = 0x0270;
}
//界面加载 边框绘制
void Init_Game()
{
initgraph(WINDOWS_WIDTH, WINDOWS_HEIGHT);
setbkcolor(LIGHTGRAY);;
cleardevice();
setfillcolor(BLACK);
solidrectangle(20, 20, GAMEBOARD_WIDTH + 20, GAMEBOARD_HEIGHT + 20);
solidrectangle(WINDOWS_WIDTH-20-HELPBOARD_WIDTH,20,WINDOWS_WIDTH-20,WINDOWS_HEIGHT-20);
Init_Help();
BlockShape_Recorder();
}
//绘制方块
//true为显示方块 false为清除原来方块
void Block_Display (BlockShape BlockType, Block_Location& BlockLocation, bool Display)
{
unsigned short mask;
int xRock = BlockLocation.left;
int yRock = BlockLocation.top;
if (Display)
{
setfillcolor(RED);
setlinecolor(WHITE);
}
else
{
setfillcolor(BLACK);
setlinecolor(BLACK);
}
if (BlockLocation.top >= 20)
{
mask = 1 << 15;
for (int i = 1; i <= 16; i++)
{
if (mask & BlockType.BlockShapeBits)
fillrectangle(xRock, yRock, xRock + BLOCK_SQUARE, yRock + BLOCK_SQUARE);
i % 4 == 0 ? (xRock = BlockLocation.left, yRock += BLOCK_SQUARE) : xRock += BLOCK_SQUARE;
mask >>= 1;
}
}
else//如果top为0 从第2行开始显示 // -20 3 //-40 4
{
int Row = 2 - BlockLocation.top / BLOCK_SQUARE;//第2行移动11位 //3 7 //4 3
mask = 1 << (19 - 4 * Row);
{
for (int i = 1; i <= 4 * (5 - Row); i++)//从第4行 有1行需要打印 //3 2 //2 3
{
if (mask & BlockType.BlockShapeBits)
fillrectangle(xRock, yRock + BLOCK_SQUARE * (Row - 1), xRock + BLOCK_SQUARE, yRock + BLOCK_SQUARE * (Row - 1) + BLOCK_SQUARE);
i % 4 == 0 ? (xRock = BlockLocation.left, yRock += BLOCK_SQUARE) : xRock += BLOCK_SQUARE;
mask >>= 1;
}
}
}
}
game_play.h
#pragma once
#include"game_init.h"
#include<conio.h>
#include<ctime>
bool JudgeMove(BlockShape &BlockShape, Block_Location &BlockLocation,char Direction);
void UserKeyBoardControl(BlockShape &TypeIdx, Block_Location &BlockLocation);
BlockShape BlockRotate(BlockShape &BlockType);
void JudgeLineFull();
void DeleteLine(int Idx);
void BoardFlagSet(BlockShape &BlockShape, Block_Location &BlockLocation);
void UpdateScore();
bool IsGameOver();
//游戏运行
void PlayGame()
{
BlockShape TmpBlock;
int CurrentBlockIdx, NextBlockIdx;
srand((unsigned)time(NULL));
CurrentBlockIdx = rand() % BlockShapeType;
NextBlockIdx = rand() % BlockShapeType;
Block_Location CurrentLocation;
Block_Location VisitLocation = { WINDOWS_WIDTH - 60 - HELPBOARD_WIDTH / 2, 80 };
CurrentLocation.left = InitBlockLocation.left;
CurrentLocation.top = InitBlockLocation.top;
Block_Display(BlockShapeArray[NextBlockIdx], VisitLocation, true);
Block_Display(BlockShapeArray[CurrentBlockIdx], CurrentLocation, true);
TmpBlock = BlockShapeArray[CurrentBlockIdx];
while (true)
{
Block_Display(TmpBlock, CurrentLocation, true);
HighSpeed = false;
for (int i = 0; i < 300; i++)
{
UserKeyBoardControl(TmpBlock, CurrentLocation);
if (HighSpeed)
break;
Sleep(1);
}
if (!JudgeMove(TmpBlock, CurrentLocation, KB_DOWN))
{
BoardFlagSet(TmpBlock, CurrentLocation);
JudgeLineFull();
if (IsGameOver())
{
MessageBox(NULL, _T("游戏结束"), _T("GAME OVER"), MB_OK);
exit(0);
}
Block_Display(BlockShapeArray[NextBlockIdx], VisitLocation, false);
CurrentBlockIdx = NextBlockIdx;
NextBlockIdx = rand() % BlockShapeType;
CurrentLocation.left = InitBlockLocation.left;
CurrentLocation.top = InitBlockLocation.top;
Block_Display(BlockShapeArray[NextBlockIdx], VisitLocation, true);
Block_Display(BlockShapeArray[CurrentBlockIdx], InitBlockLocation, true);
TmpBlock = BlockShapeArray[CurrentBlockIdx];
}
Block_Display(TmpBlock, CurrentLocation, false);
CurrentLocation.top += BLOCK_SQUARE;
}
}
//判定游戏是否结束
bool IsGameOver()
{
for (int i = 0; i < x_GAMEBOARD_NUM; i++)
if (Game_Board[0][i])return true;
return false;
}
//分数更新
void UpdateScore()
{
TCHAR s[10];
_stprintf(s, _T("%d"), game_score);
settextcolor(LIGHTBLUE);
outtextxy(WINDOWS_WIDTH - 30 - HELPBOARD_WIDTH / 2, 240, s);
}
//删除整行,并使上边的下移
void DeleteLine(int Row)
{
//将满行这一行的以上全部全都清空
setfillcolor(BLACK);
solidrectangle(20, 20, 20 + BLOCK_SQUARE*x_GAMEBOARD_NUM, 20 + BLOCK_SQUARE * Row + BLOCK_SQUARE);
//将原来上面的行下移
int count = 0;
while (count != x_GAMEBOARD_NUM)
{
count = 0;
for (int i = 0; i < x_GAMEBOARD_NUM; i++)
{
Game_Board[Row][i] = Game_Board[Row - 1][i];
if (Game_Board[Row][i])
{
setfillcolor(RED);
setlinecolor(WHITE);
fillrectangle(20 + i * 20, 20 + Row * 20, 20 + i * 20 + BLOCK_SQUARE, 20 + Row * 20 + BLOCK_SQUARE);
}
else
count++;
}
Row--;
}
}
//判断是否存在整行被占用
void JudgeLineFull()
{
bool LineFull = true;
int Idx = y_GAMEBOARD_NUM - 1;
int count = 0;
while (count != x_GAMEBOARD_NUM)
{
LineFull = true;
count = 0;
for (int i = 0; i < x_GAMEBOARD_NUM; i++)
{
if (!(Game_Board[Idx][i]))
{
LineFull = false;
count++;
}
}
if (LineFull)
{
DeleteLine(Idx);
game_score += 10;
UpdateScore();
Idx++;
}
Idx--;
}
}
//在有方块的位置设置为1
void BoardFlagSet(BlockShape &BlockShape, Block_Location &BlockLocation)
{
int xRock = BlockLocation.left;
int yRock = BlockLocation.top;
unsigned short mask = 1 << 15;
for (int i = 1; i <= 16; i++)
{
if (BlockShape.BlockShapeBits & mask)
{
Game_Board[(yRock - 20) / BLOCK_SQUARE][(xRock - 20) / BLOCK_SQUARE] = true;
}
i % 4 == 0 ? (xRock = BlockLocation.left, yRock += BLOCK_SQUARE) : xRock += BLOCK_SQUARE;
mask >>= 1;
}
}
//方块旋转
BlockShape BlockRotate(BlockShape &BlockType)
{
unsigned short mask;
unsigned short NextBlock;
for (int i = 3; i >= 0; i--)
{
for (int j = i; j < 16; j += 4)
{
mask = ((1 << j)&BlockType.BlockShapeBits) >> j;
NextBlock = (NextBlock << 1) | mask;
}
}
BlockShape Type = {NextBlock};
return Type;
}
//判定能否移动
bool JudgeMove(BlockShape &BlockType, Block_Location &CurrentLocation, char Direction)
{
unsigned short mask = 1 << 15;
int xRock = CurrentLocation.left;
int yRock = CurrentLocation.top;
for (int i = 1; i <= 16; i++)
{
if (BlockType.BlockShapeBits & mask)
{
switch (Direction)
{
case KB_UP:
if (Game_Board[(yRock - 20) / BLOCK_SQUARE][(xRock - 20) / BLOCK_SQUARE] || xRock <= 0 || xRock >= BLOCK_SQUARE*x_GAMEBOARD_NUM+20 || yRock >= 20 * y_GAMEBOARD_NUM)
return false;
break;
case KB_DOWN:
if (Game_Board[(yRock - 20) / BLOCK_SQUARE+1][(xRock - 20) / BLOCK_SQUARE] || yRock ==20 * y_GAMEBOARD_NUM)
return false;
break;
case KB_LEFT:
if (Game_Board[(yRock - 20) / BLOCK_SQUARE][(xRock - 20) / BLOCK_SQUARE - 1] || xRock == 20)
return false;
break;
case KB_RIGHT:
if (Game_Board[(yRock - 20) / BLOCK_SQUARE][(xRock - 20) / BLOCK_SQUARE + 1] || xRock == BLOCK_SQUARE*x_GAMEBOARD_NUM)
return false;
break;
default:
break;
}
}
i % 4 == 0 ? (xRock = CurrentLocation.left, yRock += BLOCK_SQUARE) : xRock += BLOCK_SQUARE;
mask >>= 1;
}
return true;
}
//按键操控
void UserKeyBoardControl(BlockShape& BlockType, Block_Location &BlockLocation)
{
if (_kbhit())
{
Key_Hit = _getch();
switch (Key_Hit)
{
case KB_UP:
if (JudgeMove(BlockRotate(BlockType), BlockLocation, Key_Hit))
{
Block_Display(BlockType, BlockLocation, false);
BlockType = BlockRotate(BlockType);
Block_Display(BlockType, BlockLocation, true);
}
break;
case KB_DOWN:
if (JudgeMove(BlockType, BlockLocation, Key_Hit))
{
HighSpeed = true;
}
break;
case KB_LEFT:
if (JudgeMove(BlockType, BlockLocation, Key_Hit))
{
Block_Display(BlockType, BlockLocation, false);
BlockLocation.left -= BLOCK_SQUARE;
Block_Display(BlockType, BlockLocation, true);
}
break;
case KB_RIGHT:
if (JudgeMove(BlockType, BlockLocation, Key_Hit))
{
Block_Display(BlockType, BlockLocation, false);
BlockLocation.left += BLOCK_SQUARE;
Block_Display(BlockType, BlockLocation, true);
}
break;
case KB_PAUSE:
while (true)
{
Key_Hit = _getch();
if (Key_Hit == ' ')
break;
}
default:
break;
}
}
}
main.cpp
#include"game_init.h"
#include"game_play.h"
int main()
{
Init_Game();
PlayGame();
closegraph();
}