c++经典项目控制台贪吃蛇小游戏详细教程

贪吃蛇GreedySnake

本文将讲解如何使用c++面向对象方法编写控制台版贪吃蛇小游戏,项目github地址:silence1772/GreedySnake
游戏下载:GreedySnake
本人属初学者,水平所限,难免有所错误及不妥之处,劳请指出或发表意见,本人定当及时加以改正。

  • 本文所有代码在code::blocks使用c++11标准编译通过,未测试在其它环境下使用情况

游戏截图

开始界面
游戏结束
开始动画:
开始动画
游戏过程:
游戏过程

游戏架构设计

该游戏的玩法简单,玩家通过键盘方向键控制蛇上下左右移动,吃到食物得分并增长,碰到墙或者自己的身体则死亡,游戏结束。
整个游戏其实就是一个无穷的循环,直到退出游戏时退出循环。我们暂且将这个循环称为一级循环,这个循环包含游戏开始动画,游戏难度选择,游戏过程这三个子模块,其中游戏过程这个模块亦是一个循环,我们暂且将其称为二级循环。它们之间的关系大致如下图:
这里写图片描述
现在我们根据上图进行细化,对各个模块的实现进行简单描述。

1.游戏开始动画

开始动画的实现主要依靠对点的操作来实现,这里我们先建立一个概念,就是将控制台界面看成一个原点在左上角的坐标系,一个点(x,y)表示坐标系中的一个格子,如下图所示:
这里写图片描述
我们的开始动画是由一条蛇和一行文字从左到右移动而成,这里我们先单独讨论一下蛇,要达到移动的效果,我采取的策略是将整个过程分为三部分:
第一部分为蛇从左边开始出现到整个身体完全出现
第二部分为蛇身整体从左移动到接触右边界的过程
第三部分为蛇从接触右边界到完全消失的过程
这里写图片描述

我们先来看一下第一部分,这一部分的实现首先是建立一个deque双端队列,用于存储点的对象,这些点就是组成蛇身的元素,然后再用一个for循环将容器中的点依次打印出来,每打印一个点停顿一会,这样就达到了移动的效果。全部打印完后就到了第二部分,这部分蛇的每次前进都是通过计算将要移动到的下一个点的坐标,然后将这个点打印出来,与此同时将蛇尾,亦即queue中的首端点去掉,并擦除屏幕上该点颜色。第三部分就直接依次从蛇尾擦除即可。
同理,文字snake的移动也基本类似,稍微改动即可,因为无需对首尾进行操作,而是要对所以点进行移动,因此容器选用vector。
具体请参看startinterface.h以及startinterface.cpp

2.选择难度

其实这个模块很简单,我就简单介绍一下,先将难度选择的文字信息打印在屏幕上,然后通过控制键盘方向键选择,回车键确认,为了突出选中项,需要给选中项打上背景色,然后每一次上下移动时,先将当前的背景色去掉,然后给下一个选中项打上背景色,按下回车后通过改变蛇移动的速度实现改变难度。其中读取键盘输入是通过getch()函数完成的。
这里写图片描述

3.游戏过程

这个模块就是整个游戏最主要的部分了,首先它先绘制出地图以及侧边栏,同时初始化蛇和食物,然后通过一个无穷循环监听键盘,以此来控制蛇移动,同时又进行各种判断,来判断是否死亡、吃到食物或暂停。需要提一下,这里使用kbhit()函数来监听键盘,它用来判断在一段固定的时间内是否有键盘输入,要知道,这个函数的返回值有两个,第一个是是否有输入的返回值,第二个才是键盘输入的内容,也就是说要经过两次的读取缓冲区才能读到真正的键盘输入。
这里写图片描述

游戏代码实现

从这里开始我们就可以真正动手来实现游戏了,在动手之前,我建议先下载游戏来玩几局,弄清整个游戏的逻辑,这样更能有一个清晰的思路。
接着你可以将以下的代码或者github上的代码按下面几张图的流程添加进工程里,当然如果你使用其他IDE的话就按照它的方式来弄,然后进行编译试一下。

