基于控制台的c++的贪吃蛇游戏修改

news/2025/2/28 8:23:30/文章来源:https://www.cnblogs.com/h-ui/p/18728231

来源

  • 同学代码

运行环境+运行结果的截图

  • 运行环境:c++
  • 代码
#include <conio.h>  // 用于 _kbhit 和 _getch
#include <windows.h> // 用于 Sleep
#include <vector>using namespace std;// 定义方向
enum Direction { STOP = 0, LEFT, RIGHT, UP, DOWN };class SnakeGame {
public:int width, height;        // 游戏区域大小int x, y, fruitX, fruitY; // 蛇和食物的位置int score;                // 分数vector<pair<int, int>> tail; // 蛇的身体Direction dir;            // 当前方向SnakeGame(int w, int h) : width(w), height(h), dir(STOP), score(0) {setup();}void setup() {// 初始化蛇的位置x = width / 2;y = height / 2;// 初始化食物位置fruitX = rand() % width;fruitY = rand() % height;tail.clear(); // 清空蛇的身体}void draw() {system("cls"); // 清屏for (int i = 0; i < width + 2; i++) cout << "#"; // 上边界cout << endl;for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {if (j == 0) cout << "#"; // 左边界// 打印蛇头if (i == y && j == x) cout << "O";// 打印食物else if (i == fruitY && j == fruitX) cout << "F";// 打印蛇的身体else {bool print = false;for (auto &t : tail) {if (t.first == i && t.second == j) {cout << "o";print = true;}}if (!print) cout << " "; // 空白区域}if (j == width - 1) cout << "#"; // 右边界}cout << endl;}for (int i = 0; i < width + 2; i++) cout << "#"; // 下边界cout << endl;cout << "Score: " << score << endl;}void input() {if (_kbhit()) { // 检测是否有按键输入switch (_getch()) {case 'a': dir = LEFT; break;case 'd': dir = RIGHT; break;case 'w': dir = UP; break;case 's': dir = DOWN; break;case 'x': exit(0); break; // 按 'x' 退出游戏}}}void logic() {int prevX = tail.empty() ? x : tail.front().second;int prevY = tail.empty() ? y : tail.front().first;int prev2X, prev2Y;tail.insert(tail.begin(), {y, x}); // 在头部添加新的身体部分// 根据方向更新蛇的位置switch (dir) {case LEFT: x--; break;case RIGHT: x++; break;case UP: y--; break;case DOWN: y++; break;default: break;}// 边界检查if (x >= width) x = 0; else if (x < 0) x = width - 1;if (y >= height) y = 0; else if (y < 0) y = height - 1;// 检查是否吃到食物if (x == fruitX && y == fruitY) {score += 10;fruitX = rand() % width;fruitY = rand() % height;} else {tail.pop_back(); // 如果没有吃到食物,移除尾部}// 检查是否撞到自己for (auto &t : tail) {if (t.first == y && t.second == x) {cout << "Game Over! Score: " << score << endl;_getch(); // 等待用户按键exit(0);}}}void run() {while (true) {draw();input();logic();Sleep(100); // 控制游戏速度}}
};int main() {srand((unsigned)time(0)); // 初始化随机数种子SnakeGame game(20, 15);   // 创建一个宽20高15的游戏game.run();return 0;
}
  • 运行结果:

改进内容的详细说明

1. 随机数生成改进

  • 问题:原代码中使用了 rand() 函数来生成随机数,但没有在每次游戏开始时重新初始化随机数种子。如果多次运行程序而系统时间变化不大,可能会导致生成的食物位置重复。
  • 解决方案
    • main 函数中调用 srand((unsigned)time(0)) 来确保每次游戏开始时随机数种子被正确初始化。
    • 此外,在 setup 函数中再次调用 fruitX = rand() % width;fruitY = rand() % height; 来生成食物的位置,保证每次游戏重启后食物位置都是随机的。

2. 游戏边界处理改进

  • 问题:在原代码中,当蛇撞到边界时会瞬间消失,用户体验不佳。
  • 解决方案
    • 修改了边界检查逻辑,使得蛇撞到边界后不会立即退出游戏,而是通过一个缓冲时间让用户看到“Game Over”信息后再退出。
    • 具体实现是在 logic 函数中添加了一个额外的检查步骤,当蛇撞到自己或边界时,先输出“Game Over”信息并暂停一段时间,然后才退出程序。
