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

2023-10-27

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

喜欢的点个赞呗!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言小游戏-俄罗斯方块 的相关文章

  • python学习笔记---函数【廖雪峰】

    函数 调用函数 函数名其实就是指向一个函数对象的引用 完全可以把函数名赋给一个变量 相当于给这个函数起了一个 别名 gt gt gt a abs 变量a指向abs函数 gt gt gt a 1 所以也可以通过a调用abs函数 1 定义函数
  • UE4内置浏览器支持h264 mp4 视频 编译cef3 3071源码

    转载 原文链接 UE4内置浏览器支持h264 mp4 视频 编译cef3 3071源码 ue4 cef 鸿蒙老道的博客 CSDN博客首先ue4内置的CEF3 浏览器是不支持h264视频的 所以需要我们重新编译CEF内核 然后覆盖旧的版本一
  • 空闲时间不要接私活,要提升自己

    这个话题很纠结 现在社会 有很多人都在利用个人时间兼职赚钱 程序员俗称 接私活 其他行业称作兼职 比如下了班出去跑滴滴 周末兼职抢单送外卖等等 都是普通人很常见的兼职方式 甚至很多技术同行 我听说也有周末去跑滴滴和送外卖的 我觉得很不可思议

随机推荐

  • JVM-对象内存分配与回收细节问题

    对象内存分配与回收细节问题 1 禁用System gc System gc 会显示直接触发Full GC 同时触发老年代和新生代进行回收 而一般情况是我们认为 垃圾回收时自动进行的 无需手动触发 频繁的垃圾回收对系统性能造成较大影响 可以使
  • [数据分析与可视化] 基于matplotlib-scalebar库绘制比例尺

    matplotlib scalebar是一个Python库 用于在matplotlib图形中添加比例尺 它允许用户指定比例尺的大小 位置 字体和颜色 以及比例尺的单位 该库支持不同的比例尺单位 例如米 英尺 英寸等 matplotlib s
  • 一个51单片机矩阵键盘的扫描,消抖和键号显示程序

    51单片机的按键判断是一项很重要的功能 在蓝桥杯的比赛中必不可少 下面介绍一个稳定性比较好的矩阵键盘程序 首先介绍一下该矩阵键盘的基本原理 1 扫描 四条列线 接单片机的P2 4 P2 7口 电平都为1 四条行线 接P2 0 P2 3口 先
  • 交叉熵、信息熵、KL散度、互信息与Information Bottleneck【信息瓶颈IB】之间的关系

    Information Bottleneck IB 预备知识 交叉熵损失函数 CrossEntropy Loss 在DL中 交叉熵损失函数用作分类问题 使用它作为Loss Function时 在模型的输出层总会接一个softmax函数 交叉
  • js制作css滚动条,css、js实现自定义滚动条的两种方式

    一般默认的滚动条会比较丑 我们可以利用css或者js来实现实现自定义滚动条的功能 下面就整理2种实现方式 1 css自定义滚动条 需要在WebKit平台上才支持 代码如下 webkit scrollbar width 8px webkit
  • Python编程练习:翻转数字

    coding utf 8 10 翻转数字 难度 中等 通过键盘输入一个数字 若 1 该数字位各个位上的数字和为奇数 则将该数各位数倒叙打印 如122 各个位上的数字之和为1 2 2 5 打印221 2 若该数字各个位数之和为偶数 则直接打印
  • python中[::-1]是什么意思

    c 1 2 3 4 5 6 c 1 6 5 4 3 2 1 反转列表顺序 扩展 a 0 1 2 3 4 5 6 7 8 9 b a i j 表示复制a i 到a j 1 以生成新的list对象 b a 1 3 那么 b的内容是 1 2 当i
  • CMake Error: The source “xxx“ does not match the source “yyy“ used to generate cache. Re-run cmake

    目录 场景复现 解决方案 找到我们的编译路径 重新使用CMake编译 场景复现 今天使用CMake再次编译fmt源码时 cmake抛出一个错误 告诉我找不到一个路径 但我这个电脑本就没有这个路径 仔细一看这个路径是我在另一个电脑编译的路径
  • HTTP/HTTPS协议详解

    目录 一 HTTP详解 1 1 概念 1 2 HTTP的协议格式 1 2 1 HTTP请求体格式 1 2 2 HTTP响应体格式 1 3 HTTP请求方法 1 4 认识请求报头 1 5 HTTP请求过程 1 6 认识状态码 二 HTTPS详
  • 【动态系统的建模与分析】一阶系统的单位阶跃响应+时间常数-笔记

    一个一阶系统 其数学表达 做系统识别 令qin c 去记录高度的变化 可得图像 这个系统的响应为 如果其4s达到稳定时间 则 该系统传递函数为 则
  • vue金额格式化保留两位小数

    Vue filter number function data return data toFixed 2 在需要的地方 integralval 100 number 元
  • Nginx常用模块

    Nginx常用模块 文章目录 Nginx常用模块 1 Nginx常用模块 1 1 Nginx目录索引 下载模块 1 1 1 配置autoindex语法 1 1 2 autoindex配置实例 1 1 3上传资源 1 1 4 autoinde
  • 【嵙大OJ】Problem 1217: 编写函数:浮点数取整 (Append Code)

    Problem A 编写函数 浮点数取整 Append Code Time Limit 1 Sec Memory Limit 2 MB Submit 7000 Solved 2864 Submit Status Web Board Desc
  • 操作系统重要概念——异步性

    在多道程序环境下 允许多个进程并发执行 进程在使用资源时可能需要等待或放弃 进程的执行并不是一气成的 而是以走走停停的形式推进 如下举例 进程以不可预知的速度向前推进 何时执行 何时暂停 何时完成都是未知的 这就造成了系统的异步性
  • UUID 生成(源代码编译)

    根据定义 UUID Universally Unique IDentifier 也称GUID 在时间和空间都是唯一的 为保证空间的唯一性 每个UUID使用了一个48位的值来记录 一般是计算机的网卡地址 为保证时间上的唯一性 每个UUID具有
  • ++ 符号

    关于 符号 i i 刚好最近看到一个关于i i 的测试题就查询资料测试了一下 int i 10 int j 30 i i Console WriteLine i Console WriteLine i i Console WriteLine
  • ajax保存文件对话框_VBA学习笔记34-2:Excel对话框(FileDialog)

    学习资源 Excel VBA从入门到进阶 第34集 by兰色幻想 这篇笔记写对话框的FileDialog对象 开始前先把上篇漏写的改变窗口默认路径补一下 chdrive 盘符 可以改变默认驱动器 chdir 路径 可以改变默认路径 这两个默
  • C#调用C回调函数后,程序奔溃问题

    原始代理声明 delegate void DlgVideoStreamCallBack IntPtr pData int size int height int width IntPtr pUserData C函数导入 DllImport
  • gitlab-ce-10.0.2版本升级到15.4.2

    先看官方升级文档说明 官方文档链接 升级 如果你的gitlab版本是10 0 2 那个要升级到15 4 2 首先得过度如下几个版本 10 1 0 10 2 3 10 8 7 11 3 4 11 11 8 12 0 12 12 1 17 12
  • C语言小游戏-俄罗斯方块

    C语言小游戏 俄罗斯方块 全部代码如下所示 include