C++贪吃蛇 新手入门项目 多线程 双缓冲

近期入门学习C++ 做的一个小项目练手

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

Gitee开源地址
单文件源码300行
使用了双缓冲技术来消除system(“cls”)刷新时带来的闪烁
多线程监听按键,拒绝阻塞
学习了对象的继承等
新人初学练手项目
代码结构观感较差,仅仅实现了功能
大佬勿喷
下载后编译运行即可
欢迎小伙伴尝试~

#include <iostream>
#include <windows.h>
#include <vector>
#include <ctime>
#include <unordered_set>
#include <thread>
#include <conio.h>

using namespace std;
const int ROW = 22;//行
const int COL = 42;//列
enum dir
{
    UP, DOWN, LEFT, RIGHT
};

class Refresh
{
protected:
    HANDLE hOutput;
    HANDLE hOutBuf;
    COORD coord;
    char data[4000];
    //双缓冲处理显示
    DWORD bytes = 0;

public:
    Refresh()
    {
        //获取默认标准显示缓冲区句柄
        coord = { 0, 0 };
        hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

        //创建新的缓冲区
        hOutBuf = CreateConsoleScreenBuffer(
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                CONSOLE_TEXTMODE_BUFFER,
                NULL
        );

        //设置新的缓冲区为活动显示缓冲
        SetConsoleActiveScreenBuffer(hOutBuf);

        //隐藏两个缓冲区的光标
        CONSOLE_CURSOR_INFO cci;
        cci.bVisible = 0;
        cci.dwSize = 1;
        SetConsoleCursorInfo(hOutput, &cci);
        SetConsoleCursorInfo(hOutBuf, &cci);

    }

    void refreshFrame()
    {

        ReadConsoleOutputCharacterA(hOutput, data, 4000, coord, &bytes);
        WriteConsoleOutputCharacterA(hOutBuf, data, 4000, coord, &bytes);
        FillConsoleOutputCharacterA(hOutput, ' ', bytes, coord, &bytes);
        //FlushFileBuffers(hOutput);//清空暂存缓存区

        SetConsoleCursorPosition(hOutput, { 0, 0 });//设置光标位置
    }

    void setCursorPosition(int x, int y)
    {
        SetConsoleCursorPosition(hOutput, { (short)(x), (short)y });
    }

    void loadMap()
    {
        for (int i = 0; i < ROW; i++)
        {
            for (int j = 0; j < COL; j++)
            {
                if (j == 0 || j == COL - 1)
                {
                    setCursorPosition(2 * j, i);
                    printf("■");
                }
                else if (i == 0 || i == ROW - 1)
                {
                    printf("■");
                }
            }
        }
        setCursorPosition(0, ROW);
        printf("得分:");
    }
};

class Snake : public Refresh
{
protected:
    struct b
    {
        int x, y;
    };
    std::vector<struct b> body;
public:
    Snake()
    {
        body.push_back({ COL / 2, ROW / 2 });//头
        body.push_back({ COL / 2 - 1, ROW / 2 });//身体
        body.push_back({ COL / 2 - 2, ROW / 2 });//身体
    }

    void printSnake()
    {
        setCursorPosition(2 * body[0].x, body[0].y);
        printf("■");
        for (int i = 1; i < body.size(); ++i)
        {
            setCursorPosition(2 * body[i].x, body[i].y);
            printf("□");
        }
    }

    void moveSnake(int dir)
    {
        for (int i = body.size() - 1; i >= 1; --i)
        {
            body[i] = body[i - 1];
        }
        switch (dir)
        {
            case UP:
                body[0].y--;
                break;
            case DOWN:
                body[0].y++;
                break;
            case LEFT:
                body[0].x--;
                break;
            case RIGHT:
                body[0].x++;
                break;
        }
    }

    void eatSnake()
    {
        body.push_back({ body.back().x, body.back().y });
    }
};

