鱼C论坛

 找回密码
 立即注册
查看: 1233|回复: 2

[技术交流] 学习了ncurses绘图库的一些函数的使用方法

[复制链接]
发表于 2020-3-9 12:37:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 就是要努力呀 于 2020-3-9 12:37 编辑
#include <stdio.h>
#include <ncurses.h>

int main(void)
{
        initscr();      /*      */
        cbreak();       /*初始化ncurses(再使用ncurses库的都要先调用这三个函数)*/
        noecho();       /*      */

        curs_set(0);    //隐藏光标

        move(10, 10);   //移动光标
        addch('A');     //打印一个字符
        addstr("ABCD"); //打印一串字符串
        refresh();      //刷新屏幕

        getch();        //类似于getchar() 但getch()会在用户按键后立刻执行 不需要等待回车

        endwin();       //退出ncurses

        return 0;
}
感谢人造人大佬给我提出的ncurse函数库
今天在b站通过学习一位up主写flappybird这个游戏学习了ncurses库的一些使用方法,在这里分享给大家
ncurse库不是linux自带的库,所以需要在命令行执行 sudo apt-get install libncurses5-dev 安装这个库
在编译的时候也需要加上-lncurese选项连接这个库

以下是flappybird的代码 因为是一边跟着up写的 所以就没有注释
#include <stdio.h>
#include <ncurses.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>

#define BIRD 'o'
#define PIPE '*'
#define BLANK ' '

typedef struct Pipe
{
        int x;
        int y;
        struct pipe *next;
}
Pipe;

Pipe *HEAD, *TAIL;

int X, Y;        //小鸟坐标
                
void drop(int sig); 小鸟下坠
int set_ticker(int n_msec); 设定时间发送信号
void clear_bird(); 清楚小鸟之前的位置
void draw_bird(); 打印出小鸟现在的位置
void clear_pipe(int x, int y); 清除一个柱子之前的位置
void draw_pipe(int x, int y); 打印一个柱子现在的位置
void init_pipes(); 初始化柱子的链表
void draw_pipes(); 打印所有柱子
void clear_pipes(); 清除所有柱子
void move_pipes(); 移动所有柱子

void move_pipes()
{
        Pipe *p;

        for(p = HEAD->next; p->next != NULL; p = p->next)
        {
                p->x--;
        }
}

void draw_pipes()
{
        Pipe *p;

        for(p = HEAD->next; p->next != NULL; p = p->next)
        {
                draw_pipe(p->x, p->y);
        }
}

void clear_pipes()
{
        Pipe *p;

        for(p = HEAD->next; p->next != NULL; p = p->next)
        {
                clear_pipe(p->x, p->y);
        }
}

void init_pipes()
{
        HEAD = (Pipe *)malloc(sizeof(Pipe));

        if(HEAD == NULL)
        {
                exit(0);
        }

        HEAD->next = NULL;

        Pipe *temp, *p = HEAD;
        int i;

        for(i = 0; i < 5; i++)
        {
                temp = (Pipe *)malloc(sizeof(Pipe));

                if(temp == NULL)
                {
                        exit(0);
                }

                temp->x = (i + 1) * 20;
                temp->y = rand()%16 + 10;

                p->next = temp;
                temp->next = NULL;
                p = temp;
        }

        TAIL = p;
}

void draw_pipe(int x, int y)
{
        int i, j;

        for(i = 0; i < 5; i++)
        {
                for(j = 0; j < 20; j++)
                {
                        move(y - j - 5, x + i);
                        addch(PIPE);

                        move(y + j + 5, x + i);
                        addch(PIPE);
                }
        }
}

void clear_pipe(int x, int y)
{        
        int i, j;

        for(i = 0; i < 5; i++)
        {
                for(j = 0; j < 20; j++)
                {
                        move(y - j - 5, x + i);
                        addch(BLANK);

                        move(y + j + 5, x + i);
                        addch(BLANK);
                }
        }

}

void draw_bird()
{
        move(X, Y);
        addch(BIRD);
        refresh();

        if((char)inch() == PIPE)
        {
                set_ticker(0);
                endwin();
                exit(0);
        }
}

void clear_bird()
{
        move(X, Y);
        addch(' ');
        refresh();
}

int set_ticker(int n_msec)
{
        struct itimerval timeset;
        long int n_sec, n_usec;

        n_sec = n_msec / 1000;
        n_usec = (n_msec % 1000) * 1000L;

        timeset.it_interval.tv_sec = n_sec;
        timeset.it_interval.tv_usec = n_usec;

        timeset.it_value.tv_sec = n_sec;
        timeset.it_value.tv_usec = n_usec;

        return setitimer(ITIMER_REAL, ×et, NULL);
}

void drop(int sig)
{
        clear_bird();

        X++;

        draw_bird();

        clear_pipes();

        Pipe *p = HEAD->next;

        if(p->x < 0)
        {
                HEAD->next = p->next;

                free(p);

                Pipe *temp = (Pipe *)malloc(sizeof(Pipe));

                if(temp == NULL)
                {
                        exit(0);
                }

                temp->x = 80;
                temp->y = 15;
                TAIL->next = temp;
                temp->next = NULL;
                TAIL = temp;
        }

        move_pipes();
        draw_pipes();
}

