EasyX实现俄罗斯方块(加BGM版)

 
#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<Windows.h>
#include<time.h>
#include<iostream>
#include"resource.h"
using namespace std;
#pragma comment(lib,"winmm.lib")

#define WIDTH 10
#define HEIGHT 22
#define UNIT 20//没个单位的实际像素

BYTE g_World[WIDTH][HEIGHT] = { 0 };//定义游戏区域
int grades;//得分数
TCHAR s[50];//更新分数用


enum CMD                            //方块状态枚举
{
	CMD_ROTATE,						// 方块旋转
	CMD_LEFT, 
	CMD_RIGHT, 
	CMD_DOWN,	// 方块左、右、下移动
	CMD_SINK,						// 方块沉底
	CMD_QUIT						// 退出游戏
};


/*16进制数必须以 0x开头。比如 0x1表示一个16进制数。而1则表示一个十进制。
另外如:0xff,0xFF,0X102A,等等。其中的x也不区分大小写。
(注意:0x中的0是数字0,而不是字母O)
以下是一些用法示例:
int a = 0x100F;
int b = 0x70 + a;*/

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;//当前方块

enum DRAW
{
	SHOW,                   //显示方块
	CLEAR,                  //擦除方块
	FIX                     //固定方块
};

//获取控制命令
DWORD m_oldtime;
/*DWORD 就是 Double Word, 每个word为2个字节的长度,
DWORD 双字即为4个字节,每个字节是8位,共32位*/

void Quit();          //退出游戏
void NewGame();       //开始游戏
void GameOver();      //结束游戏
CMD GetCmd();         //获取命令
void DispatchCmd(CMD _cmd);   //分发控制命令
void CreateNewBlock(); //创建新方块
bool CheckBlockIsPut(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 init()
{
	initgraph(800, 600);
	srand((unsigned)time(NULL));
	PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC | SND_LOOP);
	loadimage(NULL, _T("jpg"), MAKEINTRESOURCE(IDR_JPG1), 800, 600, 1);
	setbkmode(TRANSPARENT);//设置文字背景为透明色


	settextstyle(20, 20, _T("宋体"));
	settextcolor(RED);
	outtextxy(50, 50, _T("俄罗斯方块游戏"));
	settextcolor(WHITE);

	settextstyle(14, 0, _T("宋体"));
	outtextxy(20, 120, _T("空格:沉底"));
	outtextxy(20, 140, _T("A或方向键左:左移方块"));
	outtextxy(20, 160, _T("S或方向键下:下移方块"));
	outtextxy(20, 180, _T("D或方向键右:右移方块"));
	outtextxy(20, 200, _T("W或方向键上:旋转方块"));
	outtextxy(20, 220, _T("Esc:退出游戏"));
	outtextxy(20, 500, _T("制作:ZZK"));
	setorigin(480, 40);
	_stprintf(s, _T("你的得分是:   %d"), grades);
	outtextxy(20, -40, s);

	//画游戏区域
	rectangle(-1, -1, WIDTH*UNIT, HEIGHT*UNIT);
	rectangle((WIDTH + 1)*UNIT - 1, -1, (WIDTH + 5)*UNIT, 4 * UNIT);

	NewGame();
}





