【附C++源码】从零开始实现 2048 游戏

【附C++源码】从零开始实现 2048 游戏

项目概述

2048 是一款经典的数字合并游戏,通过滑动方块使相同数字合并,目标是合成 2048。本文将详细介绍基于 EGE(Easy Graphics Engine)图形库实现的图形化版本。

核心技术架构

1. 游戏数据结构设计

cpp 复制代码
const int GRID_SIZE = 100;      // 网格尺寸
const int GRID_COUNT = 4;       // 4x4 网格
const int WIN_SIZE = GRID_SIZE * GRID_COUNT;
const int MARGIN = 10;          // 单元格间距
const int CELL_SIZE = GRID_SIZE - MARGIN * 2;

int board[4][4] = {0};         // 游戏棋盘
int score = 0;                 // 当前分数
bool gameOver = false;         // 游戏结束标志
bool gameWin = false;          // 胜利标志

设计要点

  • 使用二维数组 board[4][4] 存储棋盘状态,0 表示空位
  • 通过全局变量管理游戏状态,便于各函数访问

2. 视觉渲染系统

颜色映射机制
cpp 复制代码
color_t getNumberColor(int num) {
    switch(num) {
        case 0:     return EGERGB(205, 193, 180); // 空位灰
        case 2:     return EGERGB(238, 228, 218); // 浅黄
        case 4:     return EGERGB(237, 224, 200); // 米色
        case 8:     return EGERGB(242, 177, 121); // 橙色
        // ... 更多颜色映射
        default:    return EGERGB(60, 58, 50);    // 深色背景
    }
}

技术细节 :采用经典 2048 配色方案,通过 EGE 的 EGERGB 宏实现 RGB 颜色值转换。

棋盘绘制流程
cpp 复制代码
void drawGame() {
    // 1. 清空画布并设置背景色
    setbkcolor(EGERGB(187, 173, 160));
    cleardevice();
    
    // 2. 绘制每个单元格
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            int x = j * GRID_SIZE + MARGIN;
            int y = i * GRID_SIZE + MARGIN;
            int num = board[i][j];
            
            // 绘制单元格背景
            setfillcolor(getNumberColor(num));
            bar(x, y, x + CELL_SIZE, y + CELL_SIZE);
            
            // 绘制数字(非空时)
            if (num != 0) {
                // 根据数字大小动态调整字体
                if (num < 100) setfont(40, 0, "宋体");
                else if (num < 1000) setfont(36, 0, "宋体");
                else setfont(32, 0, "宋体");
                
                // 居中绘制
                outtextxy(x + (CELL_SIZE - textwidth(str)) / 2, 
                        y + (CELL_SIZE - textheight(str)) / 2, str);
            }
        }
    }
    
    // 3. 绘制分数和状态信息
    // ...
}

关键技术

  • 使用 bar() 函数绘制填充矩形作为单元格
  • 通过 textwidth()textheight() 实现文本居中
  • 根据数字位数动态调整字体大小,保证显示美观

3. 核心游戏逻辑

方块移动算法
cpp 复制代码
void mergeAndMoveLeft(int arr[4]) {
    // 阶段1: 压缩------将非零数字移到左侧
    int temp[4] = {0};
    int idx = 0;
    for (int i = 0; i < 4; i++) {
        if (arr[i] != 0) {
            temp[idx++] = arr[i];
        }
    }
    
    // 阶段2: 合并------相邻相同数字合并
    for (int i = 0; i < 3; i++) {
        if (temp[i] != 0 && temp[i] == temp[i + 1]) {
            temp[i] *= 2;
            score += temp[i];
            if (temp[i] == 2048) gameWin = true;
            // 移除被合并的数字
            for (int j = i + 1; j < 3; j++) {
                temp[j] = temp[j + 1];
            }
            temp[3] = 0;
        }
    }
    
    // 阶段3: 回写------将结果写回原数组
    for (int i = 0; i < 4; i++) {
        arr[i] = temp[i];
    }
}

算法思路:采用"压缩-合并-回写"三阶段处理,确保逻辑清晰。

方向转换策略
cpp 复制代码
void reverseRow(int arr[4]) {
    // 反转数组,用于右移操作
    int temp[4];
    for (int i = 0; i < 4; i++) temp[i] = arr[3 - i];
    for (int i = 0; i < 4; i++) arr[i] = temp[i];
}

