C语言小游戏------俄罗斯方块

简介: C语言写的俄罗斯方块小游戏

 

#include <easyx.h>

#include <conio.h>

#include <time.h>

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

#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; // 方块的颜色

} 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("游戏结束。\n您想重新来一局吗?"), _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月前
|
C语言
扫雷游戏(用C语言实现)
扫雷游戏(用C语言实现)
102 0
|
16天前
|
C语言 Windows
C语言课设项目之2048游戏源码
C语言课设项目之2048游戏源码,可作为课程设计项目参考,代码有详细的注释,另外编译可运行文件也已经打包,windows电脑双击即可运行效果
28 1
|
2月前
|
C语言 C++
【C语言】编写“猜数字“小游戏
【C语言】编写“猜数字“小游戏
|
2月前
|
编译器 C语言
猜数字游戏实现#C语言
猜数字游戏实现#C语言
84 1
|
2月前
|
存储 C语言
揭秘C语言:泊舟的猜数字游戏
揭秘C语言:泊舟的猜数字游戏
|
3月前
|
定位技术 API C语言
C语言——实现贪吃蛇小游戏
本文介绍了一个基于Windows控制台的贪吃蛇游戏的实现方法。首先,需调整控制台界面以便更好地显示游戏。接着,文章详细描述了如何使用Win32 API函数如`COORD`、`GetStdHandle`、`GetConsoleCursorInfo`等来控制控制台的光标和窗口属性。此外,还介绍了如何利用`GetAsyncKeyState`函数实现键盘监听功能。文中还涉及了`&lt;locale.h&gt;`库的使用,以支持本地化字符显示。
60 1
C语言——实现贪吃蛇小游戏
|
3月前
|
存储 安全 算法
C 语言——实现扫雷小游戏
本文介绍了使用二维数组创建棋盘并实现扫雷游戏的方法。首先,通过初始化数组创建一个9x9的棋盘,并添加行列标识以便操作。接着,利用随机数在棋盘上布置雷。最后,通过判断玩家输入的坐标来实现扫雷功能,包括显示雷的数量和处理游戏胜利或失败的情况。文中提供了完整的代码实现。
48 1
C 语言——实现扫雷小游戏
|
2月前
|
C语言 定位技术 API
【C语言】实践:贪吃蛇小游戏(附源码)(二)
【C语言】实践:贪吃蛇小游戏(附源码)
【C语言】实践:贪吃蛇小游戏(附源码)(二)
|
2月前
|
C语言 开发者
C语言实现猜数字小游戏(详细教程)
C语言实现猜数字小游戏(详细教程)
|
2月前
|
C语言
俄罗斯方块-----C语言
俄罗斯方块-----C语言
20 0
俄罗斯方块-----C语言