首先新建工程
这里写图片描述

然后将文件一个一个添加进工程里

这里写图片描述

最后所有文件添加完就是这样了

这里写图片描述

记得要把编译器改成c++11标准

这里写图片描述

完成了以上几步后就可以点编译按钮进行编译,同时运行一下,看看效果。然后阅读源码或者修改一下,看看编译后有什么不同。
这里代码.h文件是类的定义,.cpp文件是类的实现。整个程序共有七个类,分别为Tools,Point,StartInterface,Snake,Map,Food。
因为整个游戏需要对于点的大量操作,所以建立Tools和Point两个类,Tools工具类主要是用于设置光标的位置以及输出文字的颜色,Point类设置点的对象,因为其他类都是建立在这两个类的基础上的,所以阅读代码时要先看这两个。然后才开始从main.cpp开始看,一行一行,看到出现新的类就转到该类的声明与定义文件去看,这样阅读起来比较清晰,这里简要说明一下各个类的功能,Controller类就是控制整个游戏过程的,包括游戏的各个阶段,比如更新分数,游戏难度选择等;Food类实现食物的随机出现;Map类负责绘制地图,我由于时间关系(主要是懒)没有加入地图,只有边界,但原理和边界是一模一样的,同样是将点绘制出来,然后每一次都判断蛇是否撞到地图即可;Snake类控制蛇的移动和吃到食物等。
各个类之间的关系大致如下:

这里写图片描述

以下是实现的代码

main.cpp

#include "controller.h"

int main()//程序入口
{
    Controller c;//声明一个Controller类
    c.Game();//整个游戏循环
    return 0;
}

controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H


class Controller
{
public:
    Controller() : speed(200), key(1), score(0) {}
    void Start();
    void Select();
    void DrawGame();
    int PlayGame();
    void UpdateScore(const int&);
    void RewriteScore();
    int Menu();
    void Game();
    int GameOver();
private:
    int speed;
    int key;
    int score;
};
#endif // CONTROLLER_H

controller.cpp

#include <iostream>
#include <time.h>
#include <conio.h>
#include <windows.h>
#include "controller.h"
#include "tools.h"
#include "startinterface.h"
#include "map.h"
#include "snake.h"
#include "food.h"

void Controller::Start()//开始界面
{
    SetWindowSize(41, 32);//设置窗口大小
    SetColor(2);//设置开始动画颜色
    StartInterface *start = new StartInterface();//动态分配一个StartInterface类start
    start->Action();//开始动画
    delete start;//释放内存空间

    /*设置关标位置,并输出提示语,等待任意键输入结束*/
    SetCursorPosition(13, 26);
    std::cout << "Press any key to start... " ;
    SetCursorPosition(13, 27);
    system("pause");
}