class Game final : public Snake
{
public:
    static bool isgameOver;
    int score;
    static int dir;
protected:
    /*
    struct pair_hash
    {
        inline size_t operator()(const std::pair<int, int>& p) const
        {
            return p.first * p.second;
        }
    };
    */
    struct pair_hash
    {
        template <class T1, class T2>
        size_t operator()(pair<T1, T2> const& pair) const
        {
            size_t h1 = hash<T1>()(pair.first); //用默认的 hash 处理 pair 中的第一个数据 X1
            size_t h2 = hash<T2>()(pair.second);//用默认的 hash 处理 pair 中的第二个数据 X2
            return h1 ^ h2;
        }
    };

    std::unordered_set<std::pair<int, int>, pair_hash> s;


public:
    Game()
    {
        srand((unsigned int)time(NULL));
        score = 0;
    }


    inline int getRandinRange(int a, int b)
    {
        return (rand() % (b - a + 1)) + a;
    }

    void generateFood()
    {
        int x, y;
        do
        {
            x = getRandinRange(1, COL - 2);
            y = getRandinRange(1, ROW - 2);

        } while (isOnSnake(x, y) || s.count({ x, y }));
        s.insert({ x, y });
    }

    void setFood()
    {
        for (auto it = s.begin(); it != s.end(); ++it)
        {
            setCursorPosition(2 * it->first, it->second);
            printf("●");
        }
    }

    bool isOnSnake(int x, int y)
    {
        for (const b& b : body)
        {
            if (b.x == x && b.y == y) return true;
        }
        return false;
    }

    void updateScore()
    {
        setCursorPosition(6, ROW);
        printf("%d", score);
    }

    bool gameOver()
    {
        int x = body[0].x;
        int y = body[0].y;
        if (x == 0 || y == 0 || x >= COL - 1 || y >= ROW - 1) return true;
        for (int i = 1; i < body.size(); ++i) if (x == body[i].x && y == body[i].y) return true;
        return false;
    }

    bool hasFood(int x, int y)
    {
        if (s.count({ x, y })) return true;
        else return false;
    }

    void judge()
    {
        int x = body[0].x;
        int y = body[0].y;
        if (hasFood(x, y))
        {
            s.erase({ x, y });
            score += 10;
            eatSnake();
            generateFood();
        }
    }
};

int Game::dir = RIGHT;
bool Game::isgameOver = false;

void listenKey()//按键监听线程
{
    while (1)
    {
        if (Game::isgameOver) ExitThread(0);
        int key = getch();
        switch (key)
        {
            case 72:
                if (Game::dir == UP || Game::dir == DOWN);
                else Game::dir = UP;
                break;
            case 80:
                if (Game::dir == UP || Game::dir == DOWN);
                else Game::dir = DOWN;
                break;
            case 75:
                if (Game::dir == LEFT || Game::dir == RIGHT);
                else Game::dir = LEFT;
                break;
            case 77:
                if (Game::dir == LEFT || Game::dir == RIGHT);
                else Game::dir = RIGHT;
                break;
        }

    }
}

int main()
{
    system("title 儒");
    system("mode con cols=84 lines=23");
    gameBegin:

    Game::dir = RIGHT;
    Game::isgameOver = false;
    Game g;
    std::thread task(listenKey);
    task.detach();
    g.loadMap();
    g.printSnake();
    g.generateFood();
    g.setFood();
    g.updateScore();
    g.refreshFrame();

    while (1)
    {
        g.loadMap();
        g.moveSnake(Game::dir);
        g.printSnake();
        g.judge();
        if (g.gameOver())
        {
            Game::isgameOver = true;
            g.setCursorPosition(COL - 5, ROW / 2);
            printf("GAME OVER !");
            g.setCursorPosition(COL - 5, ROW / 2 + 1);
            printf("Wanna retry?(y/n)");
            g.refreshFrame();
            while (1)
            {
                int key = getch();
                if (key == 121 || key == 89) goto gameBegin;
                else if (key == 110 || key == 78) break;
            }
        }
        g.setFood();
        g.updateScore();
        g.refreshFrame();
        this_thread::sleep_for(chrono::milliseconds(50));//移动速度
    }


    system("pause");
    return 0;
}

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JesseChan45

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

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

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

打赏作者

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

抵扣说明:

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

余额充值