c++贪吃蛇游戏-详细解释-非单纯贴代码

先说明下:本文来源:https://blog.csdn.net/silence1772/article/details/55005008

写在前面,这个项目,希望大家先下载下来,玩一玩。然后2天左右把代码敲一遍。再花一到两天写个博客,写下自己的理解,这样自己就能比较清楚。

github网址: https://github.com/LLM1602/greedy_snake.git

一:游戏演示

二:游戏思路框架介绍

   大体分为以下个步骤

(1)开始界面:蛇的移动,以及SNAKE的移动与呈现。

(2)进入界面:菜单的绘制(难度选择)以及地图(也就是围墙)绘制。

(3)游戏界面:包括食物、蛇的动态出现、移动,以及围墙右侧的文字(分数、难度等的成像)的绘制。

三:游戏代码目录以及代码整个层次关系介绍

(1)项目文件目录:  

         目录分为:.cpp文件和.h文件,是为了方便文件之间的互相调用。

         .h文件:主要是类的函数以及成员变量的声明。

         .cpp文件:主要是相应类的函数的具体实现。

         

(2)项目层次关系图

         这里说明,方框里直接采用类的名称表示相应关系。这幅图大概说了各个类之间的了个调用关系。

         

         

四:各个类的讲解

(1): tools类

        a): tools.h

#ifndef TOOLS_H
#define TOOLS_H


void SetWindowSize(int cols, int lines);//设置游戏窗口的大小
void SetCursorPosition(const int x, const int y);//设置光标位置,用来输出文字或者空格(覆盖)
void SetColor(int colorID);//设置文本颜色
void SetBackColor();//设置文本背景颜色

#endif // TOOLS_H

       b): tools.cpp

         以下几个点需要注意下:

         1)SetWindowSize()里的,system(cmd)是个固定用法,这是一个库函数,另外由于一个图形■占两个字符,故宽度乘以2。

         2)SetCursorPosition()里的,COORD 是个结构体,含有两个成员变量x,y,刚好用来表示光标的位置。再调用   

              SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);库函数,也就把光标移到position的位置

         3)同理也是一个库函数,SetConsoleTextAttribute():设置文本颜色。

#include "tools.h"
#include <windows.h>
#include <stdio.h>

void SetWindowSize(int cols, int lines)//设置窗口大小
{
    system("title 贪吃蛇");//设置窗口标题,黑款左上角会显示贪吃蛇,固定的“title+....."
    char cmd[30];
    sprintf(cmd, "mode con cols=%d lines=%d", cols * 2, lines);//一个图形■占两个字符,故宽度乘以2
    system(cmd);//system(mode con cols=88 lines=88):设置窗口宽度和高度,固定的库函数以及写法
}

void SetCursorPosition(const int x, const int y)//设置光标位置,相当于是个接口,还要调用windows里 的函数,SetConsoleCursorPosition
{
    //懂了
    COORD position;//COORD:固定的结构体变量,有两个成员X,Y
    position.X = x * 2 ;//一个图形■占两个字符,故宽度乘以2
    position.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
    //可直接记住即可。
    //GetStdHandle(STD_OUTPUT_HANDLE):标准输出程序的句柄
    //SetConsoleCursorPosition:在写入输出之前将光标移动到所需位置,即(x,y)的位置m
}

void SetColor(int colorID)//设置文本颜色
{
    //懂了
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorID);
    //GetStdHandle(STD_OUTPUT_HANDLE):固定输出句柄
    //
}

void SetBackColor()//设置文本背景色
{
    //懂了
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
                            FOREGROUND_BLUE |
                            BACKGROUND_BLUE |
                            BACKGROUND_GREEN |
                            BACKGROUND_RED );
}

(2): point类

     a): point.h

#ifndef POINT_H
#define POINT_H
class Point
{
public:
    Point(){};
    Point(const int x,const int y) :x(x),y(y){};//构造函数
    void Print();//打印地图需要用到的正方形方块
    void PrintCircular();//打印蛇需要的圆形块
    void Clear();//打印空格,用以覆盖蛇的尾部,就是蛇在移动过程中,其实是把尾部的图形用空格覆盖,在头部重新加入一个point的点进去,再绘制这条蛇
    void ChangePosition(const int x,const int y);//改变坐标
    bool operator == (const Point& point ){ return (point.x == this->x) &&(point.y == this->y) ;};//重载 == 运算符
    int GetX(){return this->x;};//得到坐标
    int GetY(){return this->y;};
private:
    int x,y;
};
#endif // POINT_H

    b): point.cpp

         注意下:

         1)Clear()函数是通过打印空格来覆盖掉原来的方块,

#include "point.h"
#include "tools.h"
#include <iostream>


void Point::Print()//输出方块
{
    //懂了
    SetCursorPosition(x,y);
    std::cout<<"■" ;//Alt+41462,一个方块占2个字节,
}

void Point::PrintCircular()//输出图形
{
    //懂啦,打印蛇
    SetCursorPosition(x,y);
    std::cout<<"●";//●
}
void Point::Clear()//清除输出
{
    //懂了,
    SetCursorPosition(x,y);//清除(x,y)这个点,不是真正清除,是通过覆盖,用空格来覆盖原来的点
    std::cout<<"  ";//此处是两个空格,因为都是占了2个字节的
}
void Point::ChangePosition(const int x,const int y)//改变坐标
{
    //懂啦
    this->x = x;
    this->y = y;
}


(3): startinterface类:

      a): startinterface.h

           需要说明下:

           1) speed是动画显示的速度

           2)其次看两个双端队列,startsnake(存储开始动画中那个v字形一样的蛇),starttext(SNAKE的那个文字)

           3)这里其实是把屏幕左上角当作坐标系的原点,横轴向右是X轴正方向,竖轴向下是y轴正方向。自己可以试着把这些点在纸上描绘以下,就能明白了,尤其是那个v字形的蛇。本人的草稿这在里给大家看看,字丑哈。

代码:

#ifndef STRATINTERFACE_H
#define STARTINTERFACE_H

#include <deque>
#include <vector>
#include "point.h"

