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

在这里插入图片描述

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

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

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

一、概述

本文将对“登录 + 贪吃蛇”游戏的代码进行详细剖析。该游戏实现了用户登录和注册功能,同时包含经典的贪吃蛇游戏玩法,还支持游戏存档和加载功能。代码使用了 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 3HWND 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下载源代码(包含有音乐和无音乐版本)
无音乐版本的代码如下,大家可以先试着玩玩看

#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>using namespace std;// 用户结构
struct User {string username;string password;
};// 存档结构
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;
};// 全局变量
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    // 指定游戏地图大小为30*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]; // 地图数组,值为0表示空,1表示蛇身,2表示苹果
std::vector<int> x, y; // 用于保存蛇身体每一个部位的位置
int ax, ay; // 苹果的位置
int px, py; // 最后一次蛇经过的位置,用于吃过苹果后加长蛇身
int dir; // 表明当前方向
unsigned long long tk; // 游戏ticket数,即已经过多少逻辑帧
bool lock; // 操作锁,防止一次刷新操作两次
time_t startTime; // 游戏开始时间
int currentMode; // 当前游戏模式
int targetLength; // 目标长度
int currentSpeed; // 当前游戏速度
string currentUsername; // 当前登录用户名
bool isPaused = false; // 暂停标志// 函数声明
bool loadUsers();
bool saveUsers();
bool registerUser(const string& username, const string& password);
bool authenticateUser(const string& username, const string& password);
string getPasswordInput();
void showMessage(const string& message, const string& title = "消息", UINT style = MB_OK);
bool fileExists(const string& filename);
string trim(const string& str);
void setConsoleColor(WORD color);
void setConsoleCursorPosition(short x, short y);
void init(); // 初始化参数
void gameover(); // 输了时调用
void youwin(); // 赢了时调用
void move(int d); // 调用次函数以移动
void update(); // 游戏更新主逻辑,每帧调用此函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
void render(); // 渲染函数,用于绘制游戏图像
DWORD WINAPI startLoop(LPVOID lpParamter); // 开始主循环
int selectGameMode(const string& username);
int selectSaveGame(const string& username);
void saveGame(const string& username);
string getTimeStr(time_t t);int main() {// 初始化:加载用户数据if (!loadUsers()) {showMessage("无法加载用户数据!程序将使用空用户数据库。", "错误", MB_ICONERROR);}int choice;bool exitProgram = false;bool loggedIn = false;while (!exitProgram) {system("cls"); // 清屏// 设置标题颜色为青色setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 3);cout << "===== 用户管理系统 =====" << endl;// 设置菜单选项颜色为白色setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(18, 5);cout << "1. 登录" << endl;setConsoleCursorPosition(18, 6);cout << "2. 注册" << endl;setConsoleCursorPosition(18, 7);cout << "0. 退出" << endl;setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 9);cout << "=======================" << endl;setConsoleCursorPosition(15, 11);cout << "请选择: ";while (!(cin >> choice)) {cin.clear();cin.ignore(numeric_limits<streamsize>::max(), '\n');showMessage("输入无效,请输入数字选项!", "错误", MB_ICONERROR);setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(15, 11);cout << "请选择: ";}cin.ignore(); // 清除输入缓冲区switch (choice) {case 1: { // 登录string username, password;setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(15, 14);cout << "用户名: ";getline(cin, username);username = trim(username);setConsoleCursorPosition(15, 15);cout << "密码: ";password = getPasswordInput();password = trim(password);if (authenticateUser(username, password)) {string welcomeMsg = "用户" + username + "欢迎您";showMessage(welcomeMsg, "欢迎", MB_ICONINFORMATION);loggedIn = true;currentUsername = username;exitProgram = true;} else {showMessage("用户名或密码错误!", "错误", MB_ICONERROR);system("pause");}break;}case 2: { // 注册string username, password, confirmPassword;bool registered = false;while (!registered) {system("cls");setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 3);cout << "===== 用户注册 =====" << endl;setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);setConsoleCursorPosition(15, 5);cout << "新用户名: ";getline(cin, username);username = trim(username);if (username.empty()) {showMessage("用户名不能为空!", "错误", MB_ICONERROR);system("pause");continue;}bool userExists = false;for (const auto& user : users) {if (user.username == username) {userExists = true;break;}}if (userExists) {showMessage("用户名已存在!", "错误", MB_ICONERROR);system("pause");continue;}bool validPassword = false;while (!validPassword) {setConsoleCursorPosition(15, 7);cout << "密码(至少6个字符): ";password = getPasswordInput();password = trim(password);if (password.length() < 6) {showMessage("密码长度至少需要6个字符!请重新输入。", "提示", MB_ICONINFORMATION);system("pause");continue;}setConsoleCursorPosition(15, 8);cout << "确认密码: ";confirmPassword = getPasswordInput();confirmPassword = trim(confirmPassword);if (password != confirmPassword) {showMessage("两次输入的密码不匹配!请重新输入。", "错误", MB_ICONERROR);system("pause");continue;}validPassword = true;}if (registerUser(username, password)) {showMessage("注册成功!请使用新账户登录。", "成功", MB_ICONINFORMATION);registered = true;system("pause");} else {showMessage("注册失败!可能是用户名包含非法字符。", "错误", MB_ICONERROR);system("pause");}}break;}case 0: // 退出setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN);setConsoleCursorPosition(15, 14);cout << "感谢使用!再见。" << endl;Sleep(2000);system("cls");cout << "正在退出...";Sleep(1000);exitProgram = true;break;default:showMessage("无效的选择,请重试!", "错误", MB_ICONERROR);system("pause");}}if (loggedIn) {int saveIndex = selectSaveGame(currentUsername);if (saveIndex == -1) {currentMode = selectGameMode(currentUsername);}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;}WNDCLASSEX wc; /* A properties struct of our window */HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */MSG msg; /* A temporary location for all messages *//* zero out the struct and set the stuff we want to modify */memset(&wc, 0, sizeof(wc));wc.cbSize = sizeof(WNDCLASSEX);wc.lpfnWndProc = WndProc; /* This is where we will send messages to */wc.hInstance = GetModuleHandle(NULL);wc.hCursor = LoadCursor(NULL, IDC_ARROW);/* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wc.lpszClassName = "WindowClass";wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */if (!RegisterClassEx(&wc)) {MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);return 0;}hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WindowClass", "贪吃蛇游戏,按ESC退出", WS_VISIBLE | WS_EX_STATICEDGE,CW_USEDEFAULT, /* x */CW_USEDEFAULT, /* y */g_nWidth, /* width */g_nHeight, /* height */NULL, NULL, GetModuleHandle(NULL), NULL);if (hwnd == NULL) {MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);return 0;}m_hwnd = hwnd;init(); // 初始化startTime = time(NULL); // 记录游戏开始时间HANDLE hThread = CreateThread(NULL, 0, startLoop, NULL, 0, NULL); // 创建线程CloseHandle(hThread); // 释放线程句柄/*This is the heart of our program where all input is processed andsent to WndProc. Note that GetMessage blocks code flow until it receives something, sothis loop will not produce unreasonably high CPU usage*/while (GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */TranslateMessage(&msg); /* Translate key codes to chars if present */DispatchMessage(&msg); /* Send it to WndProc */}saveGame(currentUsername);return msg.wParam;}return 0;
}// 检查文件是否存在
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;
}// 注册新用户
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;
}// 显示消息对话框
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);
}// 初始化参数
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;
}// 输了时调用
void gameover() {MessageBox(m_hwnd, "你输了", "游戏结束", MB_OK); // 弹窗x.clear(); // 重新初始化y.clear();init();startTime = time(NULL); // 重置游戏开始时间
}// 赢了时调用
void youwin() {MessageBox(m_hwnd, "你赢了", "游戏结束", MB_OK);x.clear();y.clear();init();startTime = time(NULL); // 重置游戏开始时间
}// 调用次函数以移动
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();
}// 游戏更新主逻辑,每帧调用此函数
void update() {if (isPaused) return; // 如果暂停,不进行更新if (tk % currentSpeed == 0) { // 每隔currentSpeed间隔帧移动一次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++;
}/* 此函数用于处理窗口接受的所有消息 */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {switch (Message) {/* Upon destruction, tell the main thread to stop */case WM_DESTROY: {PostQuitMessage(0);break;}case WM_KEYDOWN: { // 当收到按键消息if (!lock) { // 且操作锁没有锁上,则改变蛇的移动方向并上锁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; // 切换暂停状态}break;}/* All other messages (a lot of them) are processed using default procedures */default:return DefWindowProc(hwnd, Message, wParam, lParam);}return 0;
}// 渲染函数,用于绘制游戏图像
void render() {HDC hDC = GetDC(m_hwnd); // 定义窗口句柄的DCHDC memDC = CreateCompatibleDC(0); // 定义兼容DCHBITMAP 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, "蛇的长度: %zu\n游戏时长: %02d:%02d:%02d\n难度: %d\n目标长度: %d\n\n规则:\n",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");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);
}// 开始主循环,定义这个函数是为了多线程运行,函数名随意,函数格式一定
DWORD WINAPI startLoop(LPVOID /*lpParamter*/) {while (1) {update();render();Sleep(1000 / FPS);}return 0L;
}// 选择游戏模式
int selectGameMode(const string& /*username*/) {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;
}// 选择存档
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;}
}// 保存游戏
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);ofstream file(SAVE_FILE, ios::binary);if (file) {const char* header = "SAV1";file.write(header, 4);size_t count = saves.size();file.write(reinterpret_cast<const char*>(&count), sizeof(count));for (const auto& s : saves) {size_t len = s.username.length();file.write(reinterpret_cast<const char*>(&len), sizeof(len));file.write(s.username.c_str(), len);file.write(reinterpret_cast<const char*>(&s.playTime), sizeof(s.playTime));file.write(reinterpret_cast<const char*>(&s.totalPlayTime), sizeof(s.totalPlayTime));file.write(reinterpret_cast<const char*>(&s.length), sizeof(s.length));file.write(reinterpret_cast<const char*>(&s.mode), sizeof(s.mode));len = s.x.size();file.write(reinterpret_cast<const char*>(&len), sizeof(len));for (int val : s.x) {file.write(reinterpret_cast<const char*>(&val), sizeof(val));}len = s.y.size();file.write(reinterpret_cast<const char*>(&len), sizeof(len));for (int val : s.y) {file.write(reinterpret_cast<const char*>(&val), sizeof(val));}file.write(reinterpret_cast<const char*>(&s.ax), sizeof(s.ax));file.write(reinterpret_cast<const char*>(&s.ay), sizeof(s.ay));file.write(reinterpret_cast<const char*>(&s.dir), sizeof(s.dir));file.write(reinterpret_cast<const char*>(&s.tk), sizeof(s.tk));}file.close();}}
}// 获取北京时间字符串
string getTimeStr(time_t t) {struct tm* tm_info;tm_info = localtime(&t);char buffer[26];strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);return string(buffer);
}

