童年回忆!C语言实现俄罗斯方块~~(内附源码)

80、90的童鞋们
俄罗斯方块是否是你童年的专属游戏之一?

在这里插入图片描述

想不想现在就来一局?
让我们用C语言回顾童年的经典!
话不多说,上代码!

Visual Studio开发
需要EasyX,可自行百度下载

#include <stdio.h>
#include <graphics.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;  // 方块的颜色
};

BLOCK g_Blocks[7] =
{
    {0x0F00, 0x4444, 0x0F00, 0x4444, RED},     // 竖条形型            
    {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;       // 方块编号 
    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("W:旋转"));
    outtextxy(20, 370, _T("A:左移"));
    outtextxy(20, 390, _T("D:右移"));
    outtextxy(20, 410, _T("S:下移"));
    outtextxy(20, 430, _T("SPACE:沉底"));
    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;
                }
            }
        }
 
        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();
}
搜索、关注公众号超级宝爸的秘密实验室更多惊喜等着你!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值