学习了ncurses绘图库的一些函数的使用方法
本帖最后由 就是要努力呀 于 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) (指定多少时间后就发送信号)
因为视频中对这几个函数都是略过或者根本没讲 所以一点也没搞懂
参考这篇文章 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;
}
这样是可以的 人造人 发表于 2020-3-9 14:16
参考这篇文章 https://blog.csdn.net/lixianlin/article/details/25604779
我看完这篇文章,一开始写下 ...
好奥利给我会仔细看的{:10_256:}
页:
[1]