四、总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92249.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【openbmc6】entity-manager

文章目录 2.1 事件监听:dbus在linux上使用的底层通信方式多半是unix domain socket ,事件的到来可被抽象为:socket上有数据,可读 2.2 事件处理:由于主线程肯定有逻辑得跑,因此新开一个线程甚至多个线程专门用来监听和处理事件,但存在多线程就意味着可能存在竞争,存在竞…

Java 实现 UDP 多发多收通信

在网络通信领域&#xff0c;UDP&#xff08;用户数据报协议&#xff09;以其无连接、高效率的特点&#xff0c;在实时通信场景中占据重要地位。本文将结合一段实现 UDP 多发多收的 Java 代码&#xff0c;详细解析其实现逻辑&#xff0c;帮助开发者深入理解 UDP 通信的底层逻辑与…

Java学习第六十二部分——Git

目录 一、关键概述 二、核心概念 三、常用命令 四、优势因素 五、应用方案 六、使用建议 一、关键概述 提问&#xff1a;Git 是什么&#xff1f; 回答&#xff1a;一句话&#xff0c;分布式版本控制系统&#xff08;DVCS&#xff09;&#xff0c;用来跟踪文件&#…

CDN和DNS 在分布式系统中的作用

一、DNS&#xff1a;域名系统&#xff08;Domain Name System&#xff09; 1. 核心功能 DNS是互联网的“地址簿”&#xff0c;负责将人类易记的域名&#xff08;如www.baidu.com&#xff09;解析为计算机可识别的IP地址&#xff08;如180.101.50.242&#xff09;。没有DNS&…

