Dev-C++——winAPI贪吃蛇小游戏

发布于:2025-07-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

🚀欢迎互三👉:雾狩 💎💎
🚀关注博主,后期持续更新系列文章
🚀如果有错误感谢请大家批评指出,及时修改
🚀感谢大家点赞👍收藏⭐评论✍

今天水一篇吧……😂老样子,先上效果在这里插入图片描述

一、概述

本文将对“登录 + 贪吃蛇”游戏的代码进行详细剖析。该游戏实现了用户登录和注册功能,同时包含经典的贪吃蛇游戏玩法,还支持游戏存档和加载功能。代码使用了 C++ 语言,并结合了 Windows 操作系统的 API 函数,以实现窗口化的游戏界面和多媒体功能。

二、代码结构与功能模块划分

1. 头文件与命名空间

#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <conio.h>
#include <cwchar>
#include <cctype>
#include <limits>
#include <stdio.h>
#include <cstdlib>
#include <ctime>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")

using namespace std;
  • 包含了多个标准库和 Windows 相关的头文件,以实现输入输出、文件操作、随机数生成、时间处理和多媒体功能。
  • using namespace std; 允许直接使用标准库中的类和函数。

2. 结构体定义

// User结构体用于存储用户的基本信息
struct User {
    string username;
    string password;
};

// SaveData结构体用于存储游戏的存档信息
struct SaveData {
    string username;
    time_t playTime;
    int totalPlayTime;
    int length;
    int mode;
    vector<int> x;
    vector<int> y;
    int ax, ay;
    int dir;
    unsigned long long tk;
};
  • User 结构体用于存储用户的用户名和密码。
  • SaveData 结构体用于存储游戏的存档信息,包括用户名、存档时间、总游戏时长、蛇的长度、游戏模式、蛇的坐标、苹果的坐标、蛇的移动方向和游戏时间戳。

3. 全局变量定义

vector<User> users;
vector<SaveData> saves;
const string DATA_FILE = "users.dat";
const string SAVE_FILE = "saves.dat";
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

#define SIZE 30
#define WIDTH 20
#define FPS 10
#define SPEED 3

HWND m_hwnd;
int g_nWidth = WIDTH * SIZE + 10 + 300, g_nHeight = WIDTH * SIZE + 33;
int map[SIZE][SIZE];
std::vector<int> x, y;
int ax, ay;
int px, py;
int dir;
unsigned long long tk;
bool lock;
time_t startTime;
int currentMode;
int targetLength;
int currentSpeed;
string currentUsername;
bool isPaused = false;
bool isGameOver = false;
  • userssaves 分别存储所有用户信息和游戏存档信息。
  • DATA_FILESAVE_FILE 分别是用户数据文件和游戏存档文件的名称。
  • hConsole 用于控制控制台的输出。
  • 一系列宏定义了游戏地图的大小、方块宽度、帧率和初始速度。
  • 其他全局变量用于存储游戏的各种状态和信息。

4. 辅助函数

4.1 时间转换函数
string getTimeStr(time_t t) {
    char buffer[80];
    struct tm* timeinfo = localtime(&t);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
    return string(buffer);
}
  • 将时间戳转换为字符串格式,方便显示存档时间。
4.2 音乐播放函数
void playBackgroundMusic() {
    mciSendString("close background_music", NULL, 0, NULL);
    mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_music.mp3" type mpegvideo alias background_music)", NULL, 0, NULL);
    mciSendString("play background_music repeat", NULL, 0, NULL);
    isGameOver = false;
}

void playVictoryMusic() {
    mciSendString("close background_music", NULL, 0, NULL);
    mciSendString("close fail_music", NULL, 0, NULL);
    mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_victory.mp3" type mpegvideo alias victory_music)", NULL, 0, NULL);
    mciSendString("play victory_music", NULL, 0, NULL);
    isGameOver = true;
}

void playFailMusic() {
    mciSendString("close background_music", NULL, 0, NULL);
    mciSendString("close victory_music", NULL, 0, NULL);
    mciSendString(R"(open "D:\Dev-cpp 作品\登录+贪吃蛇\Snake_fail.mp3" type mpegvideo alias fail_music)", NULL, 0, NULL);
    mciSendString("play fail_music", NULL, 0, NULL);
    isGameOver = true;
}