class StartInterface
{
public:
    StartInterface() : speed(500) {
        startsnake.emplace_back(Point(0,14));//Éß,蛇刚开始的像v字形一样
        startsnake.emplace_back(Point(1,14));
        startsnake.emplace_back(Point(2,15));
        startsnake.emplace_back(Point(3,16));
        startsnake.emplace_back(Point(4,17));
        startsnake.emplace_back(Point(5,18));
        startsnake.emplace_back(Point(6,17));
        startsnake.emplace_back(Point(7,16));
        startsnake.emplace_back(Point(8,15));
        startsnake.emplace_back(Point(9,14));

        textsnake.emplace_back(Point(-26, 14));//S,刚开始SNAKE的点在X轴的负半轴
        textsnake.emplace_back(Point(-25, 14));
        textsnake.emplace_back(Point(-27, 15));
        textsnake.emplace_back(Point(-26, 16));
        textsnake.emplace_back(Point(-25, 17));
        textsnake.emplace_back(Point(-27, 18));
        textsnake.emplace_back(Point(-26, 18));

        textsnake.emplace_back(Point(-23, 14));//N
        textsnake.emplace_back(Point(-23, 15));
        textsnake.emplace_back(Point(-23, 16));
        textsnake.emplace_back(Point(-23, 17));
        textsnake.emplace_back(Point(-23, 18));
        textsnake.emplace_back(Point(-22, 15));
        textsnake.emplace_back(Point(-21, 16));
        textsnake.emplace_back(Point(-20, 17));
        textsnake.emplace_back(Point(-19, 14));
        textsnake.emplace_back(Point(-19, 15));
        textsnake.emplace_back(Point(-19, 16));
        textsnake.emplace_back(Point(-19, 17));
        textsnake.emplace_back(Point(-19, 18));

        textsnake.emplace_back(Point(-17, 18));//A
        textsnake.emplace_back(Point(-16, 17));
        textsnake.emplace_back(Point(-15, 16));
        textsnake.emplace_back(Point(-14, 15));
        textsnake.emplace_back(Point(-14, 16));
        textsnake.emplace_back(Point(-13, 14));
        textsnake.emplace_back(Point(-13, 16));
        textsnake.emplace_back(Point(-12, 15));
        textsnake.emplace_back(Point(-12, 16));
        textsnake.emplace_back(Point(-11, 16));
        textsnake.emplace_back(Point(-10, 17));
        textsnake.emplace_back(Point(-9, 18));

        textsnake.emplace_back(Point(-7, 14));//K
        textsnake.emplace_back(Point(-7, 15));
        textsnake.emplace_back(Point(-7, 16));
        textsnake.emplace_back(Point(-7, 17));
        textsnake.emplace_back(Point(-7, 18));
        textsnake.emplace_back(Point(-6, 16));
        textsnake.emplace_back(Point(-5, 15));
        textsnake.emplace_back(Point(-5, 17));
        textsnake.emplace_back(Point(-4, 14));
        textsnake.emplace_back(Point(-4, 18));

        textsnake.emplace_back(Point(-2, 14));//E
        textsnake.emplace_back(Point(-2, 15));
        textsnake.emplace_back(Point(-2, 16));
        textsnake.emplace_back(Point(-2, 17));
        textsnake.emplace_back(Point(-2, 18));
        textsnake.emplace_back(Point(-1, 14));
        textsnake.emplace_back(Point(-1, 16));
        textsnake.emplace_back(Point(-1, 18));
        textsnake.emplace_back(Point(0, 14));
        textsnake.emplace_back(Point(0, 16));
        textsnake.emplace_back(Point(0, 18));
    }
    void PrintFirst();//蛇(v字形)出现再界面上
    void PrintSecond();//蛇从左移动到右,
    void PrintThird();//蛇逐渐消失
    void PrintText();//SNAKE文字的移动
    void ClearText();//SNAKE文字的消失
    void Action();
private:
    std::deque<Point> startsnake;//开始动画中的蛇
    std::vector<Point> textsnake;//开始动画中的文字
    int speed;//动画的速度
};
#endif // STRATINTERFACE_H



       b): startinterface.cpp

            这里不太好理解,我来讲述下我自己的理解。

           1): 先来看下正常速度下的样子

          2)其实思路大概分为三部分蛇身的出现->蛇从出现移动到蛇头抵达屏幕右方->蛇自己慢慢消失并且文字SNAKEK开始出现

          3)蛇身的出现:其实就是提前把点存储到双端队列里,然后把一个个点在屏幕上打印出来,然后用sleep()来制造动态效果,将速度放慢后,看图。具体代码看了,PrintFirst()就知道啦。

     4)蛇从屏幕左侧移到右侧:由上图可以发现,其实就是把蛇尾,用空格覆盖掉,然后计算出蛇头,再绘制蛇头即可。代码,请看PrintSecond(),那个算出下一个头部的位置的公式,我建议大家自己算一算,就可以明白了。另外注意,打印蛇都是从双端队列的front开始打印的,也就是说,新加入的其实是队列.back()里,这个地方容易混淆。

    5)蛇渐渐从屏幕消失,文字SNAKE渐渐出来。请看图片,蛇渐渐消失,其实就是,把蛇的尾巴一个个砍掉,也就是把屏幕里最左边的点一个个用空格覆盖,在双端队列就是把队列的front出队。具体看PrintThird()。另外文字SNAKE怎么绘制呢?其实和第四步很像,不过这里由于很多点,它采取的是,先把屏幕上出现的所有文字的点(比如KE字母先出来了,其他字母还没出来),先用空格覆盖,再把snaketext的所有点的横坐标+1,这样在下次绘制蛇的时候,把所有点都会遍历一遍,但是只会把这些点中X大于0的点打印在屏幕上,这样相比前一次就会整体向右移动了一格

#include "startinterface.h"
#include <windows.h>

void StartInterface::PrintFirst()//蛇从左边出现到完全出现的过程,
{
    //明白了
    for(auto& point : startsnake)
    {
        point.Print();
        Sleep(speed);//sleep():延时,执行挂起一段时间,也就是等待一段时间在继续执行,,
        //这样就有蛇在移动了的效果,因为直接打印点,太快了,一下子全部打出来了,通过Sleep(),延时打印,就好像在动
    }
}

