欢迎大家点评!
一、功能简介
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; } }