void pauseAllMusic() {
    mciSendString("close background_music", NULL, 0, NULL);
    mciSendString("close victory_music", NULL, 0, NULL);
    mciSendString("close fail_music", NULL, 0, NULL);
}
  • 分别用于播放背景音乐、胜利音乐、失败音乐和暂停所有音乐,使用 mciSendString 函数进行多媒体操作。
4.3 文件操作函数
bool fileExists(const string& filename) {
    ifstream file(filename);
    return file.good();
}

bool loadUsers() {
    if (!fileExists(DATA_FILE)) {
        return true;
    }

    ifstream file(DATA_FILE, ios::binary);
    if (!file) {
        return false;
    }

    char header[4];
    file.read(header, 4);
    if (string(header, 4) != "USR1") {
        file.close();
        return false;
    }

    size_t count;
    file.read(reinterpret_cast<char*>(&count), sizeof(count));

    users.clear();
    for (size_t i = 0; i < count; i++) {
        User user;
        size_t len;

        file.read(reinterpret_cast<char*>(&len), sizeof(len));
        user.username.resize(len);
        file.read(&user.username[0], len);

        file.read(reinterpret_cast<char*>(&len), sizeof(len));
        user.password.resize(len);
        file.read(&user.password[0], len);

        users.push_back(user);
    }

    file.close();
    return true;
}

bool saveUsers() {
    ofstream file(DATA_FILE, ios::binary);
    if (!file) {
        return false;
    }

    const char* header = "USR1";
    file.write(header, 4);

    size_t count = users.size();
    file.write(reinterpret_cast<const char*>(&count), sizeof(count));

    for (const auto& user : users) {
        size_t len = user.username.length();
        file.write(reinterpret_cast<const char*>(&len), sizeof(len));
        file.write(user.username.c_str(), len);

        len = user.password.length();
        file.write(reinterpret_cast<const char*>(&len), sizeof(len));
        file.write(user.password.c_str(), len);
    }

    file.close();
    return true;
}
  • fileExists 用于判断文件是否存在。
  • loadUsers 用于加载用户数据,从二进制文件中读取用户信息。
  • saveUsers 用于保存用户数据,将用户信息写入二进制文件。
4.4 用户操作函数
bool registerUser(const string& username, const string& password) {
    for (char c : username) {
        if (!isalnum(c) && c != '_' && c != '.') {
            return false;
        }
    }

    User newUser;
    newUser.username = username;
    newUser.password = password;
    users.push_back(newUser);

    return saveUsers();
}

bool authenticateUser(const string& username, const string& password) {
    for (const auto& user : users) {
        if (user.username == username && user.password == password) {
            return true;
        }
    }
    return false;
}

string getPasswordInput() {
    string password;
    char ch;

    while ((ch = _getch()) != '\r') {
        if (ch == '\b') {
            if (!password.empty()) {
                password.pop_back();
                cout << "\b \b";
            }
        } else {
            password += ch;
            cout << '*';
        }
    }
    cout << endl;
    return password;
}
  • registerUser 用于用户注册,验证用户名的合法性并将新用户信息保存到文件中。
  • authenticateUser 用于用户登录验证,检查用户名和密码是否匹配。
  • getPasswordInput 用于获取用户输入的密码,输入时显示星号保护隐私。
4.5 其他辅助函数
void showMessage(const string& message, const string& title, UINT style) {
    MessageBox(NULL, message.c_str(), title.c_str(), style);
}

string trim(const string& str) {
    size_t first = str.find_first_not_of(" \t\n\r");
    if (first == string::npos)
        return "";
    size_t last = str.find_last_not_of(" \t\n\r");
    return str.substr(first, (last - first + 1));
}

void setConsoleColor(WORD color) {
    SetConsoleTextAttribute(hConsole, color);
}

void setConsoleCursorPosition(short x, short y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(hConsole, coord);
}
  • showMessage 用于显示消息框。
  • trim 用于去除字符串前后的空格。
  • setConsoleColor 用于设置控制台文本颜色。
  • setConsoleCursorPosition 用于设置控制台光标位置。

5. 游戏核心函数

5.1 初始化函数
void init() {
    srand((unsigned)time(NULL));
    tk = 0;
    dir = 2;
    x.push_back(0);
    y.push_back(0);
    px = py = 0;
    ax = -1;
    memset(map, 0, sizeof(map));
    map[0][0] = 1;
    lock = false;
    isGameOver = false;
}
  • 初始化游戏的各种状态,包括随机数种子、蛇的位置、苹果的位置和游戏地图等。