//下面几个函数要好好理解
void StartInterface::PrintSecond()//蛇从左向右移动的过程
{
    //懂啦
    for(int i=10;i!=40;++i)//蛇头需要从10 移动到40,因为屏幕设置就是41*32,
    {
        /*计算蛇头的下一个位置,并将其压入startsnake中,绘制出来,将蛇尾去掉*/
        int j = ( ((i-2)%8) < 4 ) ? (15 + (i-2)%8) : (21-(i-2)%8);
        //15+(i-2)%8 :往下降
        //21-(i-2)%8 :往上升
        startsnake.emplace_back(Point(i,j));//双端队列,放入队列的后面,实际上在屏幕上是蛇头,也就是最右方
        startsnake.back().Print();//最后一个元素打印出来,也就是蛇头打印出来,
        startsnake.front().Clear();//然后清除屏幕上的一个点,也就是屏幕最左边的,即蛇尾
        startsnake.pop_front();//从队列中删除,即保证蛇的长度不变
        Sleep(speed);
    }
}

void StartInterface::PrintThird()////蛇从接触右边到消失的过程
{
    //懂啦
    while ( !startsnake.empty() || textsnake.back().GetX() < 33 ) //当蛇还没消失或文字没移动到指定位置
    {
        if ( !startsnake.empty() ) //如果蛇还没消失,继续移动
        {
            startsnake.front().Clear();//清除屏幕最左边的,将最左边的出队
            startsnake.pop_front();
        }
        ClearText();//清除已有文字,
        PrintText();//文字SNAKE开始慢慢出现
        //这里其实就是,先把显示的文字,直接用空格清除,但是在ClearText()时,已经显示的文字会被空格代替,并且会把所有文字向右移动一格
        Sleep(speed);
    }
}
void StartInterface::PrintText()
{
    //懂啦
    for (auto& point : textsnake)
    {
        if (point.GetX() >= 0)
            point.Print();//只显示X大于0的位置
    }
}
void StartInterface::ClearText()
{
    //懂啦
    for (auto& point : textsnake) //把在屏幕上显示的文字,SNAKE全用空格覆盖,覆盖后再把所有的点往右移一格
    {
        if (point.GetX() >= 0)
            point.Clear();//清除已经显示的文字,也就是x>=0 的文字
        point.ChangePosition(point.GetX() + 1, point.GetY());
    }
}

void StartInterface::Action()
{
    PrintFirst();
    PrintSecond();
    PrintThird();
}



(4): map类:

       a): map.h

                   说明下,其实就是把点存储在vector里,然后调用打印,将其绘制出来

#ifndef MAP_H
#define MAP_H

#include <vector>
#include "point.h"
#endif // MAP_H

class Map
{
private:
    std::vector<Point>initmap;//保存初始地图
    /*Map类可自定义多种地图,只需将表示地图的各个点保存在相应的map中,并在Snake类中增加相应判断撞墙函数即可
    std::vector<Point> map1;
    std::vector<Point> map2;
    */
public:
    Map()//默认构造函数,将正方形各点压入initmap,30*30
    {
        initmap.emplace_back(Point(1, 1));//四周的点
        initmap.emplace_back(Point(2, 1));
        initmap.emplace_back(Point(3, 1));
        initmap.emplace_back(Point(4, 1));
        initmap.emplace_back(Point(5, 1));
        initmap.emplace_back(Point(6, 1));
        initmap.emplace_back(Point(7, 1));
        initmap.emplace_back(Point(8, 1));
        initmap.emplace_back(Point(9, 1));
        initmap.emplace_back(Point(10, 1));
        initmap.emplace_back(Point(11, 1));
        initmap.emplace_back(Point(12, 1));
        initmap.emplace_back(Point(13, 1));
        initmap.emplace_back(Point(14, 1));
        initmap.emplace_back(Point(15, 1));
        initmap.emplace_back(Point(16, 1));
        initmap.emplace_back(Point(17, 1));
        initmap.emplace_back(Point(18, 1));
        initmap.emplace_back(Point(19, 1));
        initmap.emplace_back(Point(20, 1));
        initmap.emplace_back(Point(21, 1));
        initmap.emplace_back(Point(22, 1));
        initmap.emplace_back(Point(23, 1));
        initmap.emplace_back(Point(24, 1));
        initmap.emplace_back(Point(25, 1));
        initmap.emplace_back(Point(26, 1));
        initmap.emplace_back(Point(27, 1));
        initmap.emplace_back(Point(28, 1));
        initmap.emplace_back(Point(29, 1));
        initmap.emplace_back(Point(30, 1));
        initmap.emplace_back(Point(1, 2));
        initmap.emplace_back(Point(30, 2));
        initmap.emplace_back(Point(1, 3));
        initmap.emplace_back(Point(30, 3));
        initmap.emplace_back(Point(1, 4));
        initmap.emplace_back(Point(30, 4));
        initmap.emplace_back(Point(1, 5));
        initmap.emplace_back(Point(30, 5));
        initmap.emplace_back(Point(1, 6));
        initmap.emplace_back(Point(30, 6));
        initmap.emplace_back(Point(1, 7));
        initmap.emplace_back(Point(30, 7));
        initmap.emplace_back(Point(1, 8));
        initmap.emplace_back(Point(30, 8));
        initmap.emplace_back(Point(1, 9));
        initmap.emplace_back(Point(30, 9));
        initmap.emplace_back(Point(1, 10));
        initmap.emplace_back(Point(30, 10));
        initmap.emplace_back(Point(1, 11));
        initmap.emplace_back(Point(30, 11));
        initmap.emplace_back(Point(1, 12));
        initmap.emplace_back(Point(30, 12));
        initmap.emplace_back(Point(1, 13));
        initmap.emplace_back(Point(30, 13));
        initmap.emplace_back(Point(1, 14));
        initmap.emplace_back(Point(30, 14));
        initmap.emplace_back(Point(1, 15));
        initmap.emplace_back(Point(30, 15));
        initmap.emplace_back(Point(1, 16));
        initmap.emplace_back(Point(30, 16));
        initmap.emplace_back(Point(1, 17));
        initmap.emplace_back(Point(30, 17));
        initmap.emplace_back(Point(1, 18));
        initmap.emplace_back(Point(30, 18));
        initmap.emplace_back(Point(1, 19));
        initmap.emplace_back(Point(30, 19));
        initmap.emplace_back(Point(1, 20));
        initmap.emplace_back(Point(30, 20));
        initmap.emplace_back(Point(1, 21));
        initmap.emplace_back(Point(30, 21));
        initmap.emplace_back(Point(1, 22));
        initmap.emplace_back(Point(30, 22));
        initmap.emplace_back(Point(1, 23));
        initmap.emplace_back(Point(30, 23));
        initmap.emplace_back(Point(1, 24));
        initmap.emplace_back(Point(30, 24));
        initmap.emplace_back(Point(1, 25));
        initmap.emplace_back(Point(30, 25));
        initmap.emplace_back(Point(1, 26));
        initmap.emplace_back(Point(30, 26));
        initmap.emplace_back(Point(1, 27));
        initmap.emplace_back(Point(30, 27));
        initmap.emplace_back(Point(1, 28));
        initmap.emplace_back(Point(30, 28));
        initmap.emplace_back(Point(1, 29));
        initmap.emplace_back(Point(30, 29));
        initmap.emplace_back(Point(1, 30));
        initmap.emplace_back(Point(2, 30));
        initmap.emplace_back(Point(3, 30));
        initmap.emplace_back(Point(4, 30));
        initmap.emplace_back(Point(5, 30));
        initmap.emplace_back(Point(6, 30));
        initmap.emplace_back(Point(7, 30));
        initmap.emplace_back(Point(8, 30));
        initmap.emplace_back(Point(9, 30));
        initmap.emplace_back(Point(10, 30));
        initmap.emplace_back(Point(11, 30));
        initmap.emplace_back(Point(12, 30));
        initmap.emplace_back(Point(13, 30));
        initmap.emplace_back(Point(14, 30));
        initmap.emplace_back(Point(15, 30));
        initmap.emplace_back(Point(16, 30));
        initmap.emplace_back(Point(17, 30));
        initmap.emplace_back(Point(18, 30));
        initmap.emplace_back(Point(19, 30));
        initmap.emplace_back(Point(20, 30));
        initmap.emplace_back(Point(21, 30));
        initmap.emplace_back(Point(22, 30));
        initmap.emplace_back(Point(23, 30));
        initmap.emplace_back(Point(24, 30));
        initmap.emplace_back(Point(25, 30));
        initmap.emplace_back(Point(26, 30));
        initmap.emplace_back(Point(27, 30));
        initmap.emplace_back(Point(28, 30));
        initmap.emplace_back(Point(29, 30));
        initmap.emplace_back(Point(30, 30));
    }
    void PrintInitmap();//绘制初始地图

};

      b) map.cpp

