C++ 实现 俄罗斯方块 源码分享 (Windows编程),有图形界面

欢迎大家点评!


一、功能简介

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;
  }
}


评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值