Linux平台下贪吃蛇游戏的运行

摘要:
1.参考资料描述:这是一个在Linux系统下实现的简单蛇游戏。当学生求助时,我直接在RedHat进行了调试,参考了百度文库中“猫腻汉”的文章,结合我自己实践中遇到的一些问题,整理后,我想和大家分享一下解决方案。2.开发环境:linux+gcc+netbeans 3.简介:多线程。一个线程负责逻辑和绘图,一个线程监视键。3.1双线程启用

1.参考资料说明:

       这是一个在Linux系统下实现的简单的贪吃蛇游戏,同学找帮忙,我就直接在Red Hat中调试了一下,参考的是百度文库中“maosuhan”仁兄的文章,结合自己的一些实践遇到的问题,整理后,将解决方案和大家分享一下。

2.开发环境:

       linux+gcc+netbeans

3.思路介绍:

      多线程处理。一个线程负责逻辑和画图,一个线程监听按键。

3.1 两个线程使用理由:

        在c里面,最方便的就是getch方法了,但是这个函数会进行io的阻塞,知道按下了一个键,在这之前这个线程会被阻塞住,蛇也就不会移动了。所以需要开两个线程一 个线程是专门画图的,每隔多长的时间刷一下,另外一个线程是专门负责监听键盘事件的,就算会阻塞也只是影响到的本线程,画图线程不会被阻塞。并且两个线程是通过一个全部的变量input来进行通信的,这个input存储的是按键的键值ascii

3.2   使用一个线程情况

如果非要用一个线程寄来负责逻辑、画图,又负责按键监听,也可以采用 peek函数,peek函数会接受一个数字,比如说是peek(100)100就表示一个特定的调用例程。比如这里的100就是检测键盘的缓冲区有没有按下一个键并且键值是多少,这个是不会阻塞的。虽然这个peek调用看上比较丑陋,但是却可以实现单线程监听按键不阻塞的功能。但我还是认为两个线程比较方便,也符合更多人的逻辑。

言归正传,还是考虑在linux下怎么用c编程吧!

3.3创建监听键盘事件的线程。

	void * waitForKey(void *para) 
	{
		while (1) 
		{
   			input = getch();
    		}
	}
 
	pthread_t id;//声明一个linux线程,按键等待线程 
   	int ret;
   	ret = pthread_create(&id, NULL, waitForKey, NULL);//创建线程,其实就是一个函数,有点像java里面的Runnable的感觉。
	if (ret != 0) {
   	 	exit(1);
	}

4.部分函数说明

我还用到了一个库,就是curses,这个是专门用来绘图ui用的。但是在/usr/include里面是没有的,要到网上去下

        sudo apt-get installlibncurses-dev

       (针对不同的Linux系统有所不同,可以参照下一篇《Linux系统中UIcurse.h不存在问题》)

        initscr(); //初始化操作

        do_some_drawing();//这里你可以定义你自己的绘图形式

        refresh();

        endwin();

       其中我用到的函数有move(x,y)是把光标定位在某行某列上。还有addStr(s)addch(c)。是在光标处写字符串和写字符。

       还有getch()等待用户按键。还有refresh(),将缓冲的绘图操作都输出到屏幕上。

       此外还用到了usleep(int )函数这里的参数是 int型的,表示的是微秒数,1秒等于1000000微秒。这里的时间间隔是蛇每次移动时的间隔时间。

       还有,在不断地刷新屏幕的时候,没有说把整个屏幕都刷新一遍,再重新全部画点。而是只是重画一部分,像围墙就一直没有重画。而每次paint都是只把整个蛇的身体画出   来,再把最后的一个尾巴清除掉。其实还可以有更好的方法,就是身体都不要动的,只要改变一个头,清除一个尾就可以了,我这里的效率还是不高的。

       用gcc编译: gcc Snack.c –o Snack –l pthread –l curses

        (如果不包含curses.h头文件,说明你的gcc中没有,可以参照上面curses库安装说明。)

        ./Snack   运行结果下图所示:

                   Linux平台下贪吃蛇游戏的运行第1张

5.整个的程序的源代码

#include<stdio.h>
#include <pthread.h>
#include <sys/time.h>
#include <curses.h>//这个就是我们要用到的额外的ui库
#include <stdlib.h>
 
#define MAX_X 70   //场地宽
#define MAX_Y 20  //场地长
#define CORNER_X 4  //左上角x坐标
#define CORNER_Y 2   //左上角y坐标
 
