C++之贪吃蛇 (详细注解)

                                      代码仅在VS2017和Dev-C++6.0编译执行. 并未测试在其它编译器上编译情况。
                                                        第一次写游戏,第一次写博客,如有不足还请指出,谢谢!


_getch()

getch()是编程中所用的函数,这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏,但是这个函数并非标准函数,要注意移植性!百度百科

_getch()每次读入一个字节, 上下左右为两个字节(第一个字节ASCII码为224),空格(32)回车(13)为一个字节,因此我们设置两个变量来保存,先判断第一个字节是否为224,是就意味着读入了方向键.否就判断是否读入的是32 or 13 判断是确认模式 or 暂停游戏.


自动运行时间

百度百科写的很清楚了

  • CLOCKS_PER_SEC
  • clock()
1. double start_time = (double)clock() / CLOCKS_PER_SEC;//获取到 1. 的时间

//[运行到 2. 的时间 - 运行到 1. 的时间](单位 s) > [autoSpeed(单位 ms) / 1000.0]
2. (double)clock() / CLOCKS_PER_SEC - start_time > autoSpeed / 1000.0

游戏截图

C++之贪吃蛇 (详细注解)_第1张图片
C++之贪吃蛇 (详细注解)_第2张图片


有问题可以留言, 我都会回答的, 但作为一名大一竞赛狗, 水平有限.


时间

代码

/************************贪吃蛇***********************/
/*                     by:yinggcy                   */      
/**********************2017/10/07********************/

#include 
#include 
#include                 //包含 隐藏光标
#include                   //包含 _getch() 和 _kbhit()
#include 
#define N 30
#define Up 1                        //方向键对应的的ASCII为 72
#define Down 2                      //的ASCII为   80
#define Left 3                      //的ASCII为   75
#define Right 4                     //的ASCII为   77

using namespace std;

class Map {
private:
    class snake {                   //蛇坐标类
    public:
        int x;
        int y;
    };
    class Food {                    //食物坐标类
    public:
        int x;
        int y;
    };
    Food food;
    snake head, tail;               //蛇头 蛇尾
    deque<int> direction;           //保存每次前进的方向,最近一次前进添加到最前面
    int mod;                        //游戏模式 普通模式 == 0, 冒险模式 == 1;
    int score;
    int grade;
    int autoSpeed;                  //更新游戏自动更新速度
    char map[N][N];                 //保存输出      //如 xx[Up] = 80 Down的ASCII码为80
    int opposite_direction[5] = {0, 80, 72, 77, 75};//保存各方向的反方向的ASCII码(接上面)
    int speed[9] = {0, 800, 600, 500, 420, 250, 100, 40, 5};//保存自动前进速度,单位ms
public:
    Map() : head({N / 2, N / 2}), tail({N / 2, N / 2 - 2}),     //构造函数
        mod(0), score(0), grade(1), autoSpeed(speed[grade]) { }
    void Hello();                   //欢迎界面
    void Print_Hello(int);          //打印欢迎界面
    void Start_Game();
    void Initialize_Map();
    int Update_Map();
    void Show_Map();
    void Rand_Food();
    void Reset();                   //复位
};

void Map::Hello()
{
    while (true){
        system("cls");                  //清屏
        Print_Hello(mod);

        if (_getch() != 13){                //判断是否为回车键,确定选择的模式
            switch (_getch()){              //判断上下移动
            case 80: mod = 1; break;
            case 72: mod = 0; break;
            }
            continue;
        }                           
        //Enter 的ASCII码为 13, 按了回车确定模式
        if (mod == 0){                              //普通模式
                do {
                    system("cls");
                    cout << "\n\n\n\n\t\t\t 请输入你需要的游戏级别: (1~8)   ";
                    cin >> grade;
                } while (grade > 8 || grade < 1);   //判断等级输入是否正确
                autoSpeed = speed[grade];           //更新自动快进速度
                break;
            }
            else                                    //冒险模式
                break;
        }
    Start_Game();               //开始游戏
}

void Map::Print_Hello(int mod)
{
    srand((unsigned)time(nullptr));     //用时间产生随机种子,百度一下,你便知道
    cout << "\n\n\n\t\t\t\t 贪吃蛇 \n\n\n\n";
    cout << "\t\t\t     请选择你的模式:";
    cout << "\n\n\t\t\t     普通模式:";
    if (mod == 0) cout << "   *";       //让玩家知道未按回车确定前选择的模式
    cout << "\n\n\t\t\t     冒险模式:";
    if (mod == 1) cout << "   *";
    cout << "\n\n\n\n\n\n\n\n 版本:V1.0        \t\t\t\t\t 作者: yinggcy";
}

void Map::Start_Game()
{
    char choose;
    Initialize_Map();           //初始化地图
    Rand_Food();                //产生第一个食物
    do {
        Show_Map();
    } while (Update_Map());

    cout << "\t\t Game Over!!!     你先继续吗? (Y/Others)";
    cin >> choose;
    if (choose == 'Y' || choose == 'y'){
        Reset();
        Hello();
    }
}

