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系统中UI库curse.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   运行结果下图所示:

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平台下贪吃蛇游戏的运行的更多相关文章

  1. 用C++实现的贪吃蛇游戏

    我是一个C++初学者,控制台实现了一个贪吃蛇游戏. 代码如下: //"贪吃蛇游戏"V1.0 //李国良于2016年12月29日编写完成 #include <iostream& ...

  2. WebGL实现HTML5的3D贪吃蛇游戏

    js1k.com收集了小于1k的javascript小例子,里面有很多很炫很酷的游戏和特效,今年规则又增加了新花样,传统的classic类型基础上又增加了WebGL类型,以及允许增加到2K的++类型, ...

  3. 100行JS实现HTML5的3D贪吃蛇游戏

    js1k.com收集了小于1k的javascript小例子,里面有很多很炫很酷的游戏和特效,今年规则又增加了新花样,传统的classic类型基础上又增加了WebGL类型,以及允许增加到2K的++类型, ...

  4. Love2D游戏引擎制作贪吃蛇游戏

    代码地址如下:http://www.demodashi.com/demo/15051.html Love2D游戏引擎制作贪吃蛇游戏 内附有linux下的makefile,windows下的生成方法请查 ...

  5. Qt 学习之路 2(34):贪吃蛇游戏(4)

    Qt 学习之路 2(34):贪吃蛇游戏(4) 豆子 2012年12月30日 Qt 学习之路 2 73条评论 这将是我们这个稍大一些的示例程序的最后一部分.在本章中,我们将完成GameControlle ...

  6. Qt 学习之路 2(33):贪吃蛇游戏(3)

    Qt 学习之路 2(33):贪吃蛇游戏(3) 豆子 2012年12月29日 Qt 学习之路 2 16条评论 继续前面一章的内容.上次我们讲完了有关蛇的静态部分,也就是绘制部分.现在,我们开始添加游戏控 ...

  7. linux平台下防火墙iptables原理(转)

    原文地址:http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646466.html iptables简介 netfilter/iptables( ...

  8. Windows及Linux平台下的计时函数总结

    本文对Windows及Linux平台下常用的计时函数进行总结,包括精度为秒.毫秒.微秒三种精度的各种函数.比如Window平台下特有的Windows API函数GetTickCount().timeG ...

  9. Thrift在Windows及Linux平台下的安装和使用示例

    本文章也同时发表在个人博客Thrift在Windows及Linux平台下的安装和使用示例上. thrift介绍 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的R ...

随机推荐

  1. zabbix web monitoring 监控网页

    配置 Web 场景 配置 web 场景: 转到: 配置 (Configuration)–>主机 (或者 模板 ) 点击主机 (host)/ 模板 (template) 行中的 Web 点击右上角 ...

  2. MySQL + Amoeba 负载均衡、主从备份方案

    1.  基本环境 4台内网虚拟机的操作系统都是ubuntu-14.04.4 64位 IP为:192.168.169.11.192.168.169.12.192.168.169.13.192.168.1 ...

  3. SpringCloud系列五:为Eureka Server添加用户认证及元数据

    1. 回顾 上一篇博客讲解了Eureka集群及将微服务注册到集群上.在前面的讲解中,Eureka Server都是允许匿名访问的,本次将讲解如何构建一个需要登录才能访问的Eureka Server. ...

  4. nexus5刷机

    1.下载需要的刷机包,hammerhead-mra58k-factory-52364034.tgz 2.解压包到某个文件下,如D:\Download 3.重启手机进入fastboot模式下,也叫boo ...

  5. vue实践---根据不同环境,自动转换请求的url地址

    一般的项目环境分为:本地环境,测试环境,预发环境,正式环境. 这些环境的域名一般是一样的, 前端请求接口的url也会随着这些环境的变化而改变,手动修改有点麻烦,所以想个办法,让请求的地址根据域名改变而 ...

  6. 微信小程序事件

    微信小程序事件1.什么是事件2.事件类别3.事件冒泡4.事件绑定5.事件对象详解笔记:1.事件是一种用户的行为,是一种通讯方式.2.事件类别:    点击事件:tap    长按事件:longtap  ...

  7. PHP-Manual的学习----【语言参考】----【基本语法】

    2017年6月28日11:29:311.当解析一个文件时,PHP 会寻找起始和结束标记,也就是 <?php 和 ?>,这告诉 PHP 开始和停止解析二者之间的代码.此种解析方式使得 PHP ...

  8. Solr In Action 中文版 第一章(四、五)

    1.1             功能概览1. 4 最后,让我们再依照以下的分类.高速的过一下Solr的主要功能: ·用户体验 ·数据建模 ·Solr 4的新功能 在本书中.为你的用户提供良好的搜索体验 ...

  9. IOS连接

    http://www.wuleilei.com/blog/323 不错 http://blog.csdn.net/totogo2010/ http://blog.csdn.net/totogo2010 ...

  10. 从零开始写一个Exporter

    前言 上一篇文章中已经给大家整体的介绍了开源监控系统Prometheus,其中Exporter作为整个系统的Agent端,通过HTTP接口暴露需要监控的数据.那么如何将用户指标通过Exporter的形 ...