#include "map.h"
#include <windows.h>

void Map::PrintInitmap()//绘制初始地图
{
    //懂啦,构造四面的地图
    for (auto& point : initmap)
    {
        point.Print();
        Sleep(10);//调用Sleep函数可营造动画效果
    }
}

(5): food类

       a): food.h

#ifndef FOOD_H
#define FOOD_H
#include "snake.h"
class Snake;
class Food
{

private:
    int cnt;//记录连续吃到多少次食物了,到了第五次就出现限时食物,并被清0
    bool flash_flag;//闪烁标记
    bool big_flag;//是否有限时食物标记
    int x,y;//食物的位置
    int big_x,big_y;//限时食物的坐标
    int progress_bar;//限时食物进度条,用来控制加多少分的
    friend class Snake;//设置友元,可以访问私有元素
public:
    Food():cnt(0),flash_flag(false),big_flag(false),x(0),y(0),big_x(0),big_y(0),progress_bar(0){}
    void DrawFood(Snake& snake);//这里的参数,传递的是Snake的对象
    void DrawBigFood(Snake& snake);//这里的参数,传递的是Snake的对象
    void FlashBigFood();//闪烁限时食物
    bool GetBigFlag();//是否有限时食物出现
    int GetProgressBar();//返回吃到限时食物应得的分数
};
#endif // FOOD_H

      b): food.cpp

                  说明一下

                  1): DrawFood(),用cnt来计数,用x,y记录food的位置,每连续吃到5次食物后,就会出现一个限时食物,也就是用                               DrawBigFood(),用big_x,big_y来记录限时食物的位置。

                  2): DrawBigFood()里的会有个进度条围墙在上方,在用FlashBigFood()来制造,首先限时食物绘制在屏幕上是个正方                         形方块,占两个字符的位置,闪烁是通过controller里的while循环,bool型的flash_flag来设置,true的时候,用空格                     覆盖,false的时候,打印正方形方块,食物还没吃到的时候,那个controller的while()会一直执行,然后每次都会调用                       FlashBigFood(),这样就有闪烁的效果。去看看那地方的代码,就会明白我说啥。

                 3): process_bar一个时间的限制,也是作为一个分数,吃到限时食物加多少分,process_bar会一直递减同时用\b退格来                        制造进度条的动态感。

#include "food.h"
#include "tools.h"
#include <cstdlib>
#include <iostream>
using namespace std;