5.2 游戏结束处理函数
void gameover() {
    pauseAllMusic();
    playFailMusic();
    MessageBox(m_hwnd, "你输了", "游戏结束", MB_OK);
    x.clear();
    y.clear();
    init();
    startTime = time(NULL);
}

void youwin() {
    pauseAllMusic();
    playVictoryMusic();
    MessageBox(m_hwnd, "你赢了", "游戏结束", MB_OK);
    x.clear();
    y.clear();
    init();
    startTime = time(NULL);
}
  • gameover 处理游戏失败的情况,播放失败音乐,显示消息框并重新初始化游戏。
  • youwin 处理游戏胜利的情况,播放胜利音乐,显示消息框并重新初始化游戏。
5.3 蛇移动函数
void move(int d) {
    if (d == 0 && x[0] > 0) {
        x.insert(x.begin(), x[0] - 1);
        px = x[x.size() - 1];
        x.erase(x.begin() + x.size() - 1);
        y.insert(y.begin(), y[0]);
        py = y[y.size() - 1];
        y.erase(y.begin() + y.size() - 1);
    } else if (d == 0 && x[0] <= 0) gameover();
    else if (d == 2 && x[0] < SIZE - 1) {
        x.insert(x.begin(), x[0] + 1);
        px = x[x.size() - 1];
        x.erase(x.begin() + x.size() - 1);
        y.insert(y.begin(), y[0]);
        py = y[y.size() - 1];
        y.erase(y.begin() + y.size() - 1);
    } else if (d == 2 && x[0] >= SIZE - 1) gameover();
    else if (d == 1 && y[0] > 0) {
        x.insert(x.begin(), x[0]);
        px = x[x.size() - 1];
        x.erase(x.begin() + x.size() - 1);
        y.insert(y.begin(), y[0] - 1);
        py = y[y.size() - 1];
        y.erase(y.begin() + y.size() - 1);
    } else if (d == 1 && y[0] <= 0) gameover();
    else if (d == 3 && y[0] < SIZE - 1) {
        x.insert(x.begin(), x[0]);
        px = x[x.size() - 1];
        x.erase(x.begin() + x.size() - 1);
        y.insert(y.begin(), y[0] + 1);
        py = y[y.size() - 1];
        y.erase(y.begin() + y.size() - 1);
    } else if (d == 3 && y[0] >= SIZE - 1) gameover();
}
  • 根据蛇的移动方向更新蛇的位置,如果蛇撞到边界则游戏失败。
5.4 游戏状态更新函数
void update() {
    if (isPaused || isGameOver) return;
    if (tk % currentSpeed == 0) {
        move(dir);
        lock = false;
    }
    if (x[0] == ax && y[0] == ay) {
        x.push_back(px);
        y.push_back(py);
        ax = -1;
    }
    if (currentMode != 5 && x.size() >= static_cast<size_t>(targetLength)) {
        youwin();
        return;
    }
    if (x.size() >= SIZE * SIZE) {
        youwin();
        return;
    }
    memset(map, 0, sizeof(map));
    for (size_t i = 0; i < x.size(); i++) {
        if (map[x[i]][y[i]] == 0) map[x[i]][y[i]] = 1;
        else {
            gameover();
            return;
        }
    }
    if (ax == -1) {
        ax = rand() % SIZE;
        ay = rand() % SIZE;
        while (map[ax][ay] == 1) {
            ax = rand() % SIZE;
            ay = rand() % SIZE;
        }
    }
    map[ax][ay] = 2;
    tk++;
}
  • 更新游戏的状态,包括蛇的移动、吃苹果、判断游戏胜利或失败、生成新的苹果等。