void transpose() {
    // 矩阵转置,用于上下移动
    int temp[4][4];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            temp[i][j] = board[j][i];
    // 回写
}

设计模式:通过矩阵变换(反转、转置)将四个方向的移动统一为左移操作,显著减少代码重复。

游戏状态检测
cpp 复制代码
bool checkGameOver() {
    // 检查是否有空格
    if (countEmpty() > 0) return false;
    
    // 检查水平方向是否可合并
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == board[i][j + 1])
                return false;
        }
    }
    
    // 检查垂直方向是否可合并
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 3; i++) {
            if (board[i][j] == board[i + 1][j])
                return false;
        }
    }
    
    return true;
}

4. 输入处理系统

cpp 复制代码
int main() {
    initgraph(WIN_SIZE, WIN_SIZE + 60);  // 初始化图形窗口
    
    while (is_run()) {
        drawGame();
        
        // 键盘事件处理
        if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
            moveLeft();
            delay_ms(130);  // 防抖处理
        }
        // ... 其他方向键处理
        
        // 状态更新
        if (moved && boardChanged()) {
            addRandomNumber();
            gameOver = checkGameOver();
        }
    }
}

技术要点

  • 使用 GetAsyncKeyState() 实现非阻塞键盘检测
  • 通过 delay_ms() 防止按键抖动导致的多次触发
  • 合并检测 boardChanged() 避免无效操作

开发经验总结

1. 代码组织策略

将游戏逻辑分为三个层次:

  • 数据层:棋盘状态管理
  • 逻辑层:移动、合并、判断算法
  • 表现层:图形渲染、用户交互

2. EGE 库使用技巧

  • 颜色系统 :使用 EGERGB(r, g, b) 宏定义颜色,范围 0-255
  • 字体处理 :通过 setfont() 设置字体大小和样式
  • 文本居中 :利用 textwidth()textheight() 计算居中偏移

3. 性能优化考虑

  • 使用双缓冲机制(EGE 自动处理)
  • 避免不必要的重绘,仅在状态改变时更新
  • 合理使用 delay_fps() 控制帧率

编译部署

依赖环境

  • 编译器:MinGW-w64 (64位)
  • 图形库:EGE 20.08+
  • 系统:Windows

编译命令

bash 复制代码
g++ 2048.cpp -o 2048.exe -ID:\mingw64\include -LD:\mingw64\lib -lgraphics -lgdi32 -lgdiplus -limm32 -lmsimg32 -lole32 -loleaut32 -lwinmm -luuid

运行方式

直接执行生成的 2048.exe 文件即可启动游戏。

扩展建议

  1. 音效系统:添加合并成功、游戏胜利等音效
  2. 存档功能:支持游戏进度保存和加载
  3. 难度模式:增加不同网格大小(如 5x5、6x6)
  4. AI 玩家:实现自动游戏算法

⚠️项目源码地址:https://github.com/anjuxi/2048-C_programming_language

相关推荐
沃普天科技1 小时前
TYPE C全功能10G数据放大延长PS8353 PS8780 PS8778 8K60HZ
驱动开发·游戏·计算机外设·电脑·ar·硬件工程·vr
独自归家的兔2 小时前
OCPP 1.6 协议详解:GetLocalListVersion 获取本地列表版本指令
java·后端·物联网·spring·ocpp1.6
Apache RocketMQ3 小时前
RocketMQ源码解析——秒级定时消息介绍
java·云原生·消息队列·rocketmq·java-rocketmq
xiaoming00183 小时前
JAVA项目打包部署运维全流程(多服务、批量)
java·linux·运维
拾-光4 小时前
【Git】命令大全:从入门到高手,100 个最常用命令速查(2026 版)
java·大数据·人工智能·git·python·elasticsearch·设计模式
无人不xiao4 小时前
springBoot 实现 接口进度条
java·spring boot·后端
WiChP4 小时前
【V0.1B9】从零开始的2D游戏引擎开发之路
c++·游戏引擎
pkowner4 小时前
若依分页问题及解决方法
java·前端·算法
Peter·Pan爱编程5 小时前
从 struct 到 class:封装与访问控制的真正意义
c++