void Food::DrawFood(Snake& csnake)//把蛇当作参数,这样可以计算随机出现的food是否合理
{
    /*利用rand函数获得坐标,并将其范围限制在2-29内,即在地图内,如果获得的坐标与蛇身重叠,则重新获取。
      同时每5颗食物就出现一颗限时食物*/
    while(true)
    {
        int tmp_x = rand()%30;
        int tmp_y = rand()%30;
        if(tmp_x<2)
            tmp_x += 2;
        if(tmp_y<2)
            tmp_y += 2;
        bool flag = false;
        for(auto& point : csnake.snake)
        {
            if((point.GetX() == tmp_x && point.GetY() == tmp_y) || (tmp_x == big_x && tmp_y == big_y) )//big_x:先不管
            {
                flag = true;
                break;
            }
        }
        if(flag)
            continue;
        x = tmp_x;//x,y是为了记录出现的food的位置,因为后面在绘制限时食物时,要用来和x,y比较,也就是不同与这个普通食物重复。
        y = tmp_y;
        SetCursorPosition(x,y);
        //SetCursorPosition(tmp_x,tmp_y);
        SetColor(13);
        std::cout<<"★";//星星的颜色
        ++cnt;
        cnt%=5;
        if(cnt == 0)
        {
            //每吃到5次食物,就会出现限时食物
            DrawBigFood(csnake);
        }
        break;
    }
}
void Food::DrawBigFood(Snake& csnake)//绘制限时食物
{
    SetCursorPosition(5,0);
    SetColor(11);
    std::cout<<"------------------------------------------" ;//进度条
    progress_bar = 42;//进度条的分数
    while(true)
    {
        int tmp_x = rand()%30;
        int tmp_y = rand() % 30;
        if(tmp_x < 2) tmp_x += 2;
        if(tmp_y < 2) tmp_y += 2;
        bool flag = false;
        for (auto& point : csnake.snake)
        {
            if ((point.GetX() == tmp_x && point.GetY() == tmp_y) || (tmp_x == x && tmp_y == y))
            {
                flag = true;
                break;
            }
        }
        if (flag)
            continue;
        big_x = tmp_x;
        big_y = tmp_y;
        SetCursorPosition(big_x, big_y);
        SetColor(18);
        std::cout << "■" ;
        big_flag = true;//有限时食物
        flash_flag = true;//限时食物闪烁
        break;
    }
}

void Food::FlashBigFood()//闪烁限时食物
{
    SetCursorPosition(big_x,big_y);
    SetColor(18);
    if(flash_flag)
    {
        std::cout<<" ";
        flash_flag = false;
    }
    else
    {
        std::cout<<"■" ;
        flash_flag = true;
    }//这个if,else,就是在闪烁,
    SetCursorPosition(26,0);
    SetColor(11);
    for(int i=42;i>= progress_bar;--i)
        std::cout<<"\b\b";//因为progress_bar一直在变,所以显示出来,它会一直在动,就是闪在progress_bar那个位置
    --progress_bar;
    if(progress_bar == 0)
    {
        //时间走完了,急用空格把那个限时食物覆盖掉
        SetCursorPosition(big_x,big_y);
        std::cout<<"  ";
        SetCursorPosition(5,0);
        std::cout<<"                                          " ;//用空格去掉进度条
        big_flag = false;
        big_x = 0;
        big_y = 0;
    }
}
bool Food::GetBigFlag()
{
    return big_flag;
}
int Food::GetProgressBar()
{
    return progress_bar;
}




(6): snake类

          a): snake.h

#ifndef SNAKE_H
#define SNAKE_H

#include <deque>
#include "point.h"
#include "food.h"
class Food;
class Snake
{
public:
    enum Direction{UP,DOWN,LEFT,RIGHT};//enum :用来枚举变量
    Snake(){
        //最开始蛇的大小以及方向
        snake.emplace_back(14,8);
        snake.emplace_back(15,8);
        snake.emplace_back(16,8);
        direction = Direction::DOWN;//初始选择方向,蛇一绘制完成,direction就是:down
    }
    void InitSnake();//初始化蛇
    void Move();//蛇吃到食物,增长
    void NormalMove();//蛇没吃到食物时的正常移动
    bool OverEdge();//判断蛇是否撞到边界
    bool HitItself();//判断是否撞到自己
    bool ChangeDirection();//改变方向,用来判断方向是否改变,并把direction的值改变,
    bool GetFood(const Food&);//吃到食物
    bool GetBigFood(Food&);//吃到限时食物
private:
    std::deque<Point>snake;//双端队列,有类似vector的功能
    Direction direction;
    friend class Food;//将Food类置为友元,以便访问其私有元素
};
#endif // SNAKE_H

      b): snake.cpp

           以下几点需要注意下。

          1): 蛇增长、以及两个判断其实没什么好说的,吃到食物这些也没什么好说的,看到这里应该都能明白了。

          2): 这里说下那个ChangeDirection(),kbhit()用来判断是否有键盘输入,,还有2个getch(),因为上下左右键,是双键值,会分                   两次返回值,第一次返回前八位,用int(16位)来接受的话,就都是224,如果按的不是上下左右,而是esc键,对应的                   int值就是27,这也就解释了case的值的取法。另外关于上下左右的判断,只用判断后八位的值,判断上下左右对应的int                   值,上-72,下-80,左-75,右-77。详见代码注释有讲解。这里真得注意下哈。

#include "snake.h"
#include <conio.h>
#include "tools.h"
#include <iostream>
using namespace std;
void Snake::InitSnake()//初始化蛇
{
    //打印最开始的蛇
    for(auto& point : snake)
        point.PrintCircular();
}

void Snake::Move()//蛇增长
{
    //懂啦
    //cout<<"move "<<direction<<endl;
    switch(direction)//是对的,back()是头部
    {
        case Direction::UP:
            snake.emplace_back(Point(snake.back().GetX(),snake.back().GetY()-1));
            break;
        case Direction::DOWN:
        snake.emplace_back(Point(snake.back().GetX(), snake.back().GetY() + 1 ));
        break;
    case Direction::LEFT:
        snake.emplace_back(Point(snake.back().GetX() - 1, snake.back().GetY() ));
        break;
    case Direction::RIGHT:
        snake.emplace_back(Point(snake.back().GetX() + 1, snake.back().GetY() ));
        break;
    default:
        break;
    }
    SetColor(14);
    snake.back().PrintCircular();//绘制新的蛇的头部
}