int main()
{

	init();
	CMD c;
	while (1)
	{
		c = GetCmd();
		DispatchCmd(c);
		if (c == CMD_QUIT)
		{
			HWND wnd = GetHWnd();
			if (MessageBox(wnd, _T("您要退出游戏吗?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
			{
				Quit();
			}
		}
	}





	closegraph();


	return 0;
}


void Quit()
{
	closegraph();
	exit(0);
}

void NewGame()
{
	grades = 0;
	setfillcolor(BLACK);
	//画填充矩形
	solidrectangle(0, 0, WIDTH*UNIT - 1, HEIGHT*UNIT - 1);
	/*ZeroMemory用0来填充一块内存区域,原型为:
    void ZeroMemory([in] PVOID Destination,[in] SIZE_T Length);
    memset给字符串设置缓冲,原型为:
    void *memset( void *dest, int c, size_t count );*/
	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;


	//得到新方块
	CreateNewBlock();

}


void GameOver()
{

	//句柄
	HWND wnd = GetHWnd();
	/*HWND 是一个基本类型 和char int等同级别的 不过你可以把它当做long型去看待。
	它就想是身份证号一样,人生下来政府给发个身份证号,窗口创建系统就分配一个句柄,
	通过身份号 可以知道你的 姓名 住址、年龄,通过句柄也就能知道窗口类,窗口指针了*/

	
	if (MessageBox(wnd, _T("游戏结束。\n你想重来吗?"), _T("游戏结束"), MB_YESNO | MB_ICONQUESTION) == IDYES)
		NewGame();
	else
		Quit();
}

CMD GetCmd()
{
	while (1)
	{
		/*GetTickCount是函数。GetTickCount返回(retrieve)从操作系统启动所经过
		(elapsed)的毫秒数,它的返回值是DWORD。*/
		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 's':
			case 'S':return CMD_DOWN;
			case 'd':
			case 'D':return CMD_RIGHT;
			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 CreateNewBlock()
{
	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;//右移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:
		//设置填充颜色通过获取c的RGB值实现
		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 CheckBlockIsPut(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 (CheckBlockIsPut(tmp))
	{
		dx = 0;
		goto rotate;
	}
	tmp.x = g_CurBlock.x - 1;
	if (CheckBlockIsPut(tmp))
	{dx = -1;   goto rotate;}
	tmp.x = g_CurBlock.x + 1;
	if (CheckBlockIsPut(tmp)) 
	{ dx = 1;		goto rotate; }
	tmp.x = g_CurBlock.x - 2;
	if (CheckBlockIsPut(tmp)) 
	{ dx = -2;	goto rotate; }
	tmp.x = g_CurBlock.x + 2;
	if (CheckBlockIsPut(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 (CheckBlockIsPut(tmp))
	{
		DrawBlock(g_CurBlock, CLEAR);
		//左移只需x--;
		g_CurBlock.x--;
		DrawBlock(g_CurBlock);
	}
}

void OnRight()
{
	BLOCKINFO tmp = g_CurBlock;
	tmp.x++;
	if (CheckBlockIsPut(tmp))
	{
		DrawBlock(g_CurBlock, CLEAR);
		g_CurBlock.x++;
		DrawBlock(g_CurBlock);
	}
}

void OnDown()
{
	BLOCKINFO tmp = g_CurBlock;
	tmp.y--;
	if (CheckBlockIsPut(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 (CheckBlockIsPut(tmp))
	{
		g_CurBlock.y--;
		tmp.y--;
	}
	DrawBlock(g_CurBlock, FIX);
	//固定方块在游戏区

	WORD b = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];
	for (int i = 0; i < 16; i++, b <<= 1)
	{
		if (b & 0x8000)
		{
			if (g_CurBlock.y - i / 4 >= HEIGHT)
			{
				GameOver();
				return;
			}
			else
			{
				//修改地图为1,标记这点存在一个单位方块
				g_World[g_CurBlock.x + i % 4][g_CurBlock.y - i / 4] = 1;
			}
		}
	}

	BYTE mark = 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)
		{
			grades += 10;
			mark |= (1 << (g_CurBlock.y - y));
			setfillcolor(LIGHTGREEN);
			setlinecolor(LIGHTGREEN);
			setfillstyle(BS_HATCHED, HS_DIAGCROSS);//BS_HATCHED 图案填充。HS_DIAGCROSS  xxxxxxx图案填充 
			fillrectangle(0, (HEIGHT - y - 1)*UNIT + UNIT / 2 - 5, WIDTH*UNIT - 1, (HEIGHT - y - 1)*UNIT + UNIT / 2 + 5);
			setfillstyle(BS_SOLID);//BS_SOLID  固实填充。 

		}

	}
	if (mark)//如果产生整行消除
	{
		
		Sleep(300);
		//擦掉刚刚标记的行

		IMAGE img;
		for (int i = 0; i < 4; i++, mark >>= 1)
		{
			if (mark & 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);
			}
		}

		settextcolor(getbkcolor());
		outtextxy(20, -40, s);
		_stprintf(s, _T("你的得分是:   %d"), grades);
		settextcolor(WHITE);
		outtextxy(20, -40, s);
	}
	CreateNewBlock();
}



运行效果




写这个程序花了一上午,毕竟自己还是新手,才开始学easyX库,

所以写的有点慢,写好了下午运行时发现了几个BUG,

从1:00一直改到3:00,才把BUG改完,哎。


这个程序主要是音乐占用太大的空间60M左右.



程序下载地址

链接:http://pan.baidu.com/s/1kUXUjfL 密码:s35l


  • 2
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值