uniapp用webview导入本地网页,ios端打开页面空白问题

目前还没解决&#xff0c;DCloud官方也说不行 IOS下webview加载本地网页时&#xff0c;无法加载资源 - DCloud问答

软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(8)

接前一篇文章:软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(7) 所属章节: 第15章. 面向服务架构设计理论与实践 第3节 SOA的参考架构 15.3 SOA的参考架构 IBM的Websphere业务集成参考架构(如图15-2所示,以下简称参考架构)是典型的以服务为中心的企业集…

基于 Docker 及 Kubernetes 部署 vLLM:开启机器学习模型服务的新篇章

在当今数字化浪潮中&#xff0c;机器学习模型的高效部署与管理成为众多开发者和企业关注的焦点。vLLM 作为一款性能卓越的大型语言模型推理引擎&#xff0c;其在 Docker 及 Kubernetes 上的部署方式如何呢&#xff1f;本文将深入探讨如何在 Docker 及 Kubernetes 集群中部署 vL…

工业互联网六大安全挑战的密码“解法”

目录 工业互联网密码技术应用Q&A Q1&#xff1a;设备身份认证与接入控制 Q2&#xff1a;通信数据加密与完整性保护 Q3&#xff1a;远程安全访问 Q4&#xff1a;平台与数据安全 Q5&#xff1a;软件与固件安全 Q6&#xff1a;日志审计与抗抵赖 首传信安-解决方案 总…