void Snake::NormalMove()//蛇正常移动,头增长,尾缩短
{
    //cout<<"normalmove "<<direction<<endl;
    Move();//
    snake.front().Clear();//front()对应的是尾巴
    snake.pop_front();//

}
bool Snake::OverEdge()//超出边界
{
    //懂啦
    //snack.back()就是蛇的头部
    return snake.back().GetX() <30 &&snake.back().GetY() <30 &&
           snake.back().GetX() >1  &&snake.back().GetY() >1;
}
bool Snake::HitItself()//撞到自己
{
    std::deque<Point>::size_type cnt = 1;
    Point *head = new Point(snake.back().GetX(),snake.back().GetY());//?这不是尾部元素吗啊?-》就是蛇头
    for(auto& point:snake)//????如果整条蛇中与蛇头不相同的坐标不等于蛇长,则意味着蛇头碰撞到自身
    {
        if(!(point == *head))
            cnt++;
        else
            break;
    }
    delete head;
    if(cnt == snake.size())//应该是size()-1
        return true;
    else
        return false;

}
bool Snake::ChangeDirection()//改变方向
{
    //操纵蛇的方向,懂啦
    //char ch;
    //cout<<"changeDirction "<<direction<<endl;
    int ch;
    if(kbhit())//kbhit():检查是否有键盘输入,有返回非0,无返回0
    {
        ch = getch();//getch():当用户按下某个字符时,函数自动读取,无需按回车
        //在用getch()(在头文件conio.h)获得上下左右键的键值时候,他们是双键值,会返回高八位和低八位的int型数值。
        //这里getch():先读前8位,上下左右的前8位的值是 无符号值224,对应有符号值是-32,用int :第一次就是224,第二次就是,72,用char :只有8位,且是有符号,-32
        //可用这个程序去试
//        char ch;
//        while((ch=getch())!=0x1B) /* ESC to quit */
//        {
//            printf("%d \n", ch);
//        }
        switch(ch)
        {

        case 224://
            ch = getch();//这里再读取后八位,
            //printf("22 %d\n",ch);
            switch(ch)
            {
            case 72://up
                if(direction != Direction::DOWN)//判断想要移动方向与原本运动方向是否相反,相反则无效
                    direction = Direction::UP;
                break;
            case 80://down
                if(direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case 75://left
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case 77://right
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
            default:
                break;
            }
            return true;
        case 27://esc:跳出esc:0x1B->也就是27
            return false;
        default:
            return true;
        }
    }
    return true;
}
bool Snake::GetFood(const Food& cfood)
{
    //懂啦
    if(snake.back().GetX() == cfood.x && snake.back().GetY() == cfood.y)
        return true;
    else
        return false;
}
bool Snake::GetBigFood(Food& cfood)//吃到限时食物
{
    if(snake.back().GetX() == cfood.big_x && snake.back().GetY() == cfood.big_y)
    {
        cfood.big_flag = false;//限时食物置为false
        cfood.big_x = 0;
        cfood.big_y = 0;//还原
        SetCursorPosition(1, 0);//吃大了,也要把进度条清空
        std::cout << "                                                            " ;//进度条清空
        return true;
    }
    else
        return false;
}

(7): controller类

             a): controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H


class Controller
{
public:
    Controller() : speed(1), key(1), score(0) {}
    void Start();//游戏开始界面
    void Select();//选择不同难度,对应就会有不同key值,不同key值就会对应有不同speed(蛇移动速度)
    void DrawGame();//绘制游戏界面
    int PlayGame();//玩游戏
    void UpdateScore(const int&);//更新分数
    void RewriteScore();//将更新完的分数输入到屏幕上,注意右对齐的格式
    int Menu();//按下ESC键后是菜单选项
    void Game();//整个游戏入口
    int GameOver();//绘制游戏结束的界面
private:
    int speed;//蛇移动速度
    int key;//对应选择难度
    int score;//获得分数
};
#endif // CONTROLLER_H

           b): controller.cpp

                以下有几点需要进行说明。

                1): controller类里面要调用许多其他的类的函数,它会声明的时一个指向类对象的指针。传递参数时也是一个对象,在                           DrawFood的函数里的参数形式应该也对应Snake的对象。申明指针类的对象和声明普通对象不同在于,指针类需要->                      来访问成员变量以及函数,另外需要自己来delete释放内存,而且传递参数时永远都是4个字节,也就是传递参数速度                        会快一些,另外它还能实现多态(具体不同请看博客,https://blog.csdn.net/keneyr/article/details/89364275

                   

                2): RewriteScore()函数,就是为了保证在输出的时候以右对齐的方式输出。

                3): 会不会有人在想,蛇移动的快慢是怎么设置的,其实是在选择模式时,不同的模式有个对应的Key值,不同的key值会                         对应不同的speed,每次蛇在移动的时候,想想之前是怎么制造动态效果的,其实就是,通过Sleep()函数来做到的,                           Sleep(speed)这样就可以控制速度啦。

#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"

using namespace std;

void Controller::Start()//开始界面
{

    SetWindowSize(41, 32);//设置窗口大小
    SetColor(2);//设置开始动画颜色,2对应蛇的颜色,也就是蓝色
    StartInterface *start = new StartInterface();//动态分配一个StartInterface类start,
    //new StartInterface() : 点就开始构造好了
    start->Action();//开始动画
    delete start;//释放内存空间

    /*设置关标位置,并输出提示语,等待任意键输入结束*/
    //文字 "请按任意键继续"   是system("pause"):的效果
    SetCursorPosition(13, 26);
    std::cout << "Press any key to start... " ;//
    SetCursorPosition(13, 27);
    system("pause");//暂停窗口,让用户按键,用户按键后,Start()函数就结束了-》Select()函数
}

