LINUX系统基于Ncurses图形库的C语言贪吃蛇小游戏
2022-06-20孙继承
摘 要:Linux系统不仅免费、开源,而且还安全稳定,通过学习简单的指令让程序员更快地入门,让大家早一点接触Linux系统,并用C语言进行简单的贪吃蛇游戏设计。贪吃蛇是许多人小时候的经典回忆,在对贪吃蛇游戏编写时会涉及地图,食物,蛇的更新,文章是基于Linux系统通过使用C语言进行蛇游戏的设计与编写,经过每一个功能的设计与测试一步一步地最终实现游戏呈现的效果。
关键词:Linux系统;C语言;ncurses库;结构体;线程
中图分类号:TP311 文献标识码:A文章编号:2096-4706(2022)01-0093-04
Abstract: The Linux system is not only free and open source, but also safe and stable. By learning simple instructions, programmers can get started faster, so that everyone can get in touch with the Linux system earlier, and use C language to design a simple snake game. Snake game is a classic memory of our childhood. When writing the snake game, it will involve the update of maps, food, and snakes. This paper designs and writes the snake game based on the Linux system by using C language. After the design and test of each function step by step, it finally achieves the effect of the game presentation.
Keywords: Linux system; C language; ncurses library; structure; thread
0 引 言
现在我们经常会安装这些游戏:王者荣耀、和平精英、英雄联盟等等,这些游戏画面高清,代入感强,受广大人们的喜爱,但贪吃蛇仍是我们大多数人心中的经典游戏。贪吃蛇游戏出现之后发展迅速,从最初的游戏机到普通手机再到智能机再到电脑;从最初的单机个人游戏,到现在的联网多人同时游戏。贪吃蛇可以通过设定速度摄取食物让自己变得更长,通过简单地操作让人们获得满足感和胜利感。该游戏的优势是简单易操作,实现容易。本文基于Linux ncurses库通过c语言设计和编写贪吃蛇游戏,以便程序员以后更好地接触Linux系统并在其中进行开发。
1 编程语言简介
1.1 C语言简介
C语言是一门面向大众的、过程的、抽象化的通用程序进行设计教学语言,广泛应用于底层技术开发。C语言可以用一种简单的方式编译,处理低级内存。C语言是一种高效的编程语言,它只生成少量的机器语言,并且可以在没有任何运行环境支持的情况下运行。尽管C语言发展提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个国家标准规格写出的C语言应用程序可在包括企业类似嵌入式处理器技术以及中国超级计算机等作业管理平台的许多学生计算机网络平台上进行编译,C语言则与面向对象的语言不同,在进行C语言程序设计时,如果开发者没有一开始就确定好合适的数据类型、结构体、变量等,那么在调试时,查到程序错误或者有漏洞,修改起来就会相当困难。
1.2 设计环境
本程序在vmware虚拟机配置ubuntu镜像操作系统实现Linux下编程Vim编写C文件、GCC编译文件,生成文件后运行。用户可以自由编辑、编译、运行和调试程序,该开发工具优势可以让程序员熟悉LINUX操作指令,并在其中练习C语言,为以后的开发做下铺垫。
2 程序设计
2.1 程序简介
贪吃蛇这款游戏玩家用户从成人到青年,深受大家的喜爱。蛇初始化在一个自己设计的封闭图形里 ,食物会随机出现在墙壁内。玩家可以通过按键盘上的上下左右(蛇在移动过程中不可以直接更改到相反的方向)来控制蛇头移动的方向,如果蛇的头部(蛇的头部为链表的尾部)碰到食物,食物消失,蛇的身体就会在尾部长出一节,然后食物会随机在地图内出现,重复以上的过程。如果蛇在运动中碰到边界或者碰到自己的身体,则游戏结束。
2.2 设计思路
在设计时,需要用一个图案来代表蛇的一节身体,作者用“【】”来代表,因为这个图形移动感强、相似度较高,能给玩家带来更好的体验。每当蛇吃掉一个食物时,身体就会自动在尾部增长一节。当蛇移动时必须从蛇头(在这里作者用链表的尾部充当蛇头,链表的头部充当蛇尾)开始。如果玩家不按下任何方向键,蛇就会按照游戏初始方向自动前移。当玩家按下相应的方向键时,蛇头就会朝着方向按键所指的方向移动,通过不断刷新地图来显示蛇的移动。而食物我们用“#”来代替,在这里食物的位置我们用rand函数通过计算随机地出现在地图当中,确保食物出现的随机性。
貪吃蛇的游戏过程我们可以分为5个部分。
第一部分:游戏界面初始化地图,食物,贪吃蛇出现。
第二部分:蛇向游戏初始化设定的方向自动前进。
第三部分:根据玩家的键盘指令,蛇向相应的方向前进。
第四部分:判断蛇是否吃到食物,若吃到食物,蛇在尾部增长一节,食物重新随机地出现在地图的内部,若蛇没有吃到食物则按原长度继续前进。
第五部分:当蛇撞到地图边界或者撞到自身的身体,蛇死亡游戏结束进行第一部分重新开始游戏继续执行二三四部分,若蛇没有死亡则继续执行三四部分直到游戏结束。
2.3 设计流程图
游戏的设计流程图如图1所示。
程序按照设定初始游戏界面,地图食物蛇出现,程序不断捕获玩家输入,若为无效输入,蛇按照原方向正常运行;若为有效输入,蛇就会朝着按键方向正常移动。程序会不断检测蛇是否吃到食物,若吃到食物,食物消失,蛇增长;若没吃到食物,程序继续判断。若蛇碰到自身或者边界则游戏结束重新初始化;若没有继续循环以上程序,直到蛇碰到墙壁或自己游戏结束。
3 算法分析
在这里我们需要用到ncurses库帮助我们完成从键盘获取输入的任务,因为C语言函数库里的函数从键盘获取的响应速度慢,不具有实时性。而我们用ncurses库则能弥补这一点,当我们按下方向键时,程序迅速反应来达到我们控制蛇移动方向的要求。主函数main()首先调用函数initcurses()来实现ncurses库和获取键盘输入函数的初始化,然后调用函数InitSnake()来初始化贪吃蛇(程序设定最初的蛇为3个【】),在此函数中调用InitFood()随机出现食物,调用Addnode()来增加节点。调用函数Initmap()出现地图,此处我们需要创建两个线程并在这两个线程中一个执行Refresh_()函数实现不断刷新地图和蛇的移动功能的函数另一个线程执行ChangeDir()函数不断获取用户输入的按键,(在这里我简单地阐述一下,线程是一种可以多任务共同执行的操作方式,若在这里我们创建一个新的进程去执行这个功能,需要很多的数据去维护它,实在浪费空间,若改为线程则所需的时间和空间远小于进程并且他们二者在切换时,线程的优势也体现的较明显,增强了程序的健壮性和代码的可移植性而且操作也较为简单更利于程序员理解和应用)最后执行while()函数保持整个程序地进行。
接下来对各函数进行详细分析:
(1)主函数main()是程序的主要流程,用来调用各个函数。各函数最终都在主函数中被直接或间接地调用执行来实现游戏的效果。
(2)初始化函数initSnake(),在此函数中我们首先设定了一个游戏的初始方向RIGHT,并在其中调用Initfood()函数来出现食物,并在函数外定义结构体通过结构体指针给蛇最初出现的身体赋值,在调用俩次Addnode()函数增加蛇的节点使蛇初始化长度为3,因为每次蛇死亡都会调用这个函数,为了减少对内存空间的浪费,我们在此函数的最开始把原来制造的内存释放掉,增强程序的健壮性。此段释放空间的代码为:
Structtan*p;
While(head!=NULL){
P=head;
head=head->next;
free(p);
}
(3)绘制地图函数InitMap(),地图实际上是一个封闭的正方形用一个嵌套的双重for循环打印出来的上下边界我们用-代表,左右边界用|来代表中间用空格。这样便形成一个长20宽20的正方形。并在此函数中调用HaveSnake()函数和Havefood()函数,当这两个函数判断返回值为1时便打印【】和#来确定蛇和食物在地图中出现的位置,这两个函数用我们最初给的起始值与地图的相应位置进行比较,相同返回1,不同返回0,便能达到我们想要的效果,最后在此函数开头调用move(0,0)函数调整光标,如果不调用新的地图将会在光标默认的位置打印,达到不了游戏的效果。
(4)Initcurses()获取键盘输入和初始化Ncurses库,此处比较简单具体实现代码如下:
Void Initcurses(){
Initscr();
keypad(stdscr,1);//从小键盘上获取方向
noecho();
}
(5)增加节点函数AddNode(),此函数我们在InitSnake()中调用,在这里详细讲解一下,我们用malloc函数给新增的节点开辟空间,因为我们的蛇移动的方向涉及上下左右,这就需要我们在增加节点的时候根据方向的不同来确定,此处我们用switch()函数进行方向的选择。举例 当我们按下up键时便让这个新产生的节点行坐标比原来的行坐标减一,列坐标不变。再让这个头指针指向这个新的节点,把这个新节点变成尾部,这样我们便完成了一次移动,其他方向也是如此。
(6)刷新Refresh_()函数,在此函数中我们调用while()函数并在里面不断执行Movesnake()函数和Initmap()函数和refresh()函數和usleep()函数。通过以上函数的调用我们便能不断地刷新显示出蛇的整体运动,并调整地图更新速度。
(7)删除蛇节点函数DeleteNode()当蛇没有吃到食物需要保持原长度进行移动时。我们就需要使头节点指向下一个节点,释放当前节点来达到移动的效果。代码如下:
void DeleteNode()
{
Struct tan *p=head;
Head=head->next;
Free(p);
}
(8)改变方向函数ChangeDir()在这里我们调用while()函数不断获取用户输入,当我们检测到用户输入时,该值便会在AddNode()函数中被调用来确定用户输入的方向从而进行精准走位,因为贪吃蛇的移动方向不能从左边直接转到右边,所以我们需要判断一下输入的方向与当前的方向的关系,我们在函数外用define重新定义 DOWN……,把相反的方向设为相反数,当我们从键盘输入时,该值就会传到turn()函数中进行判断,若与他自身的相反数不相等,则此值按照输入方向执行,若相等则按原方向继续移动,turn()函数中我们把输入的方向当作参数传进来,让参数的相反值与原方向相反值进行比较得出结果。
(9)移动函数MoveSnake()每次调用此函数时节点就会按照方向的输入增加一个,判断蛇是否吃到食物,若吃到食物,调用Initfood()函数更新食物,若没吃到食物则调用DeleteNode()函数删掉头节点达到蛇移动的效果。并在此函数中调用SnakeDie()函数判断蛇是否撞墙或者撞到自身,若撞到则执行InitSnake()函数,否则按照原程序继续前进。
(10)初始化食物函数Initfood()为了让食物随机出现在地图内我们调用rand()函数给食物的横,纵坐标赋值并让其与20取余
这样就能保证它出现在地图以内。代码如下:
void initfood(){
int x=rand()%20;
int y=rand()%20;
food.hang=x;
food.lie=y;
}
在主程序当中我们需要调用线程来实现ChangeDir()和Refresh_()函数,这样才能保证程序不断地刷新和获取地图的更新,应用线程能降低程序的内存,提高运行效率,给玩家带来更好的用户体验。
4 程序测试
程序测试的目的是为了检测程序有无编译上的漏洞和逻辑上的漏洞。在编写程序期间,作者每完成一个部分就去编写运行一下。作者在测试运行时发现,蛇的初始化都已经完成,但当玩家按下方向键时,地图会在原来的地图下面打印一个新地图如图2所示。
经过查找信息发现是光标的问题。找到这个bug后就需要程序员思考如何清除漏洞。这里我们在InitSnake()函数中调用move(0,0)函数并赋值便可帮助我们解决问题。
程序编写完成,进入最终测试阶段。在小键盘上按下方向键蛇按照键入方向改变了原本的线路,并且当蛇向右边运行时按下左键蛇无反应,与我们设定的程序执行效果一样,说明程序输入和蛇的移动没有问题。对于地图,我们可自行参照根据自身要求来设定。如果发现蛇移动的速度过快或过慢,或者想要改变速度,可以在程序中Refresh()函数中修改usleep()函数的参数。经过最终测试,本程序通过编译后没有明显的漏洞在逻辑上,最终效果如图3所示,且运行结果与最初预想无较大不同。
5 结 论
本文基于Linux系统调用Ncurses库用C语言和一些简单的Linux的基础知识完成了对贪吃蛇游戏的程序设计,算法分析等等。适合于刚学完C语言却不知如何使用的同学们,并且还能简单地认识一下Linux系统,每个环节的设计都源于C语言的基础知识,例如链表、封装函数、循环等等知识点,有利于启发大家对以后编程的兴趣。
参考文献:
[1] 李雨哲,孙炼.基于C语言的贪吃蛇游戏设计 [J].河南科技,2020(7):31-33.
[2] 張文华,廖俊杰,付乙眉.基于C语言的贪吃蛇游戏的简单设计与实现 [J].智富时代,2019(3):183.
[3] 王思乐,卢素魁,杨文柱,等.链表结构在基于C语言项目中复用方法 [J].电脑编程技巧与维护,2017(20):9-10+13.
[4] 匡泰,时允田.C语言程序设计项目式教程 编程语言 [M].北京:人民邮电出版社,2017.
[5] 周小云.C语言的快乐教学之旅 [J].课程教育研究,2013(2):156.
作者简介:孙继承(2000—),男,汉族,内蒙古呼伦贝尔人,本科在读,研究方向:计算机。