一个简单的基于双缓冲区的控制台绘制类 C++实现

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <vector>


// 头文件
class Drawer {
private:
    char** frame_;
    HANDLE output, buffer;
    COORD coord = { 0,0 };
    DWORD bytes = 0;

    int control_ = 0; // 用于切换输出区和缓冲区
    int lenth_; // 绘制区域长度
    int width_;// 绘制区域宽度
    double fps_; // 帧数
public:
    Drawer(int len = 10,int wid = 10, double fps = 10) :lenth_(len), width_(wid), fps_(1000.0 / fps) {
        if(lenth_ < 0)
            lenth_ = 15;
        if(width_ < 0)
            width_ = 15;
        if(fps_ < 0)
            fps_ = 20;
        Init();
    }
    void Init();
    void GetFrame(char** frame) {
        frame_ = frame;
    }
    void Draw();
};

// 实现
void Drawer::Draw() {
    for (int i = 0; i < width_; i++)
    {
        coord.Y = i;
        if (!control_)
            WriteConsoleOutputCharacterA(buffer, frame_[i], lenth_, coord, &bytes);
        else
            WriteConsoleOutputCharacterA(output, frame_[i], lenth_, coord, &bytes);
    }
    if (!control_)
        SetConsoleActiveScreenBuffer(buffer);
    else

        SetConsoleActiveScreenBuffer(output);
    Sleep(fps_);
    control_ = !control_;
}

void::Drawer::Init() {
    buffer = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    output = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = 0;
    cci.dwSize = 1;
    SetConsoleCursorInfo(output, &cci);
    SetConsoleCursorInfo(buffer, &cci);
}


使用方法:

使用前要先初始化一个Drawer对象,传入三个参数,分别是绘制区域长度、绘制区域宽度以及帧率。

用GetFrame()方法传入一个char**变量,该二维char数组储存当前要绘制的帧的内容。

Draw()方法绘制当前帧。

用例一:

int main() {
    Drawer a(15,15,20);  // 初始化对象
    char** data;
    data = new char* [16];
    for (int i = 0; i < 16; i++) {
        data[i] = new char[16];
    }
    char c = 'a';
    while (c <='z') {
        for (int i = 0; i < 15; i++){
            for (int j = 0; j < 15; j++){
                data[i][j] = c;
            }
        }
        a.GetFrame(data); // 传入帧
        a.Draw(); // 绘制
        c++;
    }
}

用例二:

把这个类插入了上学期写的贪吃蛇里,解决了闪屏问题

#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <ctime>
#include <stdio.h>
#include <stdlib.h>

#include <cstdlib>
using namespace std;
class Drawer {
private:
    char** frame_;
    HANDLE output, buffer;
    COORD coord = { 0,0 };
    DWORD bytes = 0;

    int control_ = 0; // 用于切换输出区和缓冲区
    int lenth_; // 绘制区域长度
    int width_;// 绘制区域宽度
    double fps_; // 帧数
public:
    Drawer(int len = 10, int wid = 10, double fps = 10) :lenth_(len), width_(wid), fps_(1000.0 / fps) {
        if (lenth_ < 0)
            lenth_ = 15;
        if (width_ < 0)
            width_ = 15;
        if (fps_ < 0)
            fps_ = 20;
        Init();
    }
    void Init();
    void GetFrame(char** frame) {
        frame_ = frame;
    }
    void Draw();
};

struct pos {//坐标点
    int x;//列
    int y;//行d
};

const int X = 35;
const int Y = 18;

char** screen;

Drawer D(35,18,20);

pos head{ 4,5 };//蛇头初始位置
pos foodpos{ 19, 9 }; //食物初始位置
pos body[100];//存储蛇身位置的pos数组

bool life = 1;
int score = 0;

const int speed = 1;
int moveX, moveY;//x,y坐标的增量
int lenth = 3;//蛇的初始长度
int os;//用户的输入,见get_input()函数