void Controller::Select()//选择界面
{

    //初始化界面选项->>>>>>> 懂啦
    SetColor(3);//字体颜色
    SetCursorPosition(13, 26);
    std::cout << "                          " ;
    SetCursorPosition(13, 27);
    std::cout << "                          " ;//与SNAKE 空两行,就是为了覆盖掉初始界面的"请按任意键继续",的那两行
    SetCursorPosition(6, 21);
    std::cout << "请选择游戏难度:" ;
    SetCursorPosition(6, 22);
    std::cout << "(上下键选择,回车确认)" ;

    SetCursorPosition(27, 22);
    SetBackColor();//第一个选项设置背景色以表示当前选中
    std::cout << "简单模式" ;

    SetCursorPosition(27, 24);
    SetColor(3);//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:
                    //cout<<"case 22 "<<key<<endl;
                    //在2的时候按up,就是1,也就是选中“简单模式”
                    //以下同理
                    SetCursorPosition(27, 22);//给待选中项设置背景色
                    SetBackColor();
                    std::cout << "简单模式" ;

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

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

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

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

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

                    --key;
                    break;
                }
            }
            break;

        case 80://DOWN下方向键
            if (key < 4)
            {
                //cout<<key<<endl;
                switch (key)
                {
                case 1:
                    //其实就是重新绘制,把简单模式还原SetColor(3),把普通模式选中,SetBackColor()
                    //在key = 1 的时候,按down, 才会到“普通模式”,因此将普通模式 置为选中
                    //以下皆同理
                    SetCursorPosition(27, 24);
                    SetBackColor();
                    std::cout << "普通模式" ;
                    SetCursorPosition(27, 22);
                    SetColor(3);
                    std::cout << "简单模式" ;

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

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

                    ++key;
                    break;
                }
            }
            break;

        case 13://Enter回车键
            flag = true;
            break;
        default://无效按键,就继续判断新的输入按键
            break;

        }//switch 结束
        if (flag) break;//输入Enter回车键确认,退出检查输入循环

        SetCursorPosition(0, 31);//将光标置于左下角,避免关标闪烁影响游戏体验
    }//while结束,也就是选择难度模式,up,down 结束

    //根据所选难度,会对应一个key,根据key的值来选择相应难度的速度
    switch (key)//根据所选选项设置蛇的移动速度,speed值越小,速度越快
    {
    case 1:
        speed = 135;
        break;
    case 2:
        speed = 100;
        break;
    case 3:
        speed = 60;
        break;
    case 4:
        speed = 30;
        break;
    default:
        break;
    }
    //结束选择-》>>>>> DrawGame();
}

void Controller::DrawGame()//绘制游戏界面
{
    //懂啦
    system("cls");//清屏

    //绘制地图-》懂啦
    SetColor(3);
    Map *init_map = new Map();
    init_map->PrintInitmap();
    delete init_map;

    //绘制侧边栏,右边-》》》》懂啦
    SetColor(3);
    SetCursorPosition(33, 1);
    std::cout << "Greedy Snake" ;
    SetCursorPosition(34, 2);
    std::cout << "贪吃蛇" ;
    SetCursorPosition(31, 4);
    std::cout << "难度:" ;
    SetCursorPosition(36, 5);
    switch (key)
    {
    case 1:
        std::cout << "简单模式" ;
        break;
    case 2:
        std::cout << "普通模式" ;
        break;
    case 3:
        std::cout << "困难模式" ;
        break;
    case 4:
        std::cout << "炼狱模式" ;
        break;
    default:
        break;
    }
    SetCursorPosition(31, 7);
    std::cout << "得分:" ;
    SetCursorPosition(37, 8);
    std::cout << "     0" ;//初始为0
    SetCursorPosition(33, 13);
    std::cout << " 方向键移动" ;
    SetCursorPosition(33, 15);
    std::cout << " ESC键暂停" ;
}

int Controller::PlayGame()//游戏二级循环
{
    /*初始化蛇和食物*/
    Snake *csnake = new Snake();
    Food *cfood = new Food();
    SetColor(6);
    csnake->InitSnake();//打印初始的蛇
    srand((unsigned)time(NULL));//设置随机数种子,如果没有 食物的出现位置将会固定
    cfood->DrawFood(*csnake);//随机出现food,传递指针类的对象

    /*游戏循环*/

    while (csnake->OverEdge() && csnake->HitItself()) //判断是否撞墙或撞到自身,即是否还有生命
    {
        //调出选择菜单->>>懂啦

        if (!csnake->ChangeDirection()) //按Esc键时
        {
            int tmp = Menu();//绘制菜单,并得到返回值tmp的返回值:1,2,3
            switch (tmp)
            {
            case 1://继续游戏
                break;

            case 2://重新开始
                delete csnake;
                delete cfood;
                return 1;//将1作为PlayGame函数的返回值返回到Game函数中,表示重新开始

            case 3://退出游戏
                delete csnake;
                delete cfood;
                return 2;//将2作为PlayGame函数的返回值返回到Game函数中,表示退出游戏

            default:
                break;
            }
        }

        //自然一开始是吃不到食物的,所以-》NormalMove(),也就解释蛇的移动
        if (csnake->GetFood(*cfood)) //吃到食物,传递指针类的对象
        {
            //懂啦
            csnake->Move();//蛇增长
            UpdateScore(1);//更新分数,1为分数权重,每次加上key*10,key是对应难度
            RewriteScore();//重新绘制分数
            cfood->DrawFood(*csnake);//绘制新食物
        }
        else
        {
            //没碰到食物是就是,normalmove
            csnake->NormalMove();//蛇正常移动
        }

        if (csnake->GetBigFood(*cfood)) //吃到限时食物
        {
            csnake->Move();
            UpdateScore(cfood->GetProgressBar()/5);//分数根据限时食物进度条确定
            RewriteScore();
        }

        if (cfood->GetBigFlag()) //如果此时有限时食物,闪烁它
        {
            cfood->FlashBigFood();
        }

        Sleep(speed);//制造蛇的移动效果,控制蛇的快慢
    }

    /*蛇死亡*/
    delete csnake;//释放分配的内存空间
    delete cfood;
    int tmp = GameOver();//绘制游戏结束界面,并返回所选项
    switch (tmp)
    {
    case 1:
        return 1;//重新开始
    case 2:
        return 2;//退出游戏
    default:
        return 2;
    }
}

void Controller::UpdateScore(const int& tmp)//更新分数
{
    //懂啦
    score += key * 10 * tmp;//所得分数根据游戏难度及传人的参数tmp确定
}

void Controller::RewriteScore()//重绘分数
{
    /*为保持分数尾部对齐,将最大分数设置为6位,计算当前分数位数,将剩余位数用空格补全,再输出分数*/
    //懂啦
    SetCursorPosition(37, 8);
    SetColor(11);
    int bit = 0;
    int tmp = score;
    while (tmp != 0)
    {
        ++bit;
        tmp /= 10;
    }
    for (int i = 0; i < (6 - bit); ++i)
    {
        std::cout << " " ;
    }
    std::cout << score ;
}