struct point {
 	int x;
 	int y;
};
	struct point SnakeBody[50];
	struct point food;
	 
	int Length = 4; //初始蛇长
	int life = 1;  //是否还活着
	int input = 0; //记录键盘按键的ascii
	pthread_t id;//声明一个linux线程,按键等待线程
	 
	void FoodCheck();//检查是否吃到了食物
	void FoodProduce();//生成一个食物
	void Initializition();//初始化线程,进行蛇的初始设定,创建第一个食物
	void SnakeHeadMovement();//移动蛇
	void SnakeBodyMovement();//移动蛇辅助方法
	 
	void DeathCheck();//检查是否满足死亡条件
	void Paint();//画社画场地画食物
	void * waitForKey(void *);//这个是另一个线程的函数定义
	void drawDot(int x,int y,char s);//画点喽
	void clearDot(int x,int y);//清楚点喽
	void end();//程序的结束工作
	 
	//主函数
 
int main(int argc,char** argv) 
{ 
	 Initializition();
	 refresh();//刷新画布
	 while (life) {
	 
	 Paint();
	 usleep(200000);//刷新频率是0.2秒
	 SnakeHeadMovement();//移动蛇,在这个方法里执行了foodCheck方法。其实这里的逻辑稍微混乱了点
	 DeathCheck();//判断是否死亡
 }
 	end();
 	return 0;
}
 
void * waitForKey(void *para) 
{
	while (1)
	{
		usleep(1000);//为什么要加这个,不知道什么原因,在curses下,如果建了这个线程并且不加这句话的话就会出现花屏现象。很难看
 		input = getch();
 	}
}
 
void end() 
{ 
	 move(1, 0);
	 addstr("Press any key to quit!");
	 refresh();
	 getch();
	 endwin();
}
 
//食物的随机产生
 
void FoodProduce()
{
	 int superposition = 0;
	 int i;
	 srand(time(NULL));
	 do {
	 food.x = (rand() % ((MAX_X-2) / 2))*2+2; //2 to MAX_X-2  and is 偶数
	 food.y =rand() % (MAX_Y-1)+1;  //1 to MAX_Y-1
	 for (i = 0; i < Length; i++) {
	 if (food.x == SnakeBody[i].x && food.y == SnakeBody[i].y)
	 superposition = 1;
	 }
	 }while (superposition);/*直到没有重合*/
}
 
//蛇身和食物的初始化 初始化的蛇身为4节长度
 
void Initializition() 
{
	 initscr();//curses初始化
	 noecho();//默认不将输入的字符显示在屏幕上
	 int i;
	 for (i = 3; i <= 6; i++) {//初始化蛇
	 SnakeBody[6 - i].x = 4;
	 SnakeBody[6 - i].y = i;
 }
	 FoodProduce();
	 int ret;
	 ret = pthread_create(&id, NULL, waitForKey, NULL);//创建线程
	 if (ret != 0) 
	 {
	 	exit(1);
 	 }
 
	 for ( i = 0; i <= MAX_X; i+=2) 
	 { //画围墙
		 drawDot(i, 0,'*');
		 drawDot(i, MAX_Y,'*');
 	 }
 
 for (i = 0; i <= MAX_Y; i++) 
 {
	 drawDot(0, i,'*');
	 drawDot(MAX_X, i,'*');
 }
 
}
 
//蛇移动,依次从尾巴到头赋值
void SnakeBodyMovement() 
{
	 int i;
	 for (i = Length - 1; i > 0; i--) 
	 {
		 SnakeBody[i].x = SnakeBody[i - 1].x;
		 SnakeBody[i].y = SnakeBody[i - 1].y;
    }
 
}
 
void SnakeHeadMovement() 
{
 
	 clearDot(SnakeBody[Length - 1].x, SnakeBody[Length - 1].y);
	 int directionX, directionY;/*定义原本蛇前进的方向,可通过蛇头坐标减去蛇的第二部分*/
	 int newX, newY;
	 newX = SnakeBody[0].x;
	 newY = SnakeBody[0].y;
	 
	 directionX = SnakeBody[0].x - SnakeBody[1].x;
	 directionY = SnakeBody[0].y - SnakeBody[1].y;
 
	 if (input =='w' && directionY<=0)//不走回头路
	 		newY--;
	 else if (input =='s' && directionY>=0 )
	 		newY++;
	 else if (input =='a' && directionX<=0)
	 		newX -= 2;/*因为字符高是宽的两倍*/
	 else if (input =='d' && directionX>=0)
	 		newX += 2;
	 else 
	 {
	    newX += directionX;
		 newY += directionY;
 	 }
		 FoodCheck();
		 SnakeBodyMovement();
		 SnakeBody[0].x = newX;
		 SnakeBody[0].y = newY;
 
}
//判断是否吃到食物,以及吃到后长度变长还有产生新的食物
 