void Controller::Select()//选择界面
{
    /*初始化界面选项*/
    SetColor(3);
    SetCursorPosition(13, 26);
    std::cout << "                          " ;
    SetCursorPosition(13, 27);
    std::cout << "                          " ;
    SetCursorPosition(6, 21);
    std::cout << "请选择游戏难度:" ;
    SetCursorPosition(6, 22);
    std::cout << "(上下键选择,回车确认)" ;
    SetCursorPosition(27, 22);
    SetBackColor();//第一个选项设置背景色以表示当前选中
    std::cout << "简单模式" ;
    SetCursorPosition(27, 24);
    SetColor(3);
    std::cout << "普通模式" ;
    SetCursorPosition(27, 26);
    std::cout << "困难模式" ;
    SetCursorPosition(27, 28);
    std::cout << "炼狱模式" ;
    SetCursorPosition(0, 31);
    score = 0;

    /*上下方向键选择模块*/
    int ch;//记录键入值
    key = 1;//记录选中项,初始选择第一个
    bool flag = false;//记录是否键入Enter键标记,初始置为否
    while ((ch = getch()))
    {
        switch (ch)//检测输入键
        {
        case 72://UP上方向键
            if (key > 1)//当此时选中项为第一项时,UP上方向键无效
            {
                switch (key)
                {
                case 2:
                    SetCursorPosition(27, 22);//给待选中项设置背景色
                    SetBackColor();
                    std::cout << "简单模式" ;

                    SetCursorPosition(27, 24);//将已选中项取消我背景色
                    SetColor(3);
                    std::cout << "普通模式" ;

                    --key;
                    break;
                case 3:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    std::cout << "普通模式" ;

                    SetCursorPosition(27, 26);
                    SetColor(3);
                    std::cout << "困难模式" ;

                    --key;
                    break;
                case 4:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    std::cout << "困难模式" ;

                    SetCursorPosition(27, 28);
                    SetColor(3);
                    std::cout << "炼狱模式" ;

                    --key;
                    break;
                }
            }
            break;

        case 80://DOWN下方向键
            if (key < 4)
            {
                switch (key)
                {
                case 1:
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    std::cout << "普通模式" ;
                    SetCursorPosition(27, 22);
                    SetColor(3);
                    std::cout << "简单模式" ;

                    ++key;
                    break;
                case 2:
                    SetCursorPosition(27, 26);
                    SetBackColor();
                    std::cout << "困难模式" ;
                    SetCursorPosition(27, 24);
                    SetColor(3);
                    std::cout << "普通模式" ;

                    ++key;
                    break;
                case 3:
                    SetCursorPosition(27, 28);
                    SetBackColor();
                    std::cout << "炼狱模式" ;
                    SetCursorPosition(27, 26);
                    SetColor(3);
                    std::cout << "困难模式" ;

                    ++key;
                    break;
                }
            }
            break;

        case 13://Enter回车键
            flag = true;
            break;
        default://无效按键
          
阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 263
    点赞
  • 879
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 74
    评论
以下是一个简单的 C++ 贪吃蛇小游戏的实现示例: ```c++ #include <iostream> #include <conio.h> #include <windows.h> using namespace std; bool gameOver; const int width = 20; const int height = 20; int x, y, fruitX, fruitY, score; int tailX[100], tailY[100]; int nTail; enum eDirection {STOP = 0, LEFT, RIGHT, UP, DOWN}; eDirection dir; void Setup() { gameOver = false; dir = STOP; x = width / 2; y = height / 2; fruitX = rand() % width; fruitY = rand() % height; score = 0; } 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 (int k = 0; k < nTail; k++) { if (tailX[k] == j && tailY[k] == i) { 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': gameOver = true; break; } } } void Logic() { int prevX = tailX[0]; int prevY = tailY[0]; int prev2X, prev2Y; tailX[0] = x; tailY[0] = y; for (int i = 1; i < nTail; i++) { prev2X = tailX[i]; prev2Y = tailY[i]; tailX[i] = prevX; tailY[i] = prevY; prevX = prev2X; prevY = prev2Y; } 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 || y > height || y < 0) gameOver = true; for (int i = 0; i < nTail; i++) { if (tailX[i] == x && tailY[i] == y) gameOver = true; } if (x == fruitX && y == fruitY) { score += 10; fruitX = rand() % width; fruitY = rand() % height; nTail++; } } int main() { Setup(); while (!gameOver) { Draw(); Input(); Logic(); Sleep(50); // 控制游戏速度 } return 0; } ``` 这是一个基本的贪吃蛇小游戏的实现,其中用到了数组存储尾巴的坐标,用枚举变量表示贪吃蛇移动的方向。通过控制台输入和输出来实现游戏的交互和界面显示,同时利用 `Sleep()` 函数来控制游戏的速度。 运行程序后,使用键盘上的 WASD 键来控制贪吃蛇的移动,X 键退出游戏。每吃到一个水果得分加 10 分,当贪吃蛇碰到墙壁或自己的身体时游戏结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 74
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

silence1772

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值