欢迎大家点评!
一、功能简介
1、键盘控制:上下左右键控制90度旋转积木,左右移动积木块,让积木块加快落下。
2、分数结算: 游戏区域中某一行格子全部由积木块填满,则该行会消失并成为玩家的得分,一次删除的行数越多,得分越多。
3、等级制度:初始等级为1级,分数每增加到10分后等级提升,游戏速度加快,等级提升后游戏区域清空。
4、游戏暂停:空格键盘暂停游戏,弹出消息框提示,按确认键继续。
5、游戏结束: 当积木块堆到区域最上方,弹出消息框提示游戏结束。
#本程序在Windows10系统下正常运行,在VS 2013英文版编译通过
二、运行示例
1、默认界面
2、升级功能
3、暂停功能
4、游戏结束
类名 | 成员类别 | 类型 | 成员名 | 描述 |
block |
属性 | int | type | 积木的类型 |
int | dir | 积木的方向 | ||
POINT [] | pt | 积木的位置 | ||
bool | overlapflag | 重叠标志位 | ||
bool [][16] | pregion | 指向游戏区域 | ||
方法 | bool | down | 下落函数 | |
void | change | 旋转函数 | ||
void | left | 左移函数 | ||
void | right | 右移函数 | ||
unsigned int | random | 随机函数 | ||
void | setnext | 设置下一个函数 | ||
bool | overlap | 判断重叠函数 |
三、源代码
//文件main.cpp
#include <windows.h> //使用WindowsAPI函数
#include <stdlib.h> //使用随机数等
#include "block.h" //积木对象的接口
#define row 16 //游戏区域的行数
#define column 10 //游戏区域的列数
#define size 20 //单个方块的大小
int score = 6; //游戏分数
TCHAR score_arr[5]; //使用wsprintf打印时的buffer
int level = 1; //游戏等级 等级越高,速度越快
TCHAR level_arr[2]; //使用wsprintf打印时的buffer
int speed = 600; //游戏速度
static bool region[column][row]; //当前游戏区域
static bool nextregion[4][4]; //下一个积木提示区域
static block * pblock = NULL; //指向积木的指针
int del(HWND hwnd); //消行函数
void lvup(HWND); //等级提升函数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //窗口回调函数
/*****************************************Windows主程序************************************************/
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
WNDCLASS game_cls; //新建游戏窗口类
HWND hgame; //游戏窗口句柄
MSG game_msg; //窗口消息
//以下为初始化窗口类的各个参数
game_cls.style = CS_HREDRAW | CS_VREDRAW; //窗口风格
game_cls.lpfnWndProc = WndProc; //窗口过程
game_cls.cbClsExtra = 0;
game_cls.cbWndExtra = 0;
game_cls.hInstance = hInstance; //当前窗口句柄
game_cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); //窗口图标,使用默认
game_cls.hCursor = LoadCursor(NULL, IDC_ARROW); //鼠标样式
game_cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //窗口背景画刷
game_cls.lpszMenuName = NULL; //窗口菜单,不使用菜单
game_cls.lpszClassName = TEXT("BaseWin"); //窗口类名
//注册游戏窗口类
RegisterClass(&game_cls);
//创建窗口
hgame = CreateWindow(
TEXT("BaseWin"), //传入窗口类名
TEXT("俄罗斯方块"), //设置窗口标题
WS_OVERLAPPEDWINDOW, //窗口风格
CW_USEDEFAULT, //初始化时x轴的位置
CW_USEDEFAULT, //初始化时y轴的位置
(column + column)*size, //窗口宽度
(row + 4)*size, //窗口高度
NULL, //父窗口句柄
NULL, //窗口菜单句柄
hInstance, //当前窗口的句柄
NULL);
//显示窗口
ShowWindow(hgame, iCmdShow);
//更新窗口
UpdateWindow(hgame);
//进入消息循环
while (GetMessage(&game_msg, NULL, 0, 0))
{
TranslateMessage(&game_msg); //翻译消息
DispatchMessage(&game_msg); //分派消息
}
//程序结束,返回给系统程序将退出的消息
return game_msg.wParam;
}
/*******************************************回调函数***************************************************/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc; //设备环境句柄
PAINTSTRUCT ps;
RECT rect;
//设置游戏区域的边界
static POINT border[] = { -1, -1, //当前游戏区域的边界
size* column + 1,size*row + 1 };
static POINT nextborder[] = { size* column + size, size, //“下一个”游戏区域的边界
border[1].x + size * 5, size * 5 };
static int nexttype = 0, //下一个方块的类型及方向
nextdir = 0;
switch (message) //对窗口消息处理
{
//窗口第一次创建
case WM_CREATE:
hdc = GetDC(hwnd); //获取DC环境
pblock = new block(region, nexttype, nextdir); //初始化第一个积木
pblock->setnext(nexttype, nextdir, nextregion); //设置下一个积木
SetTimer(hwnd, 1, speed, NULL); //开启定时器
ReleaseDC(hwnd, hdc); //释放DC
return 0;
//绘制客户区
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); //获取客户区环境变量
//开始绘制客户区
GetClientRect(hwnd, &rect); //获取客户区
SetViewportOrgEx(hdc, 20, 20, NULL); //设置窗口原点
Rectangle(hdc, border[0].x, border[0].y, border[1].x, border[1].y); //绘制游戏区域的边界
TextOut(hdc, border[1].x + size, 0, //绘制“下一个”字符串
TEXT("下一个:"), 7);
TextOut(hdc, border[1].x + size, size * 6, //绘制“得分字符串
TEXT("得分:"), 5);
TextOut(hdc, border[1].x + size * 3, size * 6, //绘制分数
score_arr, wsprintf(score_arr,
TEXT("%d"), score));
TextOut(hdc, border[1].x + size, size * 7, //绘制“level”字符串
TEXT("level:"), 6);
TextOut(hdc, border[1].x + size * 3, size * 7, //绘制等级
level_arr, wsprintf(level_arr,
TEXT("%d"), level));
TextOut(hdc, border[1].x + size, size * 9 - 5, //绘制作者信息
TEXT("作者:"), 5);
TextOut(hdc, border[1].x + size, size * 10,
TEXT("物联网16-2"), 10);
TextOut(hdc, border[1].x + size, size * 11,
TEXT("郭义臣:"), 6);
//换画笔,准备绘制方块
SelectObject(hdc, GetStockObject(BLACK_BRUSH)); //换画笔
SelectObject(hdc, GetStockObject(WHITE_PEN));
//绘制当前游戏区的方块
for (int x = 0; x < column; ++x)
{
for (int y = 0; y < row; ++y)
{
if (region[x][y])
{
Rectangle(hdc, x* size, y*size, (x + 1)*size, (y + 1)*size);
}
}
}
//绘制下一个游戏区域的方块
for (int x = 0; x < 4; ++x)
{
for (int y = 0; y < 4; ++y)
{
if (nextregion[x][y])
{
Rectangle(hdc, nextborder[0].x + x * size, nextborder[0].y + y * size,
nextborder[0].x + (x + 1)*size, nextborder[0].y + (y + 1)*size);
}
}
}
EndPaint(hwnd, &ps); //结束客户区的绘制
return 0;
//触发定时器
case WM_TIMER:
if (!pblock->down()) //积木定时下落,若不能下落则执行以下语句
{
score += del(hwnd); //分数结算
if (score / 10 > level - 1) //若分数为10的倍数则等级提升
{
lvup(hwnd);
}
delete(pblock); //释放当前积木内存
pblock = new block(region, nexttype, nextdir); //初始化新积木
pblock->setnext(nexttype, nextdir, nextregion); //设置下一个积木
if (pblock->overlap()) //若新积木到达游戏区域顶端则游戏结束
{
KillTimer(hwnd, 1);
MessageBox(hwnd,
TEXT("游戏结束!!"),
TEXT("提示"), MB_OK);
}
}
InvalidateRect(hwnd, NULL, TRUE); //重绘客户区
return 0;
//方向键及暂停键(空格键)被按下
case WM_KEYDOWN:
switch (wParam)
{
//空格键被按下
case VK_SPACE:
KillTimer(hwnd, 1); //游戏暂停
MessageBox(hwnd,
TEXT("暂停,按确认键继续"),
TEXT("提示"), MB_OK);
SetTimer(hwnd, 1, speed, NULL); //游戏继续
break;
//左方向键被按下
case VK_LEFT:
pblock->left(); //积木左移
InvalidateRect(hwnd, NULL, TRUE); //重绘客户区
break;
//右方向键被按下
case VK_RIGHT:
pblock->right(); //积木右移
InvalidateRect(hwnd, NULL, TRUE); //重绘客户区
break;
//上方向键被按下
case VK_UP:
pblock->change(); //积木旋转
InvalidateRect(hwnd, NULL, TRUE); //重绘客户区
break;
//下方向键被按下
case VK_DOWN:
while (pblock->down()); //积木加速下落
score += del(hwnd); //分数结算
if (score / 10 > level - 1) //若分数为10的倍数则等级提升
{
lvup(hwnd);
}
delete(pblock); //释放当前积木内存
pblock = new block(region, nexttype, nextdir); //初始化新积木
pblock->setnext(nexttype, nextdir, nextregion); //设置下一个积木
if (pblock->overlap()) //若新积木到达游戏区域顶端则游戏结束
{
KillTimer(hwnd, 1);
MessageBox(hwnd,
TEXT("游戏结束!!"),
TEXT("提示"), MB_OK);
}
InvalidateRect(hwnd, NULL, TRUE); //重绘客户区
break;
default:
break;
}
return 0;
case WM_DESTROY: //窗口销毁
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
/************************************************消行函数*******************************************/
int del(HWND hwnd)
{
int line = 0;
for (int y = 16 - 1; y >= 0; --y)
{
bool result = true;
for (int x = 0; x < 10; ++x)
{
if (region[x][y] == false)
{
result = false;
break;
}
}
if (result)
{
line++;
for (int i = y; i > 0; --i)
{
for (int j = 0; j < 10; ++j)
{
region[j][i] = region[j][i - 1];
}
}
y++;
}
}
InvalidateRect(hwnd, NULL, TRUE);
if (line)
{
return (line - 1) * 2 + 1;
}
else
{
return 0;
}
}
/******************************************等级提升函数********************************************/
void lvup(HWND hwnd)
{
//等级提升
level = score / 10 + 1;
KillTimer(hwnd, 1);
MessageBox(hwnd, TEXT("level up!!"), TEXT("提示"), MB_OK);
//重置游戏区域
for (int x = 0; x < column; ++x)
{
for (int y = 0; y < row; ++y)
{
region[x][y] = false;
}
}
//速度提升
speed = (int)(speed*0.8);
SetTimer(hwnd, 1, speed, NULL);
}
//文件block.h
#pragma once
#include<windows.h>
#include <stdlib.h>
class block
{
public:
block(bool region[][16], int type, int dir);
virtual ~block();
bool down(); //方块下落函数
void change(); //方块旋转函数
void left(); //方块左移函数
void right(); //方块右移函数
bool overlap(); //判断新出现的方块是否与已有方块重合函数
void setnext(int &nexttype, int & nextdir, bool nextregion[][4]); //设置下一个出现的方块
private:
bool(*pregion)[16]; //在block对象中暂存游戏区域的变量
unsigned int random(int n); //获取随机数函数
POINT pt[4]; //方块在游戏区域的位置
int type; //方块的7种类型
int dir; //方块的4种方向
bool overlapflag; //新出现的方块是否与已有方块重合标志
};
//文件block.cpp
#include "block.h"
struct
{
POINT pt[4]; //一个最小方块单元的坐标
}
blocktype[7][4] = //所有积木的坐标
{
//正7的四种方向
{ { 0, 0, 1, 0, 1, 1, 1, 2 },{ 2, 0, 0, 1, 1, 1, 2, 1 },{ 1, 0, 1, 1, 1, 2, 2, 2 },{ 0, 1, 1, 1, 2, 1, 0, 2 } },
//反7的四种方向
{ { 1, 0, 2, 0, 1, 1, 1, 2 },{ 0, 1, 1, 1, 2, 1, 2, 2 },{ 1, 0, 1, 1, 0, 2, 1, 2 },{ 0, 0, 0, 1, 1, 1, 2, 1 } },
//正Z的两种方向
{ { 0, 0, 1, 0, 1, 1, 2, 1 },{ 2, 0, 1, 1, 2, 1, 1, 2 },{ 0, 0, 1, 0, 1, 1, 2, 1 },{ 2, 0, 1, 1, 2, 1, 1, 2 } },
//反Z的两种方向
{ { 1, 0, 2, 0, 0, 1, 1, 1 },{ 1, 0, 1, 1, 2, 1, 2, 2 },{ 1, 0, 2, 0, 0, 1, 1, 1 },{ 1, 0, 1, 1, 2, 1, 2, 2 } },
//T型的四种方向
{ { 1, 0, 0, 1, 1, 1, 2, 1 },{ 0, 0, 0, 1, 1, 1, 0, 2 },{ 0, 0, 1, 0, 2, 0, 1, 1 },{ 1, 0, 0, 1, 1, 1, 1, 2 } },
//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 },{ 0, 1, 1, 1, 2, 1, 3, 1 } },
//田字方块
{ { 0, 0, 1, 0, 0, 1, 1, 1 },{ 0, 0, 1, 0, 0, 1, 1, 1 },{ 0, 0, 1, 0, 0, 1, 1, 1 },{ 0, 0, 1, 0, 0, 1, 1, 1 } }
};
block::block(bool region[][16], int type, int dir)
{
overlapflag = false; //默认 新出现的方块是否与已有方块未重合
block::type = type; //设置新出现积木的类型
block::dir = dir; //设置新出现积木的方向
pregion = region; //把游戏区域传给对象,方便操作
//判断新出现的方块是否与已有方块重合
for (int i = 0; i < 4; i++)
{
if (pregion[blocktype[type][dir].pt[i].x + 4][blocktype[type][dir].pt[i].y] == true)
{
overlapflag = true;
break;
}
}
//初始化新出现的方块
for (int i = 0; i < 4; i++)
{
pt[i].x = blocktype[type][dir].pt[i].x + 4;
pt[i].y = blocktype[type][dir].pt[i].y;
pregion[pt[i].x][pt[i].y] = true;
}
}
/*****************************************析构函数*******************************************/
block::~block()
{
}
/******************************************获取随机数函数*****************************************/
unsigned int block::random(int n)
{
SYSTEMTIME st;
GetLocalTime(&st);
srand(st.wMilliseconds);
return rand() % n;
}
/*************************************方块下落函数***************************************/
bool block::down()
{
bool result = true;
//将方块所在格子先假设指定为无方块
for (int i = 0; i < 4; ++i)
pregion[pt[i].x][pt[i].y] = false;
for (int i = 0; i < 4; ++i)
{
//假如继续落下超过下底边界,返回false;或者假如该小方块下落一格已经有方块,结果为false
if (pt[i].y + 1 > 15 || pregion[pt[i].x][pt[i].y + 1] == true)
{
result = false;
break;
}
}
//恢复方块所在格子为有方块
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = true;
}
if (result)
{
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = false;
++pt[i].y;
}
for (int k = 0; k < 4; ++k)
pregion[pt[k].x][pt[k].y] = true;
}
return result;
}
/******************************************方块旋转函数**************************************/
void block::change()
{
bool result = true;
for (int i = 0; i < 4; i++)
{
pregion[pt[i].x][pt[i].y] = 0;
}
int t = (dir + 1) % 4;
int x = pt[0].x - blocktype[type][dir].pt[0].x,
y = pt[0].y - blocktype[type][dir].pt[0].y;
for (int i = 0; i < 4; i++)
{
if (pregion[blocktype[type][t].pt[i].x + x][blocktype[type][t].pt[i].y + y] ||//该方格已经有方块
blocktype[type][t].pt[i].x + x > 10 - 1 || //x坐标超越了右边界
blocktype[type][t].pt[i].x + x < 0 || //x坐标超越了左边界
blocktype[type][t].pt[i].y + y > 16 - 1) //y坐标超越了下底边界
{
result = false;
break;
}
}
if (result)
{
for (int i = 0; i < 4; i++)
{
pt[i].x = blocktype[type][t].pt[i].x + x;
pt[i].y = blocktype[type][t].pt[i].y + y;
pregion[pt[i].x][pt[i].y] = 1;
}
dir = t;
}
else
{
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = true;
}
}
}
/********************************************方块左移函数****************************************/
void block::left()
{
bool result = true;
//将方块所在格子先假设指定为无方块
for (int i = 0; i < 4; ++i)
pregion[pt[i].x][pt[i].y] = false;
for (int i = 0; i < 4; ++i)
{
//假如继续左移超过左边边界,结果为false;或者假如该小方块左移一格已经有方块,结果为false
if (pt[i].x <= 0 || pregion[pt[i].x - 1][pt[i].y] == true)
{
result = false;
break;
}
}
//恢复方块所在格子为有方块
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = true;
}
if (result)
{
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = false;
--pt[i].x;
}
for (int k = 0; k < 4; ++k)
{
pregion[pt[k].x][pt[k].y] = true;
}
}
}
/***********************************************方块右移函数****************************************/
void block::right()
{
bool result = true;
//将方块所在格子先假设指定为无方块
for (int i = 0; i < 4; ++i)
pregion[pt[i].x][pt[i].y] = false;
for (int i = 0; i < 4; ++i)
{
//假如继续左移超过右边边界,结果为false;或者假如该小方块左移一格已经有方块,结果为false
if (pt[i].x + 1 >= 10 || pregion[pt[i].x + 1][pt[i].y] == true)
{
result = false;
break;
}
}
//恢复方块所在格子为有方块
for (int i = 0; i < 4; ++i)
pregion[pt[i].x][pt[i].y] = true;
if (result)
{
for (int i = 0; i < 4; ++i)
{
pregion[pt[i].x][pt[i].y] = false;
++pt[i].x;
}
for (int k = 0; k < 4; ++k)
pregion[pt[k].x][pt[k].y] = true;
}
}
/*****************************判断新出现的方块是否与已有方块重合函数***************************/
bool block::overlap()
{
return overlapflag;
}
/*******************************************设置下一个出现的方块*************************************/
void block::setnext(int &nexttype, int & nextdir, bool nextregion[][4])
{
nexttype = random(7);
nextdir = random(4);
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 4; y++)
{
nextregion[x][y] = false;
}
}
for (int i = 0; i < 4; i++)
{
nextregion[blocktype[nexttype][nextdir].pt[i].x][blocktype[nexttype][nextdir].pt[i].y] = true;
}
}