void FoodCheck() 
{
	 if (food.x == SnakeBody[0].x && food.y == SnakeBody[0].y) 
	 {
	 Length = Length + 1;
	 FoodProduce();
 	 }
}
//判断是否死亡
 
void DeathCheck() {
	 int i;
	 if (SnakeBody[0].x <=1 || SnakeBody[0].x >= MAX_X  || SnakeBody[0].y <= 0 || SnakeBody[0].y >=MAX_Y)
	 life = 0;
	 for (i = 4; i < Length; i++)
	 if (SnakeBody[0].x == SnakeBody[i].x && SnakeBody[0].y == SnakeBody[i].y)
	 life = 0;
}
 
//排序和打印
 
void Paint() {
	 int i = 0;
	 
	 drawDot(SnakeBody[i].x, SnakeBody[i].y,'@');
	 for (i=1; i < Length; i++) {
	 drawDot(SnakeBody[i].x, SnakeBody[i].y,'*');
 }
	 drawDot(food.x, food.y,'$');
	 move(0,0);
	 refresh();//刷新画布
}
 
void drawDot(int x,int y,char s) {
	 move(y+CORNER_Y, x+CORNER_X);
	 addch(s);
}
 
void clearDot(int x,int y) {
	 move(y+CORNER_Y, x+CORNER_X);
	 addch(' ');
}

免责声明:文章转载自《Linux平台下贪吃蛇游戏的运行》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇visual Studio 2017 扩展开发(二)《菜单图标详解》phalcon使用Redis发布订阅(pub/sub)命令介绍下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySQL锁问题

MySQL锁问题 MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎 采用的是表级锁;BDB存储引擎采用的是页面锁,但也支持表级锁;InnoDB存储引擎既支持行级锁,也支持表级锁,但默认 情况下采用行级锁。 MySQL这3种锁的特性可大致归纳如下: (1)表级锁:开销小...

【Chromium中文文档】跨进程通信 (IPC)

跨进程通信 (IPC) 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Inter-process_Communication.html 全书地址 Chromium中文文档 for https://www.chromium.org/...

Spring Cloud 生产环境性能优化

先思考几个问题: 什么是百万并发连接? 什么是吞吐量? 操作系统能否支持百万连接? 操作系统维持百万连接需要多少内存? 应用程序维持百万连接需要多少内存? 百万连接的吞吐量是否超过了网络限制? 百万的并发连接挑战意味着什么: 100 万的并发连接数 10 万个连接/秒——(如果每个连接以这个速率持续约10秒) 1 GB/秒的连接——快速连接到互联网。...

Windows几种线程同步方法介绍

系统中的所有线程都要访问系统资源,一个线程霸占某个资源,其他需要该资源的线程就不能完成自己的任务;另外如一个线程在读取某块内存中的数据,而另一个线程又正在修改这块内存的值,这同样不是我们想要的,所以线程之间必须要有一套自己的规则,不然就凌乱了。线程之间需要通信,如A线程霸占某个B线程需要的资源X,在A占用期间,B线程只能等待,或处于挂起状态,当A线程用完资...

排查CPU占用过高的问题

背景 最近测试服出现了CPU异常高的情况,占用率接近 100%,所以写篇文章简单地记录下碰到这种情况,该如何去定位导致CPU异常的代码,下文介绍了几种比较常用的工具。 下文均基于测试代码。 准备 我们先准备一个测试项目,此处使用的是一个简单的 springboot 的 web 项目,直接跑去官网初始化一个,地址: 地址,然后写了段简单的示例代码,见下图。...

【Python之路】特别篇--多线程与多进程

并发 与 并行 的区别: 解释一:并发是在同一实体上的多个事件,并行是在不同实体上的多个事件; 解释二:并发是指两个或多个事件在同一时间间隔发生,而并行是指两个或者多个事件在同一时刻发生。 并发:就是同时做多件事情。 例如:终端用户程序利用并发功能,在输入数据的同时响应用户输入。服务器利用并发,在处理第一个请求的同时响应第二个请求。只要你希望程序同时做多件...