Linux系统下实现贪吃蛇小游戏
一、准备工作
首先我们需要安装Ncurses库,读者可自行百度如何安装
#include<curses.h> //ncurses必备的头文件
#include<stdlib.h> //malloc函数必备的头文件
#include<pthread.h> //多线程必备的头文件
//定义一个结构体作为蛇身
struct Snake
{
int row;
int line;
struct Snake *next;
};
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
//定义键盘输入
int key;
//定义移动方向
int dir;
int score = 0;
//定义蛇头
struct Snake *head = NULL;
//定义蛇尾
struct Snake *tail = NULL;
//定义食物
struct Snake food;
//初始化Ncurses
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
二、显示基本图形界面
//对蛇身初始化
void initSnake()
{
struct Snake *p = NULL;
//释放死掉的贪吃蛇所占空间
while(head != NULL){
p = head;
head = head->next;
free(p);
}
//定义初始移动方向
dir = RIGHT;
//为蛇头开辟空间
head = (struct Snake *)malloc(sizeof(struct Snake));
//定义蛇头初始位置
head->row = 1;
head->line = 1;
head->next = NULL;
//将蛇尾初始化指向蛇头
tail = head;
//为蛇身增加节点
addNode();
addNode();
}
//初始化食物
void initFood()
{
int row = rand()%20;
int line = rand()%20;
food.row = row;
food.line = line;
}
//显示蛇身
int snakeBody(int row,int line)
{
struct Snake *p = head;
while(p != NULL){
if(p->row == row && p->line == line){
return 1;
}
p = p->next;
}
return 0;
}
//显示食物
int snakeFood(int row,int line)
{
if(food.row == row && food.line == line){
return 1;
}
return 0;
}
//定义20X20大小的地图
void gamePic()
{
int row;
int line;
//移动光标
move(0,0);
for(row=0;row<=20;row++){
//定义上下边界
if(row == 0 || row == 20){
for(line=0;line<20;line++){
printw("--");
}
printw("\n");
}
if(row > 0 && row < 20){
//定义左右边界
for(line=0;line<=20;line++){
if(line == 0 || line == 20){
printw("|");
//定义蛇身
}else if(snakeBody(row,line)){
printw("[]");
//定义食物
}else if(snakeFood(row,line)){
printw("##");
}else{
printw(" ");
}
}
printw("\n");
}
}
printw("by Teng\n");
//打印分数
printw("score %d\n",score);
}
三、蛇身移动
算法说明:贪吃蛇是根据蛇尾来移动的,移动方向不同蛇尾的坐标变化也不一样,当蛇尾不吃食物时,需要删除头节点,在尾巴处增加新节点,这样我们视觉效果就会看到蛇在移动,当吃到食物时,无需删除头节点。
//蛇身增加节点
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->next = NULL;
//判断方向来增加节点
switch(dir){
case UP:
new->row = tail->row-1;
new->line = tail->line;
break;
case DOWN:
new->row = tail->row+1;
new->line = tail->line;
break;
case LEFT:
new->row = tail->row;
new->line = tail->line-1;
break;
case RIGHT:
new->row = tail->row;
new->line = tail->line+1;
}
//将新节点加入蛇身
tail->next = new;
tail = new;
}
//删除蛇头
void deleteNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
//判断蛇的死亡
int snakeDie()
{
struct Snake *p = head;
//当贪吃蛇的尾巴碰到了蛇身的其他部位,贪吃蛇重生
while(p->next != NULL){
if(p->row == tail->row && p->line == tail->line){
return 1;
}
p = p->next;
}
//当贪吃蛇撞墙后死亡重生
if(tail->row == 0 || tail->row == 20 || tail->line == 0 || tail->line == 20){
return 1;
}
return 0;
}
void moveSnake()
{
//当蛇尾碰到食物,判定吃到食物,增加节点,分数加10,并重置新的食物
if(snakeFood(tail->row,tail->line)){
addNode();
initFood();
score += 10;
//当食物坐标存在0点时,无法在界面显示,需重置食物位置
if(food.row == 0 || food.line == 0){
initFood();
}
}else{
//贪吃蛇未能吃到食物,仅在移动
addNode();
deleteNode();
}
//当贪吃蛇死亡时,全部重置
if(snakeDie()){
initSnake();
initFood();
score = 0;
}
}
四、控制移动方向并刷新界面
贪吃蛇不合理走位算法说明:若无绝对值来控制实际方向,贪吃蛇将出现能够反向走位的情况,算是一个小BUG,程序一开始我使用宏定义确定方向的整数值,只需通过对比键盘输入的方向及贪吃蛇走位方向的绝对值,即可判断是否是合理走位。
//刷新界面
void* refreshInterFace()
{
while(1){
moveSnake();
gamePic();
refresh();
usleep(100000);
}
}
//防止贪吃蛇不合理走位
void turn(int direction)
{
if(abs(dir) != abs(direction)){
dir = direction;
}
}
//控制移动方向
void* controlDir()
{
while(1){
key = getch();
switch(key){
case KEY_UP:
turn(UP);
break;
case KEY_DOWN:
turn(DOWN);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}
五、多线程控制
由于我们需要一边控制贪吃蛇走位一边刷新ncurses界面,所以我们需要用到多线程
int main()
{
//定义两个线程
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
initFood();
gamePic();
//线程1刷新界面
pthread_create(&t1,NULL,refreshInterFace,NULL);
//线程2控制方向
pthread_create(&t2,NULL,controlDir,NULL);
while(1);
getch();
endwin();
return 0;
}
现在完成了基本的贪吃蛇小游戏,由于笔者能力有限,如有错误之处还请不吝赐教。