void Map::Initialize_Map()
{
    for (int i = 0; i < N; i++)                     //左右边框
        map[0][i] = map[N - 1][i] = '*';
    for (int i = 1; i < N; i++)                     //上下边框
        map[i][0] = map[i][N - 1] = '*';
    for (int i = 1; i < N - 1; i++)                 //中间空白
        for (int j = 1; j < N - 1; j++)
            map[i][j] = ' ';

    map[N / 2][N / 2] = '@';                                //初始蛇头
    map[N / 2][N / 2 - 1] = map[N / 2][N / 2 - 2] = 'O';    //初始蛇身
    direction.push_front(Right);        //初始化 direction
    direction.push_front(Right);        //因为初始为 OO@ 尾部要进行两个向右才能到蛇头位置
}

void Map::Show_Map()                            //输出游戏画面
{
    system("cls");
    for (int i = 0; i < N; i++){
        cout << "\t";
        for (int j = 0; j < N; j++)
            cout << map[i][j] << ' ';
        if (i == N / 4)
            cout << "\t Score: " << score;
        if (i == N / 4 + 4)
            cout << "\t grade: " << grade;
        if (i == N / 4 + 8)
            cout << " 按空格键暂停/继续";
        cout << endl;
    }
}

int Map::Update_Map()                           //更新游戏(最主要的)
{
    int tmp_direction, tmp_others, sign = 0;    //sign == 1(读入了方向键)         
    double start_time = (double)clock() / CLOCKS_PER_SEC;//读入执行这段代码时的时间

    do {
        if (_kbhit()){          //_kbhit()判断是否有按键输入,但不读入.
            tmp_others = _getch();              //代码前有介绍
            if (tmp_others == 32)               //为空格键 暂停游戏
                while (_getch() != 32);         //当读入空格键退出循环
            else if (tmp_others == 224){ //224为方向键的第一个字节的ASCII码
                    tmp_direction = _getch();
                if (opposite_direction[direction.front()] == tmp_direction) 
                //是否为上一次方向的反向,在这个设定下长度<=4不会吃到自己(下接)
                    continue;   //假设当前蛇为: OO@ 如果输入左转向 判断为无效按钮,继续循环  
                switch (tmp_direction){                                 //判断方向
                case 72: direction.push_front(Up); sign = 1; break;
                case 80: direction.push_front(Down); sign = 1; break;
                case 75: direction.push_front(Left); sign = 1; break;
                case 77: direction.push_front(Right); sign = 1; break;
                }
                if (sign) break;                //读入了方向键,推出循环 判断情况
            }
        }
        //未读入方向键或空格,判断是否超过自动前进时间
        if ((double)clock() / CLOCKS_PER_SEC - start_time > autoSpeed / 1000.0){//用法,代码前有介绍
            direction.push_front(direction.front());    //自动前进,和前一次前进方向一样
            break;
        }
    } while (true);

    map[head.x][head.y] = 'O';          //更新地图把蛇头原蛇头位置置为蛇身
    switch (direction.front()){         //更新蛇头坐标
    case Up: head.x -= 1; break;
    case Down: head.x += 1; break;
    case Left: head.y -= 1; break;
    case Right: head.y += 1; break;
    }

    if (head.x == food.x && head.y == food.y){  //是否吃到食物
        map[head.x][head.y] = '@';
        score += 10;
        if (mod == 1){          //更新冒险模式的游戏等级和速度,每吃6个增加一级,最高5级
            if (score / 60 >= grade){
                if (grade < 5)
                    grade++;
                autoSpeed = speed[grade];
            }
        }
        Rand_Food();                    //更新食物
    }
    else if (map[head.x][head.y] != ' ' && !(head.x == tail.x && head.y == tail.y)) 
        return 0;   //if(更新的头位置是否不为地图中的空白 && 是否不为蛇尾) (接下)
        //前面已经判断没吃到食物,所以蛇尾会前进,因为只更新了蛇头没有更新蛇尾,所以排除蛇尾情况
    else {                              //没有吃到食物    
        map[tail.x][tail.y] = ' ';      //更新地图蛇尾
        map[head.x][head.y] = '@';      //更新地图蛇头
        switch (direction.back()){      //更新蛇尾坐标
        case Up: tail.x -= 1; break;
        case Down: tail.x += 1; break;
        case Left: tail.y -= 1; break;
        case Right: tail.y += 1; break;
        }
        direction.pop_back();           //弹出 前 蛇尾坐标
    }
    return 1;                           //未撞墙或吃到自己
}

void Map::Rand_Food()
{
    do {
        food.x = rand() % (N - 2) + 1;
        food.y = rand() % (N - 2) + 1;
    } while (map[food.x][food.y] != ' ');
    map[food.x][food.y] = '%';
}

void Map::Reset()
{
    Initialize_Map();
    head = {N / 2, N / 2};
    tail = {N / 2, N / 2 - 2};
    mod = 0;
    score = 0;
    grade = 1;
    autoSpeed = 800;
    direction.clear();
}

int main()
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);                    //  百度
    CONSOLE_CURSOR_INFO CursorInfo;                                     //  搜的
    GetConsoleCursorInfo(handle, &CursorInfo);      //获取控制台光标信息 //  隐藏
    CursorInfo.bVisible = false;                    //隐藏控制台光标       //  光标
    SetConsoleCursorInfo(handle, &CursorInfo);      //设置控制台光标状态 //  方法

    Map m;
    m.Hello();

    return 0;
}

你可能感兴趣的:(控制台游戏)