5.5 窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    switch (Message) {
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        case WM_KEYDOWN: {
            if (!lock && !isGameOver) {
                if (wParam == VK_UP && (dir - 0) % 2 != 0) {
                    dir = 0;
                    lock = true;
                } else if (wParam == VK_DOWN && (dir - 2) % 2 != 0) {
                    dir = 2;
                    lock = true;
                } else if (wParam == VK_LEFT && (dir - 1) % 2 != 0) {
                    dir = 1;
                    lock = true;
                } else if (wParam == VK_RIGHT && (dir - 3) % 2 != 0) {
                    dir = 3;
                    lock = true;
                }
            }
            if (wParam == VK_ESCAPE) {
                int result = MessageBox(hwnd, "确定要退出游戏吗?", "确认退出", MB_YESNO | MB_ICONQUESTION);
                if (result == IDYES) {
                    PostQuitMessage(0);
                }
            }
            if (wParam == VK_SPACE) {
                isPaused = !isPaused;
                if (isPaused) {
                    pauseAllMusic();
                } else {
                    if (!isGameOver) {
                        playBackgroundMusic();
                    }
                }
            }
            if (wParam == VK_RETURN && isGameOver) {
                init();
                startTime = time(NULL);
                playBackgroundMusic();
            }
            break;
        }
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
  • 处理窗口的各种消息,包括窗口销毁、按键按下等。根据不同的按键操作,更新蛇的移动方向、暂停或继续游戏、退出游戏等。
5.6 游戏画面渲染函数
void render() {
    HDC hDC = GetDC(m_hwnd);
    HDC memDC = CreateCompatibleDC(0);
    HBITMAP bmpBack = CreateCompatibleBitmap(hDC, g_nWidth, g_nHeight);
    SelectObject(memDC, bmpBack);
    HPEN penBack = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
    SelectObject(memDC, penBack);
    HBRUSH brushBack = CreateSolidBrush(RGB(255, 255, 255));
    SelectObject(memDC, brushBack);
    RECT rcClient;
    GetClientRect(m_hwnd, &rcClient);
    FillRect(memDC, &rcClient, brushBack);
    HBRUSH brushHead = CreateSolidBrush(RGB(255, 0, 0));
    HBRUSH brushBody = CreateSolidBrush(RGB(0, 128, 0));
    HBRUSH brushApple = CreateSolidBrush(RGB(0, 0, 255));
    int dw = WIDTH;
    int rows = SIZE;
    int cols = SIZE;
    for (int r = 0; r < rows; ++r) {
        for (int c = 0; c < cols; ++c) {
            if (map[r][c] == 1) {
                if (r == x[0] && c == y[0]) {
                    SelectObject(memDC, brushHead);
                } else {
                    SelectObject(memDC, brushBody);
                }
            } else if (map[r][c] == 2) {
                SelectObject(memDC, brushApple);
            } else {
                SelectObject(memDC, brushBack);
            }
            Rectangle(memDC, c * dw, r * dw, (c + 1)*dw, (r + 1)*dw);
        }
    }
    time_t currentTime = time(NULL);
    int elapsedTime = static_cast<int>(currentTime - startTime);
    int hours = elapsedTime / 3600;
    int minutes = (elapsedTime % 3600) / 60;
    int seconds = elapsedTime % 60;
    char info[200];
    sprintf(info, "蛇的长度: %u\n游戏时长: %02d:%02d:%02d\n难度: %d\n目标长度: %d\n\n规则:\n",
            static_cast<unsigned>(x.size()), hours, minutes, seconds, currentMode, targetLength);
    switch (currentMode) {
        case 1:
            strcat(info, "简单模式:蛇的长度达到15即可通关。\n");
            break;
        case 2:
            strcat(info, "普通模式:蛇的长度达到30即可通关。\n");
            break;
        case 3:
            strcat(info, "困难模式:蛇的长度达到60即可通关。\n");
            break;
        case 4:
            strcat(info, "地狱模式:蛇的长度达到80即可通关。\n");
            break;
        case 5:
            strcat(info, "无尽模式:蛇占满全屏即可通关。\n");
            break;
    }
    strcat(info, "\n操作步骤:\n");
    strcat(info, "方向键:控制蛇的移动方向。\n");
    strcat(info, "Space键:暂停/继续游戏。\n");
    strcat(info, "Esc键:退出游戏。\n");
    if (isGameOver) {
        strcat(info, "\n游戏已结束,按Enter键重新开始。\n");
    }
    SetBkMode(memDC, TRANSPARENT);
    SetTextColor(memDC, RGB(0, 0, 0));
    RECT infoRect = {SIZE * WIDTH + 20, 20, g_nWidth - 20, g_nHeight - 20};
    DrawText(memDC, info, -1, &infoRect, DT_LEFT | DT_TOP);
    DeleteObject(brushHead);
    DeleteObject(brushBody);
    DeleteObject(brushApple);
    BitBlt(hDC, 0, 0, g_nWidth, g_nHeight, memDC, 0, 0, SRCCOPY);
    DeleteObject(penBack);
    DeleteObject(brushBack);
    DeleteObject(bmpBack);
    DeleteDC(memDC);
    ReleaseDC(m_hwnd, hDC);
}
  • 渲染游戏画面,包括绘制蛇、苹果、游戏地图和显示游戏信息。使用双缓冲技术,避免画面闪烁。
5.7 游戏循环线程函数
DWORD WINAPI startLoop(LPVOID) {
    while (1) {
        update();
        render();
        Sleep(1000 / FPS);
    }
    return 0L;
}
  • 游戏的主循环,不断更新游戏状态和渲染画面,通过 Sleep 函数控制游戏帧率。

6. 游戏模式和存档相关函数

6.1 选择游戏模式函数
int selectGameMode(const string&) {
    int mode;
    system("cls");
    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);
    setConsoleCursorPosition(15, 3);
    cout << "===== 选择游戏模式 =====" << endl;
    setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    setConsoleCursorPosition(15, 5);
    cout << "1. 简单模式 (长度达到15通关)" << endl;
    setConsoleCursorPosition(15, 6);
    cout << "2. 普通模式 (长度达到30通关)" << endl;
    setConsoleCursorPosition(15, 7);
    cout << "3. 困难模式 (长度达到60通关)" << endl;
    setConsoleCursorPosition(15, 8);
    cout << "4. 地狱模式 (长度达到80通关)" << endl;
    setConsoleCursorPosition(15, 9);
    cout << "5. 无尽模式 (占满全屏通关)" << endl;
    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);
    setConsoleCursorPosition(15, 11);
    cout << "=======================" << endl;
    setConsoleCursorPosition(15, 13);
    cout << "请选择: ";
    while (!(cin >> mode) || mode < 1 || mode > 5) {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        showMessage("输入无效,请输入1 - 5之间的数字!", "错误", MB_ICONERROR);
        setConsoleCursorPosition(15, 13);
        cout << "请选择: ";
    }
    cin.ignore();
    return mode;
}
  • 让用户选择游戏模式,根据用户输入返回相应的模式编号。
