0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言零基础项目:俄罗斯方块游戏!详细思路+源码分享

C语言编程学习基地 来源:C语言编程学习基地 作者:C语言编程学习基地 2022-12-19 14:52 次阅读

每天一个C语言小项目,提升你的编程能力!

俄罗斯方块是童年的经典游戏~~

由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

本次用C语言来实现!这个程序在界面上没有做额外的修饰,重点放在游戏手感和消息处理过程。游戏手感上做了一些细节处理,仔细体验感受一下。

效果如图:

56009552-7d2d-11ed-8abf-dac502259ad0.png

其中也是加入了按键操作功能:"A"左移一格;"D"右移一格;"W"旋转方块;S 下移一格;空格键让方块下落到底),"ESC"健退出游戏,用户还可以自己根据自定义习惯的按键来操作游戏。

编译环境:Visual Studio 2019/2022,EasyX插件

代码展示:

#include 
#include 
#include 
#include 






/////////////////////////////////////////////
// 定义常量、枚举量、结构体、全局变量
/////////////////////////////////////////////


#define  WIDTH  10    // 游戏区宽度
#define  HEIGHT  22    // 游戏区高度
#define  UNIT  20    // 每个游戏区单位的实际像素


// 定义操作类型
enum CMD
{
  CMD_ROTATE,            // 方块旋转
  CMD_LEFT, CMD_RIGHT, CMD_DOWN,  // 方块左、右、下移动
  CMD_SINK,            // 方块沉底
  CMD_QUIT            // 退出游戏
};


// 定义绘制方块的方法
enum DRAW
{
  SHOW,  // 显示方块
  CLEAR,  // 擦除方块
  FIX    // 固定方块
};


// 定义七种俄罗斯方块
struct BLOCK
{
  WORD dir[4];  // 方块的四个旋转状态
  COLORREF color;  // 方块的颜色
};
BLOCK g_Blocks[7] = {  {0x0F00, 0x4444, 0x0F00, 0x4444, RED},    // I
            {0x0660, 0x0660, 0x0660, 0x0660, BLUE},    // 口
            {0x4460, 0x02E0, 0x0622, 0x0740, MAGENTA},  // L
            {0x2260, 0x0E20, 0x0644, 0x0470, YELLOW},  // 反 L
            {0x0C60, 0x2640, 0x0C60, 0x2640, CYAN},    // Z
            {0x0360, 0x4620, 0x0360, 0x4620, GREEN},  // 反 Z
            {0x4E00, 0x4C40, 0x0E40, 0x4640, BROWN}};  // T


// 定义当前方块、下一个方块的信息
struct BLOCKINFO
{
  byte id;  // 方块 ID
  char x, y;  // 方块在游戏区中的坐标
  byte dir:2;  // 方向
}  g_CurBlock, g_NextBlock;


// 定义游戏区
BYTE g_World[WIDTH][HEIGHT] = {0};






/////////////////////////////////////////////
// 函数声明
/////////////////////////////////////////////


void Init();                      // 初始化游戏
void Quit();                      // 退出游戏
void NewGame();                      // 开始新游戏
void GameOver();                    // 结束游戏
CMD GetCmd();                      // 获取控制命令
void DispatchCmd(CMD _cmd);                // 分发控制命令
void NewBlock();                    // 生成新的方块
bool CheckBlock(BLOCKINFO _block);            // 检测指定方块是否可以放下
void DrawUnit(int x, int y, COLORREF c, DRAW _draw);  // 画单元方块
void DrawBlock(BLOCKINFO _block, DRAW _draw = SHOW);  // 画方块
void OnRotate();                    // 旋转方块
void OnLeft();                      // 左移方块
void OnRight();                      // 右移方块
void OnDown();                      // 下移方块
void OnSink();                      // 沉底方块






/////////////////////////////////////////////
// 函数定义
/////////////////////////////////////////////