int get_rannum(int left, int right);
void get_input();
void hide_cursor(bool Visible);
bool food_eaten();
void update_pos();
bool is_body(int y, int x);
void fill_screen();
void draw();


int main() {
    screen = new char* [Y]; // Y 是为行
    for (int i = 0; i < Y; i++) {
        screen[i] = new char[X]; // X是为列
    }
    

    for (int i = 0; i < lenth; i++) {
        body[i] = head;
    }
    hide_cursor(true);
    while (life) {
        get_input();
        update_pos();
        fill_screen();
        draw();
    }
}


int get_rannum(int left, int right)//返回一个[left,right]内的整型数据
{
    srand((unsigned)time(NULL));
    return (rand() % (right - left + 1)) + left;
}

void get_input() {//获取用户输入
    if (_kbhit()) os = _getch();//这一段可以直接抄
    if (char(os) == 'a') {
        moveX = -speed; moveY = 0;
    }
    if (char(os) == 'd') {
        moveX = speed; moveY = 0;
    }
    if (char(os) == 'w') {
        moveY = -speed; moveX = 0;
    }
    if (char(os) == 's') {
        moveY = speed; moveX = 0;
    }

}

void hide_cursor(bool Visible)//隐藏光标 直接抄就完事了
{
    CONSOLE_CURSOR_INFO Cursor;
    Cursor.bVisible = !Visible;
    Cursor.dwSize = sizeof(Cursor);
    HANDLE Hand = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorInfo(Hand, &Cursor);
}

bool food_eaten() {
    if (head.x == foodpos.x && head.y == foodpos.y) {//若蛇头碰到了食物
        return true;
    }
    return false;
}

void update_pos() {//蛇的身体坐标更新,食物位置更新
    for (int i = 0; i < lenth - 1; i++) {//这是贪吃蛇实现中唯一一个需要动脑子的地方,注释说不清楚,自己想想吧~
        body[i] = body[i + 1];
    }
    body[lenth - 1] = head;
    head.x += moveX;
    head.y += moveY;
    if (food_eaten()) {
        lenth++;
        foodpos.x = get_rannum(5, 30);
        foodpos.y = get_rannum(5, 15);
    }

}

bool is_body(int y, int x) {//坐标点x,y是不是蛇的身体
    for (int i = 0; i < lenth; i++) {
        if (y == body[i].y && x == body[i].x) {
            return true;
        }
    }
    return false;
}

void fill_screen() {//填充屏幕矩阵
    for (int y = 0; y < Y; y++) {
        for (int x = 0; x < X; x++) {
            if (x == head.x && y == head.y) {
                screen[y][x] = 'O';
            }
            else if (y == 0 || y == Y - 1) {
                screen[y][x] = '-';
            }
            else if (x == 0 || x == X - 1) {
                screen[y][x] = '|';
            }
            else if (is_body(y, x))
            {
                screen[y][x] = 'o';
            }
            else if (y == foodpos.y && x == foodpos.x) {
                screen[y][x] = '%';
            }
            else {
                screen[y][x] = ' ';
            }
        }
    }
    D.GetFrame(screen);
}

void draw() {//绘制图案
    D.Draw();
}

void Drawer::Draw() {
    for (int i = 0; i < width_; i++)
    {
        coord.Y = i;
        if (!control_)
            WriteConsoleOutputCharacterA(buffer, frame_[i], lenth_, coord, &bytes);
        else
            WriteConsoleOutputCharacterA(output, frame_[i], lenth_, coord, &bytes);
    }
    if (!control_)
        SetConsoleActiveScreenBuffer(buffer);
    else

        SetConsoleActiveScreenBuffer(output);
    Sleep(fps_);
    control_ = !control_;
}

void::Drawer::Init() {
    buffer = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    output = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = 0;
    cci.dwSize = 1;
    SetConsoleCursorInfo(output, &cci);
    SetConsoleCursorInfo(buffer, &cci);
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值