基于springboot的在线问卷调查系统的设计与实现(源码+论文)

一、开发环境 1 Java语言 Java语言是当今为止依然在编程语言行业具有生命力的常青树之一。Java语言最原始的诞生&#xff0c;不仅仅是创造者感觉C语言在编程上面很麻烦&#xff0c;如果只是专注于业务逻辑的处理&#xff0c;会导致忽略了各种指针以及垃圾回收这些操作&#x…

民法学学习笔记(个人向) Part.1

民法学学习笔记(个人向) Part.1有关民法条文背后的事理、人心、经济社会基础&#xff1b;民法的结构民法学习的特色就是先学最难的民法总论&#xff0c;再学较难的物权法、合同法等&#xff0c;最后再学习最简单的婚姻、继承、侵权部分。这是一个由难到易的过程&#xff0c;尤为…

ElasticSearch Doc Values和Fielddata详解

一、Doc Values介绍倒排索引在搜索包含指定 term 的文档时效率极高&#xff0c;但在执行相反操作&#xff0c;比如查询一个文档中包含哪些 term&#xff0c;以及进行排序、聚合等与指定字段相关的操作时&#xff0c;表现就很差了&#xff0c;这时候就需要用到 Doc Values。倒排…

【C语言】解决VScode中文乱码问题

文章目录【C语言】解决VScode中文乱码问题弹出无法写入用户设置的处理方法弹出无法在只读编辑器编辑的问题处理方法【C语言】解决VScode中文乱码问题 &#x1f4ac;欢迎交流&#xff1a;在学习过程中如果你有任何疑问或想法&#xff0c;欢迎在评论区留言&#xff0c;我们可以共…

MySQL笔记4

一、范式1.概念与意义范式&#xff08;Normal Form&#xff09;是数据库设计需遵循的规范&#xff0c;解决“设计随意导致后期重构困难”问题。主流有 三大范式&#xff08;1NF、2NF、3NF&#xff09;&#xff0c;还有进阶的 BCNF、4NF、5NF 等&#xff0c;范式间是递进依赖&am…

切比雪夫不等式的理解以及推导【超详细笔记】

文章目录参考教程一、意义1. 正态分布的 3σ 法则2. 不等式的含义3. 不等式的意义二、不等式的证明1. 马尔科夫不等式马尔可夫不等式证明(YYY 为非负随机变量 &#xff09;2. 切比雪夫不等式推导参考教程 一个视频&#xff0c;彻底理解切比雪夫不等式 一、意义 1. 正态分布的…

Spring Boot Jackson 序列化常用配置详解

一、引言在当今的 Web 开发领域&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;已然成为数据交换的中流砥柱。无论是前后端分离架构下前后端之间的数据交互&#xff0c;还是微服务架构里各个微服务之间的通信&#xff0c;JSON 都承担着至关重要的角色 。它…

Jetpack ViewModel LiveData:现代Android架构组件的核心力量

引言在Android应用开发中&#xff0c;数据管理和界面更新一直是开发者面临的重大挑战。传统的开发方式常常导致Activity和Fragment变得臃肿&#xff0c;难以维护&#xff0c;且无法优雅地处理配置变更&#xff08;如屏幕旋转&#xff09;。Jetpack中的ViewModel和LiveData组件正…

Python数据分析案例79——基于征信数据开发信贷风控模型

背景 虽然模型基本都是表格数据那一套了&#xff0c;算法都没什么新鲜点&#xff0c;但是本次数据还是很值得写个案例的&#xff0c;有征信数据&#xff0c;各种&#xff0c;个人&#xff0c;机构&#xff0c;逾期汇总..... 这么多特征来做机器学习模型应该还不错。本次带来&…

板凳-------Mysql cookbook学习 (十二--------3_2)

3.3链接表 结构 P79页 用一个类图来表示EmployeeNode类的结构&#xff0c;展示其属性和关系&#xff1a; plaintext ----------------------------------------- | EmployeeNode | ----------------------------------------- | - emp_no: int …

深度学习图像预处理:统一输入图像尺寸方案

在实际训练中&#xff0c;最常见也最简单的做法&#xff0c;就是在送入网络前把所有图片「变形」到同一个分辨率&#xff08;比如 256256 或 224224&#xff09;&#xff0c;或者先裁剪&#xff0f;填充成同样大小。具体而言&#xff0c;可以分成以下几类方案&#xff1a;一、图…

pytest-log

问题1&#xff1a;我们在运行测试用例的时候如何记录测试的log&#xff0c;如何使用&#xff1f;问题2&#xff1a;我写的函数&#xff0c;为了方便log记录&#xff0c;但是在pytest运行时&#xff0c;会兼容pytest且不会重复记录&#xff0c;怎么解决&#xff1f;1、pytest有内…