一、视频游戏如何做
(1)空间:游戏必须在计算机屏幕的特定位置画影像。程序如何控制视频显示?
(2)时间:影像以不同的速度在屏幕上移动。以一个特定的时间间隔改变位置。程序是如何获知时间并且在特定的时间安排事情的发生?
(3)中断:程序在屏幕上平滑移动的物体,用户可以在任何时刻产生输入。程序是如何响应中断的?
(4)同时做几件事:游戏必须在保持几个物体移动的同时还要响应中断。程序是如何同时做多件事情而不被弄得晕头转向的?
二、操作系统面临着类似的问题
操作系统同样要面对这4个问题。内核将程序载入内存空间并维护每个程序在内存中所处的位置。在内核的调度下,程序以时间片间隔的方式运行,同时,内核也在特定的时刻运行特定的内部任务。内核必须在很短的时间内响应用户和外设在任何时刻的输入。同时做几件事需要一些技巧。内核是如何保证数据的有序和规整的?
上面的都是那本书上说的,个人觉得讲的很好,看完这本后再看那本Linux圣经《Unix环境高级编程》或许更好些。回归正题吧,主要介绍一下设计一个终端下的贪吃蛇游戏所实现的功能以及所需要的几个条件和一些函数。
本贪吃蛇实现的功能是通过吃食物来增长自己的长度,可以利用按键 'f' 实现加速和 's' 键实现减速, 'q' 键退出,方向键控制方向,蛇要是碰到自己的身体或者碰到墙或者吃到一定数量,那么游戏就结束。功能还是挺简单的吧,下面就介绍下各个步骤的设计:
1.首先要使用终端图形库curses.h文件,由于不是C标准库,一般电脑不会自带,需要自行下载安装,ubuntu下可以这么下载
基本curse函数 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的代码:由于在纯文本模式下编程以及本人英语水平有限,可能有的注释比较别扭。
- "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);
- "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' 改变运动速度呢?这里我采用的是信号计时中断调用的函数
-
- 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); - }
蛇的移动的函数代码如下:
- 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编程实践教程》
http://note.sdo.com/my#!note/preview/xJ29Q~jAYzOpnM01Y0004K
curses库的使用
http://blog.sina.com.cn/s/blog_4c3b26e10100sd7b.html
贪吃蛇双链表模型