linux字符界面下贪吃蛇游戏…
2014-09-11 15:18
597 查看
原文地址:linux字符界面下贪吃蛇游戏***作者:raosea
一、视频游戏如何做
(1)空间:游戏必须在计算机屏幕的特定位置画影像。程序如何控制视频显示?
(2)时间:影像以不同的速度在屏幕上移动。以一个特定的时间间隔改变位置。程序是如何获知时间并且在特定的时间安排事情的发生?
(3)中断:程序在屏幕上平滑移动的物体,用户可以在任何时刻产生输入。程序是如何响应中断的?
(4)同时做几件事:游戏必须在保持几个物体移动的同时还要响应中断。程序是如何同时做多件事情而不被弄得晕头转向的?
二、操作系统面临着类似的问题
操作系统同样要面对这4个问题。内核将程序载入内存空间并维护每个程序在内存中所处的位置。在内核的调度下,程序以时间片间隔的方式运行,同时,内核也在特定的时刻运行特定的内部任务。内核必须在很短的时间内响应用户和外设在任何时刻的输入。同时做几件事需要一些技巧。内核是如何保证数据的有序和规整的?
上面的都是那本书上说的,个人觉得讲的很好,看完这本后再看那本Linux圣经《Unix环境高级编程》或许更好些。回归正题吧,主要介绍一下设计一个终端下的贪吃蛇游戏所实现的功能以及所需要的几个条件和一些函数。
本贪吃蛇实现的功能是通过吃食物来增长自己的长度,可以利用按键 'f' 实现加速和 's' 键实现减速, 'q'
键退出,方向键控制方向,蛇要是碰到自己的身体或者碰到墙或者吃到一定数量,那么游戏就结束。功能还是挺简单的吧,下面就介绍下各个步骤的设计:
1.首先要使用终端图形库curses.h文件,由于不是C标准库,一般电脑不会自带,需要自行下载安装,ubuntu下可以这么下载 sudo
apt-get install libncurses5-dev
已经替换成ncurses.h 即 new
curses.h的意思,完全兼容curses。介绍下此游戏需要用到的常见的几个curses函数。
基本curse函数
更详细的可以
man ncurses
或者参见http://bbs.chinaunix.net/viewthread.php?tid=909369
2.介绍完ncurses图形库,接下来进行屏幕绘图,我初始化屏幕效果图见下图所示:先是外围边框,然后是蛇“@”和食物“*”。
废话不多说,上代码吧。
首先是头文件
snake.h的代码:由于在纯文本模式下编程以及本人英语水平有限,可能有的注释比较别扭。
[cpp] view
plaincopy
"font-size:13px;">
#define SNAKE_SYMBOL '@'
#define FOOD_SYMBOL '*'
#define MAX_NODE 30
#define DFL_SPEED 50
#define TOP_ROW 5
#define BOT_ROW LINES - 1
#define LEFT_EDGE 0
#define RIGHT_EDGE COLS - 1
typedef struct node
{
int x_pos;
int y_pos;
struct node *prev;
struct node *next;
} Snake_Node;
struct position
{
int x_pos;
int y_pos;
} ;
void Init_Disp();
void Food_Disp();
void Wrap_Up();
void Key_Ctrl();
int set_ticker(int n_msecs);
void DLL_Snake_Create();
void DLL_Snake_Insert(int x, int y);
void DLL_Snake_Delete_Node();
void DLL_Snake_Delete();
void Snake_Move();
void gameover(int n);
接下来是初始化界面图形的子函数:
[cpp] view
plaincopy
"font-size:13px;">
void Init_Disp()
{
char wall = ' ';
int i, j;
initscr();
cbreak();
noecho();
curs_set(0);
attrset(A_NORMAL);
attron(A_REVERSE);
for(i = 0; i < LINES; i++)
{
mvaddch(i, LEFT_EDGE, wall);
mvaddch(i, RIGHT_EDGE, wall);
}
for(j = 0; j < COLS; j++)
{
mvaddch(0, j, '=');
mvaddch(TOP_ROW, j, wall);
mvaddch(BOT_ROW, j, wall);
}
attroff(A_REVERSE);
mvaddstr(1, 2, "Game: snake version: 1.0 date: 2011/08/22");
mvaddstr(2, 2, "Author: Dream Fly Blog: blog.csdn.net/jjzhoujun2010");
mvaddstr(3, 2, "Usage: Press 'f' to speed up, 's' to speed down,'q' to quit.");
mvaddstr(4, 2, " Nagivation key controls snake moving.");
refresh();
}
void Food_Disp()
{
srand(time(0));
food.x_pos = rand() % (COLS - 2) + 1;
food.y_pos = rand() % (LINES - TOP_ROW - 2) + TOP_ROW + 1;
mvaddch(food.y_pos, food.x_pos, FOOD_SYMBOL);
refresh();
}
void DLL_Snake_Create()
{
Snake_Node *temp = (Snake_Node *)malloc(sizeof(Snake_Node));
head = (Snake_Node *)malloc(sizeof(Snake_Node));
tail = (Snake_Node *)malloc(sizeof(Snake_Node));
if(temp == NULL || head == NULL || tail == NULL)
perror("malloc");
temp->x_pos = 5;
temp->y_pos = 10;
head->prev =NULL;
tail->next = NULL;
head->next = temp;
temp->next = tail;
tail->prev = temp;
temp->prev = head;
mvaddch(temp->y_pos, temp->x_pos, SNAKE_SYMBOL);
refresh();
}
3.接下来就是蛇的移动问题,这个是核心部分以及最困难的设计部分了,我采用的是蛇用双向链表的结构来构造出来,分别有一个head
和tail指针,用来添加和删除元素。这里若要实现移动的话(未碰到食物前),就是在链表的头部(head的下一个)插入一个新元素,记录下此时的坐标,用mvaddch(y,x,c)函数添加蛇的图形'@',与此同时,在链表尾部(tail的前一个)删除一个节点,同时这里的坐标用mvaddch(y,x,
' ')添加了' '空白字符,实现删除效果,最后加上refresh().
这样就可以看到蛇在“移动”了。当然,要是碰到食物的话,尾部节点处就不用删除,达到增长长度的效果。
那么,接下来的问题是:如何触发蛇的移动呢?如何实现均匀移动以及通过按键 ‘f’ 或 's'
改变运动速度呢?这里我采用的是信号计时中断调用的函数 signal(SIGALRM,
Snake_Move) 和
间隔计数器来实现,通过产生相同间隔的时间片段来不断地调用Snake_Move()函数来执行相应的功能。加减速的功能是通过设定其他变量ttm,
ttg来实现再此基本计数器上面再次分频的效果来加减速,ttm, ttg 越大,减速越明显,反之则相反效果。
具体的间隔计数器的函数设计见下:(参考了以上所提书本上的函数)
[cpp] view
plaincopy
int set_ticker(int n_msecs)
{
struct itimerval new_timeset;
long n_sec, n_usecs;
n_sec = n_msecs / 1000;
n_usecs = (n_msecs % 1000) * 1000L;
new_timeset.it_interval.tv_sec = n_sec;
new_timeset.it_interval.tv_usec = n_usecs;
new_timeset.it_value.tv_sec = n_sec;
new_timeset.it_value.tv_usec = n_usecs;
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}
蛇的移动的函数代码如下:
[cpp] view
plaincopy
void Snake_Move()
{
static int length = 1;
int Length_Flag = 0;
int moved = 0;
signal(SIGALRM, SIG_IGN);
if((head->next->x_pos == RIGHT_EDGE-1 && x_dir == 1)
|| (head->next->x_pos == LEFT_EDGE+1 && x_dir == -1)
|| (head->next->y_pos == TOP_ROW+1 && y_dir == -1)
|| (head->next->y_pos == BOT_ROW-1 && y_dir == 1))
{
gameover(1);
}
if(mvinch(head->next->y_pos + y_dir, head->next->x_pos + x_dir) == '@')
gameover(2);
if(ttm > 0 && ttg-- == 1)
{
DLL_Snake_Insert(head->next->x_pos + x_dir, head->next->y_pos + y_dir);
ttg = ttm;
moved = 1;
}
if(moved)
{
if(head->next->x_pos == food.x_pos && head->next->y_pos == food.y_pos)
{
Length_Flag = 1;
length++;
if(length >= MAX_NODE)
gameover(0);
Food_Disp();
}
if(Length_Flag == 0)
{
mvaddch(tail->prev->y_pos, tail->prev->x_pos, ' ');
DLL_Snake_Delete_Node();
}
mvaddch(head->next->y_pos, head->next->x_pos, SNAKE_SYMBOL);
refresh();
}
signal(SIGALRM, Snake_Move);
}
主要函数的实现就是这些,以下贴上完整的源代码供大家参考,或者去这里下载:http://download.csdn.net/source/3540117
通过本程序可以加强自己对信号间隔计数器的理解,以及终端图形编程的理解和了解设计的此类游戏的一般思路,也实现了我大一学习C语言所想要实现的想法。本人第一次在CSDN上面把自己的一些编程想法写的比较详细,不足之处还请指出,共同讨论。
参考资料:《Unix/Linux编程实践教程》
(美)Bruce Molay 著
杨宗源 黄海涛
译
清华大学出版社
http://note.sdo.com/my#!note/preview/xJ29Q~jAYzOpnM01Y0004K
curses库的使用
http://blog.sina.com.cn/s/blog_4c3b26e10100sd7b.html
贪吃蛇双链表模型
一、视频游戏如何做
(1)空间:游戏必须在计算机屏幕的特定位置画影像。程序如何控制视频显示?
(2)时间:影像以不同的速度在屏幕上移动。以一个特定的时间间隔改变位置。程序是如何获知时间并且在特定的时间安排事情的发生?
(3)中断:程序在屏幕上平滑移动的物体,用户可以在任何时刻产生输入。程序是如何响应中断的?
(4)同时做几件事:游戏必须在保持几个物体移动的同时还要响应中断。程序是如何同时做多件事情而不被弄得晕头转向的?
二、操作系统面临着类似的问题
操作系统同样要面对这4个问题。内核将程序载入内存空间并维护每个程序在内存中所处的位置。在内核的调度下,程序以时间片间隔的方式运行,同时,内核也在特定的时刻运行特定的内部任务。内核必须在很短的时间内响应用户和外设在任何时刻的输入。同时做几件事需要一些技巧。内核是如何保证数据的有序和规整的?
上面的都是那本书上说的,个人觉得讲的很好,看完这本后再看那本Linux圣经《Unix环境高级编程》或许更好些。回归正题吧,主要介绍一下设计一个终端下的贪吃蛇游戏所实现的功能以及所需要的几个条件和一些函数。
本贪吃蛇实现的功能是通过吃食物来增长自己的长度,可以利用按键 'f' 实现加速和 's' 键实现减速, 'q'
键退出,方向键控制方向,蛇要是碰到自己的身体或者碰到墙或者吃到一定数量,那么游戏就结束。功能还是挺简单的吧,下面就介绍下各个步骤的设计:
1.首先要使用终端图形库curses.h文件,由于不是C标准库,一般电脑不会自带,需要自行下载安装,ubuntu下可以这么下载 sudo
apt-get install libncurses5-dev
已经替换成ncurses.h 即 new
curses.h的意思,完全兼容curses。介绍下此游戏需要用到的常见的几个curses函数。
initscr() | 初始化curses库和tty |
endwin() | 关闭curses并重置tty |
refresh() | 刷新屏幕显示 |
mvaddch(y,x,c) | 在坐标(y,x)处显示字符c |
mvaddstr(y,x,str) | 在坐标(y,x)处显示字符串str |
cbreak() | 开启输入立即响应 |
noecho() | 输入不回显到屏幕 |
curs_set(0) | 使光标不可见 |
attrset() | 开启图形显示模式 |
keypad(stdscr, true) | 开启小键盘方向键输入捕捉支持 |
man ncurses
或者参见http://bbs.chinaunix.net/viewthread.php?tid=909369
2.介绍完ncurses图形库,接下来进行屏幕绘图,我初始化屏幕效果图见下图所示:先是外围边框,然后是蛇“@”和食物“*”。
废话不多说,上代码吧。
首先是头文件
snake.h的代码:由于在纯文本模式下编程以及本人英语水平有限,可能有的注释比较别扭。
[cpp] view
plaincopy
"font-size:13px;">
#define SNAKE_SYMBOL '@'
#define FOOD_SYMBOL '*'
#define MAX_NODE 30
#define DFL_SPEED 50
#define TOP_ROW 5
#define BOT_ROW LINES - 1
#define LEFT_EDGE 0
#define RIGHT_EDGE COLS - 1
typedef struct node
{
int x_pos;
int y_pos;
struct node *prev;
struct node *next;
} Snake_Node;
struct position
{
int x_pos;
int y_pos;
} ;
void Init_Disp();
void Food_Disp();
void Wrap_Up();
void Key_Ctrl();
int set_ticker(int n_msecs);
void DLL_Snake_Create();
void DLL_Snake_Insert(int x, int y);
void DLL_Snake_Delete_Node();
void DLL_Snake_Delete();
void Snake_Move();
void gameover(int n);
接下来是初始化界面图形的子函数:
[cpp] view
plaincopy
"font-size:13px;">
void Init_Disp()
{
char wall = ' ';
int i, j;
initscr();
cbreak();
noecho();
curs_set(0);
attrset(A_NORMAL);
attron(A_REVERSE);
for(i = 0; i < LINES; i++)
{
mvaddch(i, LEFT_EDGE, wall);
mvaddch(i, RIGHT_EDGE, wall);
}
for(j = 0; j < COLS; j++)
{
mvaddch(0, j, '=');
mvaddch(TOP_ROW, j, wall);
mvaddch(BOT_ROW, j, wall);
}
attroff(A_REVERSE);
mvaddstr(1, 2, "Game: snake version: 1.0 date: 2011/08/22");
mvaddstr(2, 2, "Author: Dream Fly Blog: blog.csdn.net/jjzhoujun2010");
mvaddstr(3, 2, "Usage: Press 'f' to speed up, 's' to speed down,'q' to quit.");
mvaddstr(4, 2, " Nagivation key controls snake moving.");
refresh();
}
void Food_Disp()
{
srand(time(0));
food.x_pos = rand() % (COLS - 2) + 1;
food.y_pos = rand() % (LINES - TOP_ROW - 2) + TOP_ROW + 1;
mvaddch(food.y_pos, food.x_pos, FOOD_SYMBOL);
refresh();
}
void DLL_Snake_Create()
{
Snake_Node *temp = (Snake_Node *)malloc(sizeof(Snake_Node));
head = (Snake_Node *)malloc(sizeof(Snake_Node));
tail = (Snake_Node *)malloc(sizeof(Snake_Node));
if(temp == NULL || head == NULL || tail == NULL)
perror("malloc");
temp->x_pos = 5;
temp->y_pos = 10;
head->prev =NULL;
tail->next = NULL;
head->next = temp;
temp->next = tail;
tail->prev = temp;
temp->prev = head;
mvaddch(temp->y_pos, temp->x_pos, SNAKE_SYMBOL);
refresh();
}
3.接下来就是蛇的移动问题,这个是核心部分以及最困难的设计部分了,我采用的是蛇用双向链表的结构来构造出来,分别有一个head
和tail指针,用来添加和删除元素。这里若要实现移动的话(未碰到食物前),就是在链表的头部(head的下一个)插入一个新元素,记录下此时的坐标,用mvaddch(y,x,c)函数添加蛇的图形'@',与此同时,在链表尾部(tail的前一个)删除一个节点,同时这里的坐标用mvaddch(y,x,
' ')添加了' '空白字符,实现删除效果,最后加上refresh().
这样就可以看到蛇在“移动”了。当然,要是碰到食物的话,尾部节点处就不用删除,达到增长长度的效果。
那么,接下来的问题是:如何触发蛇的移动呢?如何实现均匀移动以及通过按键 ‘f’ 或 's'
改变运动速度呢?这里我采用的是信号计时中断调用的函数 signal(SIGALRM,
Snake_Move) 和
间隔计数器来实现,通过产生相同间隔的时间片段来不断地调用Snake_Move()函数来执行相应的功能。加减速的功能是通过设定其他变量ttm,
ttg来实现再此基本计数器上面再次分频的效果来加减速,ttm, ttg 越大,减速越明显,反之则相反效果。
具体的间隔计数器的函数设计见下:(参考了以上所提书本上的函数)
[cpp] view
plaincopy
int set_ticker(int n_msecs)
{
struct itimerval new_timeset;
long n_sec, n_usecs;
n_sec = n_msecs / 1000;
n_usecs = (n_msecs % 1000) * 1000L;
new_timeset.it_interval.tv_sec = n_sec;
new_timeset.it_interval.tv_usec = n_usecs;
new_timeset.it_value.tv_sec = n_sec;
new_timeset.it_value.tv_usec = n_usecs;
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}
蛇的移动的函数代码如下:
[cpp] view
plaincopy
void Snake_Move()
{
static int length = 1;
int Length_Flag = 0;
int moved = 0;
signal(SIGALRM, SIG_IGN);
if((head->next->x_pos == RIGHT_EDGE-1 && x_dir == 1)
|| (head->next->x_pos == LEFT_EDGE+1 && x_dir == -1)
|| (head->next->y_pos == TOP_ROW+1 && y_dir == -1)
|| (head->next->y_pos == BOT_ROW-1 && y_dir == 1))
{
gameover(1);
}
if(mvinch(head->next->y_pos + y_dir, head->next->x_pos + x_dir) == '@')
gameover(2);
if(ttm > 0 && ttg-- == 1)
{
DLL_Snake_Insert(head->next->x_pos + x_dir, head->next->y_pos + y_dir);
ttg = ttm;
moved = 1;
}
if(moved)
{
if(head->next->x_pos == food.x_pos && head->next->y_pos == food.y_pos)
{
Length_Flag = 1;
length++;
if(length >= MAX_NODE)
gameover(0);
Food_Disp();
}
if(Length_Flag == 0)
{
mvaddch(tail->prev->y_pos, tail->prev->x_pos, ' ');
DLL_Snake_Delete_Node();
}
mvaddch(head->next->y_pos, head->next->x_pos, SNAKE_SYMBOL);
refresh();
}
signal(SIGALRM, Snake_Move);
}
主要函数的实现就是这些,以下贴上完整的源代码供大家参考,或者去这里下载:http://download.csdn.net/source/3540117
通过本程序可以加强自己对信号间隔计数器的理解,以及终端图形编程的理解和了解设计的此类游戏的一般思路,也实现了我大一学习C语言所想要实现的想法。本人第一次在CSDN上面把自己的一些编程想法写的比较详细,不足之处还请指出,共同讨论。
参考资料:《Unix/Linux编程实践教程》
(美)Bruce Molay 著
杨宗源 黄海涛
译
清华大学出版社
http://note.sdo.com/my#!note/preview/xJ29Q~jAYzOpnM01Y0004K
curses库的使用
http://blog.sina.com.cn/s/blog_4c3b26e10100sd7b.html
贪吃蛇双链表模型
相关文章推荐
- Linux下的字符界面扫雷游戏
- Linux下的字符界面扫雷游戏
- linux下使用C语言实现2048简易字符界面游戏
- Linux字符界面操作
- Linux中图形界面与字符界面的转换
- 怎样在Linux启动时进入字符界面
- linux字符界面下的乱码问题---改成英文显示
- linux从字符界面转入图形界面一法
- linux下man命令显示帮助信息后的退回字符界面
- linux更改字符界面分辨率的方法 推荐
- linux字符界面修改IP
- linux图形界面和字符界面概念解释
- Redhat9 Linux下字符界面乱码解决方法
- 分享自己写的一个贪吃蛇的游戏(Linux)
- 字符界面下安装Red Hat Enterprise Linux 5
- linux 字符界面 安装模式创建LVM
- 如何在linux字符界面下设置打印机printer
- linux启动后如何直接进入字符界面及如何返回图形界面
- 详解linux系列之字符界面安装apache及配置
- linux字符界面下的乱码问题