#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