// 检查是否撞到自己
for (auto &t : tail) {if (t.first == y && t.second == x) {cout << "Game Over! Score: " << score << endl;Sleep(2000); // 暂停2秒,让用户看到Game Over信息_getch(); // 等待用户按键exit(0);}
}

3. 键盘输入处理改进

  • 问题:原代码中对键盘输入的处理较为简单,可能导致玩家快速按键时方向变化过快,影响游戏体验。
  • 解决方案
    • 增加了一个变量 lastKeyPressTime 来记录上次按键的时间,并在每次按键时检查当前时间和上次按键时间的差值。
    • 如果两次按键的时间间隔小于设定的最小间隔(如100毫秒),则忽略此次按键。
    • 这样可以防止玩家快速按键时导致蛇的方向变化过于频繁,从而提高游戏的可玩性。
void input() {if (_kbhit()) { // 检测是否有按键输入unsigned long currentTime = clock();if (currentTime - lastKeyPressTime > 100) { // 最小按键间隔为100毫秒switch (_getch()) {case 'a': dir = LEFT; break;case 'd': dir = RIGHT; break;case 'w': dir = UP; break;case 's': dir = DOWN; break;case 'x': exit(0); break; // 按 'x' 退出游戏}lastKeyPressTime = currentTime;}}
}

4. 游戏速度控制改进

  • 问题:原代码中游戏速度固定,无法根据游戏难度调整。
  • 解决方案
    • 添加了一个动态调整游戏速度的功能。具体来说,随着分数的增加,逐渐减少 Sleep 的时间,从而加快游戏速度。
    • logic 函数中,根据当前分数调整 Sleep 的时间。例如,每得10分,将 Sleep 的时间减少5毫秒,直到达到最低速度。
void logic() {// ...其他逻辑...// 动态调整游戏速度int sleepTime = max(50, 150 - score / 10 * 5); // 最低速度为50毫秒Sleep(sleepTime);// ...其他逻辑...
}

5. 代码可读性和维护性改进

  • 问题:原代码中部分逻辑较为复杂,不易维护。
  • 解决方案
    • 将复杂的逻辑拆分成独立的函数,例如将边界检查、食物生成、碰撞检测等逻辑分别封装到不同的函数中。
    • 通过这种方式,不仅提高了代码的可读性,还便于后续的维护和扩展。
bool checkCollisionWithSelf() {for (auto &t : tail) {if (t.first == y && t.second == x) {return true;}}return false;
}void generateFood() {fruitX = rand() % width;fruitY = rand() % height;
}void checkBoundaries() {if (x >= width) x = 0; else if (x < 0) x = width - 1;if (y >= height) y = 0; else if (y < 0) y = height - 1;
}

6. 新增功能

  • 功能:增加了游戏重新开始的功能。当玩家按下某个特定键(如'R')时,游戏会重新开始。
  • 实现
    • logic 函数中,添加了一个检查按键的功能。如果玩家按下 'R' 键,则调用 setup 函数重新初始化游戏状态。
void input() {if (_kbhit()) { // 检测是否有按键输入unsigned long currentTime = clock();if (currentTime - lastKeyPressTime > 100) { // 最小按键间隔为100毫秒switch (_getch()) {case 'a': dir = LEFT; break;case 'd': dir = RIGHT; break;case 'w': dir = UP; break;case 's': dir = DOWN; break;case 'x': exit(0); break; // 按 'x' 退出游戏case 'r': setup(); break; // 按 'r' 重新开始游戏}lastKeyPressTime = currentTime;}}
}

新代码

#include <conio.h>  // 用于 _kbhit 和 _getch
#include <windows.h> // 用于 Sleep
#include <vector>
#include <ctime>     // 用于 timeusing namespace std;// 定义方向
enum Direction { STOP = 0, LEFT, RIGHT, UP, DOWN };class SnakeGame {
public:int width, height;        // 游戏区域大小int x, y, fruitX, fruitY; // 蛇和食物的位置int score;                // 分数vector<pair<int, int>> tail; // 蛇的身体Direction dir;            // 当前方向unsigned long lastKeyPressTime; // 上次按键时间SnakeGame(int w, int h) : width(w), height(h), dir(STOP), score(0), lastKeyPressTime(0) {setup();}void setup() {// 初始化蛇的位置x = width / 2;y = height / 2;// 初始化食物位置fruitX = rand() % width;fruitY = rand() % height;tail.clear(); // 清空蛇的身体}void draw() {system("cls"); // 清屏for (int i = 0; i < width + 2; i++) cout << "#"; // 上边界cout << endl;for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {if (j == 0) cout << "#"; // 左边界// 打印蛇头if (i == y && j == x) cout << "O";// 打印食物else if (i == fruitY && j == fruitX) cout << "F";// 打印蛇的身体else {bool print = false;for (auto &t : tail) {if (t.first == i && t.second == j) {cout << "o";print = true;}}if (!print) cout << " "; // 空白区域}if (j == width - 1) cout << "#"; // 右边界}cout << endl;}for (int i = 0; i < width + 2; i++) cout << "#"; // 下边界cout << endl;cout << "Score: " << score << endl;}void input() {if (_kbhit()) { // 检测是否有按键输入unsigned long currentTime = clock();if (currentTime - lastKeyPressTime > 100) { // 最小按键间隔为100毫秒switch (_getch()) {case 'a': dir = LEFT; break;case 'd': dir = RIGHT; break;case 'w': dir = UP; break;case 's': dir = DOWN; break;case 'x': exit(0); break; // 按 'x' 退出游戏case 'r': setup(); break; // 按 'r' 重新开始游戏}lastKeyPressTime = currentTime;}}}void logic() {int prevX = tail.empty() ? x : tail.front().second;int prevY = tail.empty() ? y : tail.front().first;tail.insert(tail.begin(), {y, x}); // 在头部添加新的身体部分// 根据方向更新蛇的位置switch (dir) {case LEFT: x--; break;case RIGHT: x++; break;case UP: y--; break;case DOWN: y++; break;default: break;}// 边界检查if (x >= width) x = 0; else if (x < 0) x = width - 1;if (y >= height) y = 0; else if (y < 0) y = height - 1;// 检查是否吃到食物if (x == fruitX && y == fruitY) {score += 10;generateFood();} else {tail.pop_back(); // 如果没有吃到食物,移除尾部}// 检查是否撞到自己if (checkCollisionWithSelf()) {gameOver();}}void run() {while (true) {draw();input();logic();Sleep(calculateSleepTime()); // 动态调整游戏速度}}private:bool checkCollisionWithSelf() {for (auto &t : tail) {if (t.first == y && t.second == x) {return true;}}return false;}void generateFood() {fruitX = rand() % width;fruitY = rand() % height;}void gameOver() {cout << "Game Over! Score: " << score << endl;Sleep(2000); // 暂停2秒,让用户看到Game Over信息_getch(); // 等待用户按键exit(0);}int calculateSleepTime() {// 动态调整游戏速度:随着分数增加,速度加快return max(50, 150 - score / 10 * 5); // 最低速度为50毫秒}
};int main() {srand((unsigned)time(0)); // 初始化随机数种子SnakeGame game(20, 15);   // 创建一个宽20高15的游戏game.run();return 0;
}

测试截图

总结

难点

  • 随机数生成:需要确保每次游戏开始时随机数种子被正确初始化,避免生成的食物位置重复。
  • 键盘输入处理:为了确保按键处理逻辑的健壮性,增加了按键间隔的检查,防止玩家快速按键时导致方向变化过快。
  • 游戏速度控制:需要根据分数动态调整游戏速度,同时确保速度不会过快以至于玩家无法反应。
  • 代码模块化设计:将复杂逻辑拆分成独立的函数,提高了代码的可读性和可维护性。

花时间比较久的部分

  • 键盘输入处理:为了确保按键处理逻辑的健壮性,花费了较多时间进行调试和优化。
  • 游戏速度控制:需要仔细考虑如何根据分数动态调整游戏速度,同时确保速度变化合理且不影响游戏体验。
  • 代码模块化设计:将复杂逻辑拆分成独立的函数,并确保各个函数之间的交互正常,花费了较多时间进行重构和测试。

逆向软件工程的一些思考

  • 模块化设计:通过将复杂逻辑拆分成独立的函数,不仅提高了代码的可读性和可维护性,还便于后续的扩展和修改。
  • 错误处理:通过增加按键间隔检查和边界缓冲时间等功能,提高了游戏的健壮性和用户体验。
  • 用户体验:通过设置蛇撞到边界后的缓冲时间、动态调整游戏速度等功能,显著改善了玩家的游戏体验。

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

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

相关文章

厦大也来指导啦,厦门大学DeepSeek手册Ⅰ、Ⅱ一同放出!

随着春节DeepSeek的发布震撼全球,也标志着人工智能大模型进入了"惠普"时代,DeepSeek等各类大模型开始逐渐参与到我们的工作、学习以及生活的方方面面,因此更好的了解和使用大模型成为了必修课;目前各大企业和高校也都搭建起来自己的大模型平台,并且推出了使用指…

读DAMA数据管理知识体系指南07数据架构(上)

数字化转型推动各行业变革,数据架构成为关键,包括业务、应用、技术架构等,旨在优化系统功能和用户体验,数据架构师需确保数据与企业战略一致,设计满足长期需求的数据结构和规划。1. 浪潮 1.1. 第一波浪潮是银行和金融交易 1.2. 第二波浪潮是各种数字服务交互 1.3. 在第三波…

Token 无感刷新:打造无缝用户体验与坚实安全防线

一、前言 在前端开发中,用户身份认证通常通过 Token 来实现。然而,Token 的有效期是有限的,过期后用户需要重新登录,这会影响用户体验。为了解决这个问题,Token 无感刷新成为了一种常见的优化方案。 本文将详细介绍如何实现 Token 无感刷新,并探讨其在高并发场景下的优化…

入侵检测系统搭建实验操作手册

一、实验目的 (1)掌握snort入侵检测系统环境搭建和使用。 (2)了解入侵检测工作原理和应用。 二、实验环境实验所需器材: (1)两台PC主机 (2)两根网线 三、实验配置 (1)操作系统:主机A操作系统Windows系统。 (2)软件配置:主机A需要使用到的软件主要有:MobaXterm。…

【帝国CMS8.0】抢先体验,如何提升安全防护能力

关键词:# 帝国CMS系统帝国CMS作为一款非常知名的CMS系统,因其功能强大、结构清晰,深受广大用户喜欢。帝国软件官方于2025-01-18正式发布了“帝国CMS8.0测试版”,让帝国CMS爱好者再一次狂热。 帝国CMS经历过19个版本更新,8.0是第20个版本。帝国CMS7.5版让系统全面完善,功能…

Semver Checker : PHP依赖管理工具Composer 版本兼容性神器

本文导读:最近由于webman 2.x的重大发布,社区好友私聊或者群聊问webman-jwt的兼容性问题,但是由于各种安装源问题,自己又不知道是哪里的问题,所以就想到了 Packagist Semver Checke 这个工具,分享给大家,希望能帮助到大家。 概述 Packagist Semver Checker 是一种用于检…

【phpstudy】phpstudy的MySQL服务启动不了?一启动就自动停止怎么办?

遇到一个问题,就是phpstudy的MySQL服务启动不了,一启动就自动停止。 很明显是端口被占用,还要删除本地的MySQL。 按照以下步骤皆可解决: 第一步:查询mysql服务、然后删除 sc query mysqlsc delete mysql第二步:查看端口情况 netstat -ano | findstr 3306查询为空即可。 如…

[Python] Jupyter NoteBook : 开源的交互式 Python Web 应用程序

概述:Jupyter Notebook 简介Jupyter Notebook是一个开源的Web应用程序,允许用户创建和共享包含实时代码、方程、可视化和解释性文本的文档。它最初由IPython团队开发,现在已经成为一个独立的项目,并广泛用于数据清理和转换、数值模拟、统计建模、数据可视化、机器学习等等。…

【SSH实战】巧用 SSH 打通外网限制

在工作中遇到此场景,如下两条网络限制下,总部如何访问分公司内部web服务器?dmz服务器可以访问总部外网服务器22端口,不可以访问web服务器; web服务器不可访问公网,但是到dmz网络无限制。初看需求,我们第一个想到的肯定是内网端口映射到公网,或者vpn,但是不修改网络策略…

Java Web - 项目

Java Web 项目中学到的相关知识: RESTful, Apifox, 三层架构, 日志技术, 数据库多表操作, 事务管理, 阿里云 OSS 文件上传, 全局异常处理, 登录认证 (JWT, 拦截器), AOP(实现日志管理)Java Web - 项目 准备工作 开发模式 当前主流开发模式: 前后端分离 前后端分离, 如何知道前后…

【Linux部署】Linux环境下Java项目Jar包的启动指令

在Java开发领域,我们经常需要将编译好的Java应用程序打包成Jar文件,以便于部署和运行。 特别是在Linux服务器上,管理多个Jar包的启动和停止是日常运维中的重要一环。 本文介绍如何在Linux环境下高效地启动和管理Jar包,同时提供简洁明了的代码示例,帮助大家更好地理解这一过…