6.2 选择游戏存档函数
int selectSaveGame(const string& username) {
    vector<SaveData> userSaves;
    if (fileExists(SAVE_FILE)) {
        ifstream file(SAVE_FILE, ios::binary);
        if (file) {
            char header[4];
            file.read(header, 4);
            if (string(header, 4) == "SAV1") {
                size_t count;
                file.read(reinterpret_cast<char*>(&count), sizeof(count));
                saves.clear();
                for (size_t i = 0; i < count; i++) {
                    SaveData save;
                    size_t len;
                    file.read(reinterpret_cast<char*>(&len), sizeof(len));
                    save.username.resize(len);
                    file.read(&save.username[0], len);
                    file.read(reinterpret_cast<char*>(&save.playTime), sizeof(save.playTime));
                    file.read(reinterpret_cast<char*>(&save.totalPlayTime), sizeof(save.totalPlayTime));
                    file.read(reinterpret_cast<char*>(&save.length), sizeof(save.length));
                    file.read(reinterpret_cast<char*>(&save.mode), sizeof(save.mode));
                    file.read(reinterpret_cast<char*>(&len), sizeof(len));
                    save.x.resize(len);
                    for (int& val : save.x) {
                        file.read(reinterpret_cast<char*>(&val), sizeof(val));
                    }
                    file.read(reinterpret_cast<char*>(&len), sizeof(len));
                    save.y.resize(len);
                    for (int& val : save.y) {
                        file.read(reinterpret_cast<char*>(&val), sizeof(val));
                    }
                    file.read(reinterpret_cast<char*>(&save.ax), sizeof(save.ax));
                    file.read(reinterpret_cast<char*>(&save.ay), sizeof(save.ay));
                    file.read(reinterpret_cast<char*>(&save.dir), sizeof(save.dir));
                    file.read(reinterpret_cast<char*>(&save.tk), sizeof(save.tk));
                    saves.push_back(save);
                }
            }
            file.close();
        }
    }
    for (const auto& save : saves) {
        if (save.username == username) {
            userSaves.push_back(save);
        }
    }
    if (userSaves.empty()) {
        showMessage("没有可用的存档,将开始新游戏。", "提示", MB_ICONINFORMATION);
        return -1;
    }
    system("cls");
    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);
    setConsoleCursorPosition(15, 3);
    cout << "===== 选择存档 =====" << endl;
    for (size_t i = 0; i < userSaves.size(); i++) {
        setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
        setConsoleCursorPosition(15, static_cast<short>(5 + i));
        cout << i + 1 << ". 存档时间: " << getTimeStr(userSaves[i].playTime)
             << " 游玩总时长: " << userSaves[i].totalPlayTime << " 秒"
             << " 长度: " << userSaves[i].length
             << " 模式: " << userSaves[i].mode << endl;
    }
    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);
    setConsoleCursorPosition(15, static_cast<short>(6 + userSaves.size()));
    cout << "0. 开始新游戏" << endl;
    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);
    setConsoleCursorPosition(15, static_cast<short>(8 + userSaves.size()));
    cout << "=======================" << endl;
    setConsoleCursorPosition(15, static_cast<short>(10 + userSaves.size()));
    cout << "请选择: ";
    int choice;
    while (!(cin >> choice) || choice < 0 || static_cast<size_t>(choice) > userSaves.size()) {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        showMessage("输入无效,请输入正确的存档编号!", "错误", MB_ICONERROR);
        setConsoleCursorPosition(15, static_cast<short>(10 + userSaves.size()));
        cout << "请选择: ";
    }
    cin.ignore();
    if (choice == 0) {
        return -1;
    } else {
        x = userSaves[choice - 1].x;
        y = userSaves[choice - 1].y;
        ax = userSaves[choice - 1].ax;
        ay = userSaves[choice - 1].ay;
        dir = userSaves[choice - 1].dir;
        tk = userSaves[choice - 1].tk;
        currentMode = userSaves[choice - 1].mode;
        startTime = time(NULL) - userSaves[choice - 1].totalPlayTime;
        switch (currentMode) {
            case 1: targetLength = 15; currentSpeed = 5; break;
            case 2: targetLength = 30; currentSpeed = 3; break;
            case 3: targetLength = 60; currentSpeed = 2; break;
            case 4: targetLength = 80; currentSpeed = 1; break;
            case 5: targetLength = SIZE * SIZE; currentSpeed = 3; break;
        }
        return choice - 1;
    }
}
  • 让用户选择游戏存档,如果有可用存档,显示存档信息供用户选择;如果没有可用存档,提示用户开始新游戏。选择存档后,加载存档信息。