int main(void)
{
        int i;
        char ch;

        X = 5;
        Y = 10;

        initscr();
        cbreak();
        noecho();
        curs_set(0);
        srand(time(NULL));
        
        signal(SIGALRM, drop);
        set_ticker(500);

        init_pipes();

        while(true)
        {
                clear_bird();
                draw_bird();
                clear_pipes();
                draw_pipes();

                if((ch = getch()) == ' ')
                {
                        clear_bird();

                        X--;
                }
                else if(ch == 'q' || ch == 'Q')
                {
                        break;
                }
        }

        endwin();
        
        return 0;
}
当然自己也有很多没有弄明白的地方
<sys/time.h>头文件
signal函数 signal(SIGALRM, drop)(我知道这个函数的意思是接受数据就执行drop函数)

还有set_ticker这个函数中定义的 itimerval 结构体
以及 setitimer函数 setitimer(ITIMER_REAL, &TIMESET, NULL) (指定多少时间后就发送信号)
因为视频中对这几个函数都是略过或者根本没讲 所以一点也没搞懂


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-3-9 14:16:24 | 显示全部楼层
参考这篇文章 https://blog.csdn.net/lixianlin/article/details/25604779

我看完这篇文章,一开始写下的测试代码是这样的
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

// 回调函数
void handler(int sig)
{
    static int count;
    printf("handler: %d\n", count++);
}

int main(void)
{
    // 注册回调函数
    // 当程序收到SIGALRM信号后
    // 暂停当前程序的执行,转去执行handler函数
    // handler函数返回后再从暂停的位置继续向下执行
    signal(SIGALRM, handler);
    struct itimerval new_value;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 500000;
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_usec = 0;
    
    // 设置定时器
    // 当定时器超时后让系统发送一个信号给当前程序
    // ITIMER_REAL参数指明要让系统发送SIGALRM信号
    setitimer(ITIMER_REAL, &new_value, NULL);
    while(1)
        ;
    return 0;
}

首先系统会在一个单位时间后对it_value递减,单位时间是多少,取决于你的系统,可能是10毫秒也可能是1毫秒,甚至是微妙、纳秒
总之就是系统会对it_value减一个合适的值,并不一定是减1,减多少取决于系统
系统每一个单位时间都对it_value减一个合适的值,直到it_value小于等于0
然后系统会做两件事
1.把it_interval复制到it_value,开始新一轮定时
2.发送一个信号,我们通过参数ITIMER_REAL要求发送SIGALRM
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 500000;
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_usec = 0;

我期望的是500毫秒发一个信号
调用完setitimer函数后立刻发一个信号,因为it_value为0

但是系统不允许我这样做,系统会把一开始的it_value为0作为定时器停止的标志

那么修改代码
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void handler(int sig)
{
    static int count;
    printf("handler: %d\n", count++);
}

int main(void)
{
    signal(SIGALRM, handler);
    struct itimerval new_value;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 500000;
    new_value.it_value = new_value.it_interval;
    setitimer(ITIMER_REAL, &new_value, NULL);
    while(1)
        ;
    return 0;
}

让it_value直接等于it_interval,也就是不要一开始就发信号了
这样确实可以了,500ms发一次信号

不知道你是不是对 tv_sec 和 tv_usec 的设置有疑问
我是应该只设置tv_sec还是只设置tv_usec  ?
这两个是怎么表示时间的?

https://blog.csdn.net/lyc_daniel/article/details/11733715
“tv_sec为Epoch到创建struct timeval时的秒数,tv_usec为微秒数,即秒后面的零头”

看到 “秒后面的零头”,我明白了
这两个参数是一起表示时间的,就像是整数和小数分开存储一样
tv_sec存储秒,tv_usec存储微秒

那我又有了另一个问题,我能不能把tv_sec设置为0,通过设置tv_usec来定时任意时间?
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void handler(int sig)
{
    static int count;
    printf("handler: %d\n", count++);
}

int main(void)
{
    signal(SIGALRM, handler);
    struct itimerval new_value;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 1000000;        // 1秒,6个0
    new_value.it_value = new_value.it_interval;
    setitimer(ITIMER_REAL, &new_value, NULL);
    while(1)
        ;
    return 0;
}


通过测试是
不能这样,tv_sec 表示秒的部分
tv_usec只能表示微秒和毫秒,会忽略超过毫秒的部分
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void handler(int sig)
{
    static int count;
    printf("handler: %d\n", count++);
}

int main(void)
{
    signal(SIGALRM, handler);
    struct itimerval new_value;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_usec = 100000;        // 100毫秒,5个0
    new_value.it_value = new_value.it_interval;
    setitimer(ITIMER_REAL, &new_value, NULL);
    while(1)
        ;
    return 0;
}

这样是可以的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-9 16:43:53 From FishC Mobile | 显示全部楼层
人造人 发表于 2020-3-9 14:16
参考这篇文章 https://blog.csdn.net/lixianlin/article/details/25604779

我看完这篇文章,一开始写下 ...

好奥利给我会仔细看的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-15 20:59

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表