// 主函数
void main()
{
  Init();


  CMD c;
  while(true)
  {
    c = GetCmd();
    DispatchCmd(c);


    // 按退出时,显示对话框咨询用户是否退出
    if (c == CMD_QUIT)
    {
      HWND wnd = GetHWnd();
      if (MessageBox(wnd, _T("您要退出游戏吗?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
        Quit();
    }
  }
}




// 初始化游戏
void Init()
{
  initgraph(640, 480);
  srand((unsigned)time(NULL));
  setbkmode(TRANSPARENT);      // 设置图案填充的背景色为透明


  // 显示操作说明
  settextstyle(14, 0, _T("宋体"));
  outtextxy(20, 330, _T("操作说明"));
  outtextxy(20, 350, _T("上:旋转"));
  outtextxy(20, 370, _T("左:左移"));
  outtextxy(20, 390, _T("右:右移"));
  outtextxy(20, 410, _T("下:下移"));
  outtextxy(20, 430, _T("空格:沉底"));
  outtextxy(20, 450, _T("ESC:退出"));


  // 设置坐标原点
  setorigin(220, 20);


  // 绘制游戏区边界
  rectangle(-1, -1, WIDTH * UNIT, HEIGHT * UNIT);
  rectangle((WIDTH + 1) * UNIT - 1, -1, (WIDTH + 5) * UNIT, 4 * UNIT);


  // 开始新游戏
  NewGame();
}




// 退出游戏
void Quit()
{
  closegraph();
  exit(0);
}




// 开始新游戏
void NewGame()
{
  // 清空游戏区
  setfillcolor(BLACK);
  solidrectangle(0, 0, WIDTH * UNIT - 1, HEIGHT * UNIT - 1);
  ZeroMemory(g_World, WIDTH * HEIGHT);


  // 生成下一个方块
  g_NextBlock.id = rand() % 7;
  g_NextBlock.dir = rand() % 4;
  g_NextBlock.x = WIDTH + 1;
  g_NextBlock.y = HEIGHT - 1;


  // 获取新方块
  NewBlock();
}




// 结束游戏
void GameOver()
{
  HWND wnd = GetHWnd();
  if (MessageBox(wnd, _T("游戏结束。
您想重新来一局吗?"), _T("游戏结束"), MB_YESNO | MB_ICONQUESTION) == IDYES)
    NewGame();
  else
    Quit();
}




// 获取控制命令
DWORD m_oldtime;
CMD GetCmd()
{
  // 获取控制值
  while(true)
  {
    // 如果超时,自动下落一格
    DWORD newtime = GetTickCount();
    if (newtime - m_oldtime >= 500)
    {
      m_oldtime = newtime;
      return CMD_DOWN;
    }


    // 如果有按键,返回按键对应的功能
    if (_kbhit())
    {
      switch(_getch())
      {
        case 'w':
        case 'W':  return CMD_ROTATE;
        case 'a':
        case 'A':  return CMD_LEFT;
        case 'd':
        case 'D':  return CMD_RIGHT;
        case 's':
        case 'S':  return CMD_DOWN;
        case 27:  return CMD_QUIT;
        case ' ':  return CMD_SINK;
        case 0:
        case 0xE0:
          switch(_getch())
          {
            case 72:  return CMD_ROTATE;
            case 75:  return CMD_LEFT;
            case 77:  return CMD_RIGHT;
            case 80:  return CMD_DOWN;
          }
      }
    }


    // 延时 (降低 CPU 占用率)
    Sleep(20);
  }
}




// 分发控制命令
void DispatchCmd(CMD _cmd)
{
  switch(_cmd)
  {
    case CMD_ROTATE:  OnRotate();    break;
    case CMD_LEFT:    OnLeft();    break;
    case CMD_RIGHT:    OnRight();    break;
    case CMD_DOWN:    OnDown();    break;
    case CMD_SINK:    OnSink();    break;
    case CMD_QUIT:    break;
  }
}




// 生成新的方块
void NewBlock()
{
  g_CurBlock.id = g_NextBlock.id,    g_NextBlock.id = rand() % 7;
  g_CurBlock.dir = g_NextBlock.dir,  g_NextBlock.dir = rand() % 4;
  g_CurBlock.x = (WIDTH - 4) / 2;
  g_CurBlock.y = HEIGHT + 2;


  // 下移新方块直到有局部显示
  WORD c = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];
  while((c & 0xF) == 0)
  {
    g_CurBlock.y--;
    c >>= 4;
  }


  // 绘制新方块
  DrawBlock(g_CurBlock);


  // 绘制下一个方块
  setfillcolor(BLACK);
  solidrectangle((WIDTH + 1) * UNIT, 0, (WIDTH + 5) * UNIT - 1, 4 * UNIT - 1);
  DrawBlock(g_NextBlock);


  // 设置计时器,用于判断自动下落
  m_oldtime = GetTickCount();
}




// 画单元方块
void DrawUnit(int x, int y, COLORREF c, DRAW _draw)
{
  // 计算单元方块对应的屏幕坐标
  int left = x * UNIT;
  int top = (HEIGHT - y - 1) * UNIT;
  int right = (x + 1) * UNIT - 1;
  int bottom = (HEIGHT - y) * UNIT - 1;


  // 画单元方块
  switch(_draw)
  {
    case SHOW:
      // 画普通方块
      setlinecolor(0x006060);
      roundrect(left + 1, top + 1, right - 1, bottom - 1, 5, 5);
      setlinecolor(0x003030);
      roundrect(left, top, right, bottom, 8, 8);
      setfillcolor(c);
      setlinecolor(LIGHTGRAY);
      fillrectangle(left + 2, top + 2, right - 2, bottom - 2);
      break;


    case FIX:
      // 画固定的方块
      setfillcolor(RGB(GetRValue(c) * 2 / 3, GetGValue(c) * 2 / 3, GetBValue(c) * 2 / 3));
      setlinecolor(DARKGRAY);
      fillrectangle(left + 1, top + 1, right - 1, bottom - 1);
      break;


    case CLEAR:
      // 擦除方块
      setfillcolor(BLACK);
      solidrectangle(x * UNIT, (HEIGHT - y - 1) * UNIT, (x + 1) * UNIT - 1, (HEIGHT - y) * UNIT - 1);
      break;
  }
}




// 画方块
void DrawBlock(BLOCKINFO _block, DRAW _draw)
{
  WORD b = g_Blocks[_block.id].dir[_block.dir];
  int x, y;


  for(int i = 0; i < 16; i++, b <<= 1)
    if (b & 0x8000)
    {
      x = _block.x + i % 4;
      y = _block.y - i / 4;
      if (y < HEIGHT)
        DrawUnit(x, y, g_Blocks[_block.id].color, _draw);
    }
}




// 检测指定方块是否可以放下
bool CheckBlock(BLOCKINFO _block)
{
  WORD b = g_Blocks[_block.id].dir[_block.dir];
  int x, y;


  for(int i = 0; i < 16; i++, b <<= 1)
    if (b & 0x8000)
    {
      x = _block.x + i % 4;
      y = _block.y - i / 4;
      if ((x < 0) || (x >= WIDTH) || (y < 0))
        return false;


      if ((y < HEIGHT) && (g_World[x][y]))
        return false;
    }


  return true;
}




// 旋转方块
void OnRotate()
{
  // 获取可以旋转的 x 偏移量
  int dx;
  BLOCKINFO tmp = g_CurBlock;
  tmp.dir++;          if (CheckBlock(tmp))  {  dx = 0;    goto rotate;  }
  tmp.x = g_CurBlock.x - 1;  if (CheckBlock(tmp))  {  dx = -1;  goto rotate;  }
  tmp.x = g_CurBlock.x + 1;  if (CheckBlock(tmp))  {  dx = 1;    goto rotate;  }
  tmp.x = g_CurBlock.x - 2;  if (CheckBlock(tmp))  {  dx = -2;  goto rotate;  }
  tmp.x = g_CurBlock.x + 2;  if (CheckBlock(tmp))  {  dx = 2;    goto rotate;  }
  return;


rotate:
  // 旋转
  DrawBlock(g_CurBlock, CLEAR);
  g_CurBlock.dir++;
  g_CurBlock.x += dx;
  DrawBlock(g_CurBlock);
}




// 左移方块
void OnLeft()
{
  BLOCKINFO tmp = g_CurBlock;
  tmp.x--;
  if (CheckBlock(tmp))
  {
    DrawBlock(g_CurBlock, CLEAR);
    g_CurBlock.x--;
    DrawBlock(g_CurBlock);
  }
}




// 右移方块
void OnRight()
{
  BLOCKINFO tmp = g_CurBlock;
  tmp.x++;
  if (CheckBlock(tmp))
  {
    DrawBlock(g_CurBlock, CLEAR);
    g_CurBlock.x++;
    DrawBlock(g_CurBlock);
  }
}




// 下移方块
void OnDown()
{
  BLOCKINFO tmp = g_CurBlock;
  tmp.y--;
  if (CheckBlock(tmp))
  {
    DrawBlock(g_CurBlock, CLEAR);
    g_CurBlock.y--;
    DrawBlock(g_CurBlock);
  }
  else
    OnSink();  // 不可下移时,执行“沉底方块”操作
}




// 沉底方块
void OnSink()
{
  int i, x, y;


  // 连续下移方块
  DrawBlock(g_CurBlock, CLEAR);
  BLOCKINFO tmp = g_CurBlock;
  tmp.y--;
  while (CheckBlock(tmp))
  {
    g_CurBlock.y--;
    tmp.y--;
  }
  DrawBlock(g_CurBlock, FIX);


  // 固定方块在游戏区
  WORD b = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];
  for(i = 0; i < 16; i++, b <<= 1)
    if (b & 0x8000)
    {
      if (g_CurBlock.y - i / 4 >= HEIGHT)
      {  // 如果方块的固定位置超出高度,结束游戏
        GameOver();
        return;
      }
      else
        g_World[g_CurBlock.x + i % 4][g_CurBlock.y - i / 4] = 1;
    }


  // 检查是否需要消掉行,并标记
  BYTE remove = 0;  // 低 4 位用来标记方块涉及的 4 行是否有消除行为
  for(y = g_CurBlock.y; y >= max(g_CurBlock.y - 3, 0); y--)
  {
    i = 0;
    for(x = 0; x < WIDTH; x++)
      if (g_World[x][y] == 1)
        i++;


    if (i == WIDTH)
    {
      remove |= (1 << (g_CurBlock.y - y));
      setfillcolor(LIGHTGREEN);
      setlinecolor(LIGHTGREEN);
      setfillstyle(BS_HATCHED, HS_DIAGCROSS);
      fillrectangle(0, (HEIGHT - y - 1) * UNIT + UNIT / 2 - 5, WIDTH * UNIT - 1, (HEIGHT - y - 1) * UNIT + UNIT / 2 + 5);
      setfillstyle(BS_SOLID);
    }
  }


  if (remove)  // 如果产生整行消除
  {
    // 延时 300 毫秒
    Sleep(300);


    // 擦掉刚才标记的行
    IMAGE img;
    for(i = 0; i < 4; i++, remove >>= 1)
    {
      if (remove & 1)
      {
        for(y = g_CurBlock.y - i + 1; y < HEIGHT; y++)
          for(x = 0; x < WIDTH; x++)
          {
            g_World[x][y - 1] = g_World[x][y];
            g_World[x][y] = 0;
          }


        getimage(&img, 0, 0, WIDTH * UNIT, (HEIGHT - (g_CurBlock.y - i + 1)) * UNIT);
        putimage(0, UNIT, &img);
      }
    }
  }


  // 产生新方块
  NewBlock();
}

大家赶紧去动手试试吧!

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 游戏
    +关注

    关注

    2

    文章

    696

    浏览量

    26039
  • C语言
    +关注

    关注

    180

    文章

    7530

    浏览量

    128667
  • 源码
    +关注

    关注

    8

    文章

    573

    浏览量

    28586

原文标题:C语言零基础项目:俄罗斯方块游戏!详细思路+源码分享

文章出处:【微信号:cyuyanxuexi,微信公众号:C语言编程学习基地】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    汇编语言的结构化设计及其在俄罗斯方块中的应用

    为了简化汇编语言的编写过程,本文提出了一种结构化的汇编编程思路,并以基于AT89C51芯片(以下对汇编语言的讨论针对51单片机系统)的俄罗斯方块游戏
    发表于 06-23 10:27 2763次阅读
    汇编<b class='flag-5'>语言</b>的结构化设计及其在<b class='flag-5'>俄罗斯方块</b>中的应用

    基于单片机实现的俄罗斯方块游戏

    基于单片机实现的俄罗斯方块游戏
    发表于 08-20 12:06

    俄罗斯方块游戏实例

    一个很完善的俄罗斯方块游戏
    发表于 01-10 17:45

    俄罗斯方块

    单片机做的俄罗斯方块游戏
    发表于 07-31 19:56

    好玩的俄罗斯方块游戏仿真

    好玩的俄罗斯方块游戏仿真,学protues看到的,很好玩,分享下
    发表于 10-21 20:31

    俄罗斯方块仿真

    网上找的俄罗斯方块游戏,自己连接的图,程序,希望大家喜欢
    发表于 12-02 12:16

    c语言版的俄罗斯方块

    c语言版的俄罗斯方块自己写的,一时高兴和大家分享
    发表于 12-03 18:09

    游戏手柄俄罗斯方块

    本帖最后由 依然Dirk 于 2015-1-21 15:52 编辑 游戏手柄俄罗斯方块
    发表于 01-21 15:27

    谁有俄罗斯方块游戏机的源码

    如题,谁有俄罗斯方块游戏机的源码
    发表于 10-24 18:02

    带AI的俄罗斯方块

    本帖最后由 shi_dongyu 于 2017-2-11 08:05 编辑 这周花了几天时间研究了下俄罗斯方块,并写了一个带AI的俄罗斯方块游戏。我想这个游戏对每个人都很熟悉,很
    发表于 02-10 20:08

    采用单片机来实现的智能俄罗斯方块游戏

    游戏,该设计选用的处理器型号为AT89C51的单片机。重点从软件工程角度论述了俄罗斯方块模型构造,图形旋转,坐标变换,双人游戏中多任务实时操作的设计方法与实现。2.硬件设计总体电路图硬
    发表于 11-19 08:26

    OpenHarmony开发板运行俄罗斯方块游戏

    本案例展示在OpenHarmony开发板上运行俄罗斯方块游戏, 通过12864液晶屏进行显示. 项目底层通过OpenHarmony的HDF框架来驱动, 并基于linkboy图形引擎编程框架完成
    发表于 12-03 17:27

    俄罗斯方块源码(带烟花版)

    俄罗斯方块源码(带烟花版)游戏简介经典游戏俄罗斯方块源码公布。我争取简化功能,使结构清晰、框
    发表于 08-02 09:55 0次下载

    俄罗斯方块游戏设计原理

    俄罗斯方块游戏是VHDL 应用于复杂数字系统的一个经典设计,本章将详细介绍该游戏的设计原理和设计方法。其中包括系统构成、系统设计原理和系统各个模块的实现方法。
    发表于 09-01 17:24 0次下载

    使用labview进行俄罗斯方块游戏程序

    本文档的主要内容详细介绍的是使用labview进行俄罗斯方块游戏程序。
    发表于 05-21 08:00 88次下载
    使用labview进行<b class='flag-5'>俄罗斯方块</b>的<b class='flag-5'>游戏</b>程序