int Controller::Menu()//选择菜单
{
    //整个懂啦
    /*绘制菜单*///懂啦
    SetColor(11);
    SetCursorPosition(32, 19);
    std::cout << "菜单:" ;
    Sleep(100);//用来显示动态效果
    SetCursorPosition(34, 21);
    SetBackColor();
    std::cout << "继续游戏" ;
    Sleep(100);
    SetCursorPosition(34, 23);
    SetColor(11);
    std::cout << "重新开始" ;
    Sleep(100);
    SetCursorPosition(34, 25);
    std::cout << "退出游戏" ;
    SetCursorPosition(0, 31);

    /*选择部分*/
    int ch;
    int tmp_key = 1;
    bool flag = false;
    while ((ch = getch()))
    {
        //cout<<ch<<endl;
        //这里依旧会分两次赋值给,ch,所以它这里写的是while,所以每按一次键,实际while运行了两次,第一次直接defalut,第二次才有相应的选择
        //ch = getch();在这里写个ch = getch()其实并不好,因为这样点enter就得按两次了
        //cout<<ch<<endl;
        switch (ch)
        {
        case 72://UP
            if (tmp_key > 1)
            {
                switch (tmp_key)
                {
                case 2:
                    SetCursorPosition(34, 21);
                    SetBackColor();
                    std::cout << "继续游戏" ;
                    SetCursorPosition(34, 23);
                    SetColor(11);
                    std::cout << "重新开始" ;

                    --tmp_key;
                    break;
                case 3:
                    SetCursorPosition(34, 23);
                    SetBackColor();
                    std::cout << "重新开始" ;
                    SetCursorPosition(34, 25);
                    SetColor(11);
                    std::cout << "退出游戏" ;

                    --tmp_key;
                    break;
                }
            }
            break;

        case 80://DOWN
            if (tmp_key < 3)
            {
                switch (tmp_key)
                {
                case 1:
                    SetCursorPosition(34, 23);
                    SetBackColor();
                    std::cout << "重新开始" ;
                    SetCursorPosition(34, 21);
                    SetColor(11);
                    std::cout << "继续游戏" ;

                    ++tmp_key;
                    break;
                case 2:
                    SetCursorPosition(34, 25);
                    SetBackColor();
                    std::cout << "退出游戏" ;
                    SetCursorPosition(34, 23);
                    SetColor(11);
                    std::cout << "重新开始" ;

                    ++tmp_key;
                    break;
                }
            }
            break;

        case 13://Enter
            flag = true;
            break;

        default:
            break;
        }//switch结束

        if (flag)
        {
            break;
        }
        SetCursorPosition(0, 31);
    }//while结束

    if (tmp_key == 1) //选择继续游戏,则将菜单擦除
    {
        SetCursorPosition(32, 19);
        std::cout << "      " ;
        SetCursorPosition(34, 21);
        std::cout << "        ";
        SetCursorPosition(34, 23);
        std::cout << "        ";
        SetCursorPosition(34, 25);
        std::cout << "        ";
    }
    return tmp_key;
}

void Controller::Game()//游戏一级循环
{

    Start();//开始界面
    while (true)//游戏可视为一个死循环,直到退出游戏时循环结束
    {

        Select();//选择界面
        DrawGame();//绘制游戏界面
        int tmp = PlayGame();//开启游戏循环,当重新开始或退出游戏时,结束循环并返回值给tmp:1,2
        if (tmp == 1) //返回值为1时重新开始游戏
        {
            system("cls");
            continue;
        }
        else//只要不是重新开始,就可以break了,结束了
        {
            break;
        }
    }
}

int Controller::GameOver()//游戏结束界面
{
    /*绘制游戏结束界面*/
    Sleep(500);
    SetColor(11);
    SetCursorPosition(10, 8);
    std::cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ;
    Sleep(30);
    SetCursorPosition(9, 9);
    std::cout << " ┃               Game Over !!!              ┃" ;
    Sleep(30);
    SetCursorPosition(9, 10);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 11);
    std::cout << " ┃              很遗憾!你挂了              ┃" ;
    Sleep(30);
    SetCursorPosition(9, 12);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 13);
    std::cout << " ┃             你的分数为:                 ┃" ;
    SetCursorPosition(24, 13);
    std::cout << score ;
    Sleep(30);
    SetCursorPosition(9, 14);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 15);
    std::cout << " ┃   是否再来一局?                         ┃" ;
    Sleep(30);
    SetCursorPosition(9, 16);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 17);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 18);
    std::cout << " ┃    嗯,好的        不了,还是学习有意思  ┃" ;
    Sleep(30);
    SetCursorPosition(9, 19);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(9, 20);
    std::cout << " ┃                                          ┃" ;
    Sleep(30);
    SetCursorPosition(10, 21);
    std::cout << "━━━━━━━━━━━━━━━━━━━━━━" ;

    Sleep(100);
    SetCursorPosition(12, 18);
    SetBackColor();
    std::cout << "嗯,好的" ;//默认选中,”嗯,好的“
    SetCursorPosition(0, 31);

    /*选择部分*/
    int ch;
    int tmp_key = 1;
    bool flag = false;
    while ((ch = getch()))
    {
        switch (ch)
        {
        case 75://LEFT
            if (tmp_key > 1)
            {
                SetCursorPosition(12, 18);
                SetBackColor();
                std::cout << "嗯,好的" ;
                SetCursorPosition(20, 18);
                SetColor(11);
                std::cout << "不了,还是学习有意思" ;
                --tmp_key;
            }
            break;

        case 77://RIGHT
            if (tmp_key < 2)
            {
                SetCursorPosition(20, 18);
                SetBackColor();
                std::cout << "不了,还是学习有意思" ;
                SetCursorPosition(12, 18);
                SetColor(11);
                std::cout << "嗯,好的" ;
                ++tmp_key;
            }
            break;

        case 13://Enter
            flag = true;
            break;

        default:
            break;
        }//switch结束

        SetCursorPosition(0, 31);
        if (flag) {
            break;
        }
    }//while结束

    SetColor(11);
    switch (tmp_key)//判断重新开始,还是结束
    {
    case 1:
        return 1;//重新开始,也就是,“嗯,好的”
    case 2:
        return 2;//退出游戏,也就是”不了,还是学习有意思“
    default:
        return 1;
    }
}


五:程序运行调用关系图。

原创文章 23 获赞 23 访问量 1678

猜你喜欢

转载自blog.csdn.net/LLM1602/article/details/106154357