6.3 保存游戏存档函数
void saveGame(const string& username) {
    int result = MessageBox(m_hwnd, "是否要保存存档?", "保存存档", MB_YESNO | MB_ICONQUESTION);
    if (result == IDYES) {
        time_t currentTime = time(NULL);
        int elapsedTime = static_cast<int>(currentTime - startTime);
        SaveData save;
        save.username = username;
        save.playTime = currentTime;
        save.totalPlayTime = elapsedTime;
        save.length = static_cast<int>(x.size());
        save.mode = currentMode;
        save.x = x;
        save.y = y;
        save.ax = ax;
        save.ay = ay;
        save.dir = dir;
        save.tk = tk;
        for (auto it = saves.begin(); it != saves.end(); ) {
            if (it->username == username) {
                it = saves.erase(it);
            } else {
                ++it;
            }
        }
        saves.push_back(save);
        // 后续应添加将saves保存到文件的代码
    }
}
  • 询问用户是否保存存档,如果用户选择是,创建一个新的存档对象并保存到 saves 向量中,同时删除该用户的旧存档。

三、完整代码

下载地址
解压后,跟着操作步骤.txt下载源代码

四、总结

该代码实现了一个功能丰富的登录 + 贪吃蛇游戏,包括用户注册、登录、游戏存档和加载、多种游戏模式等功能。通过合理的结构体定义和函数封装,代码具有较好的模块化和可维护性。同时,使用 Windows API 实现了窗口化的游戏界面和多媒体功能,提升了游戏的用户体验。不过,代码中保存游戏存档的部分还未完成将 saves 向量保存到文件的操作,需要进一步完善。


网站公告

今日签到

点亮在社区的每一天
去签到