一、前言
学习c语言一段时间后,利用c语言链表和ncurses库实现贪吃蛇游戏项目。项目涉及到ncurses库的基本使用、c语言指针、链表的基本知识以及linux多线程的基本使用。
二、项目展示
- a、贪吃蛇撞墙死亡,游戏重启
- b、贪吃蛇撞到自己死亡,游戏重启
- c、正常游戏,下方记录分数以及下一个food的位置坐标
三、项目流程
四、代码结构
为了在更新游戏界面的同时能够让程序对键盘的输入能够进行反应,使用linux中的多线程实现两个任务的尽可能的同时进行:
int main()
{
pthread_t th1;
pthread_t th2;
initScr();
initSnake();
gamPic();
//多线程
pthread_create(&th1,NULL,changeDirction,NULL);
pthread_create(&th2,NULL,refreshjiemian,NULL);
while(1);
getch();
endwin();
return 0;
}
当然这里为了达到固定界面并且能够实时对键盘的操作做出回应,使用了ncurses库,创建窗口:
initscr();
keypad(stdscr,1);
getch();
endwin();
定义数据结构:
struct Snake //贪吃🐍结构体
{
int hang;
int lie;
struct Snake *next;
};
//全局变量
struct Snake* head =NULL;//蛇头
struct Snake* tail =NULL;//蛇尾
struct Snake food; //食物
int dir; //方向
int fen=0; //分数
实物位置生成(rand()%19):
void initFood()//食物位置生成
{
int x;
int y;
while(1){
x=rand()%19;
y=rand()%19;
if (x!=0 && y !=0)
{
break;
}
}
food.hang=x;
food.lie =y;
food.next=NULL;
printw("Next Food is: x:%d y:%d\n",food.hang,food.lie);
}
根据键盘的方向输入对贪吃蛇链表节点进行修改:
void addNode()//增加节点
{
struct Snake *new =(struct Snake*)malloc(sizeof(struct Snake));
switch(dir)
{
case UP:
new->hang=tail->hang-1;
new->lie=tail->lie;
new->next=NULL;
break;
case DOWN:
new->hang=tail->hang+1;
new->lie=tail->lie;
new->next=NULL;
break;
case LEFT:
new->hang=tail->hang;
new->lie=tail->lie-1;
new->next=NULL;
break;
case RIGHT:
new->hang=tail->hang;
new->lie=tail->lie+1;
new->next=NULL;
break;
}
tail->next =new;
tail=new;
}
需要注意的是,之前写到的防止内存泄露问题 使用malloc 开辟地址之后要及时对废弃的地址进行释放:
void initSnake()//蛇身的初始化
{
initFood();//初始化食物
dir=RIGHT; //默认方向
struct Snake *p;
while(head!=NULL){
p=head;
head=head->next;
free(p);//释放废弃地址
}
head =(struct Snake *)malloc(sizeof(struct Snake));
head->hang=2;
head->lie=2;
head->next=NULL;
tail=head;
head->next=tail;
addNode();
addNode();
addNode();
addNode();
addNode();
}
蛇身的移动:
- 没吃食物时,移动为尾增节点、头减节点;
- 吃食物后不删除头部节点即可
碰撞检测:
- 撞墙
- 撞自己
int ifSnakedie()//判断蛇是否撞到自己
{
struct Snake *p;
p=head;
while(p->next!=NULL)
{
if (p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p=p->next;
}
return 0;
}
void movesnake()//蛇身移动,没吃食物移动为尾增节点、头减节点;吃食物后不删除头部节点即可!
{
addNode();
if(hasFood(tail->hang,tail->lie)){
initFood();
fen=fen+1;
}else{
head=head->next;//删除节点
}
if((tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20) ||(ifSnakedie()) )//撞墙、撞自己检测
{
initSnake();
fen=0;
}
}
五、源码
#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
struct Snake //贪吃🐍结构体
{
int hang;
int lie;
struct Snake *next;
};
//全局变量
struct Snake* head =NULL;//蛇头
struct Snake* tail =NULL;//蛇尾
struct Snake food; //食物
int dir; //方向
int fen=0; //分数
void initFood()//食物位置生成
{
int x;
int y;
while(1){
x=rand()%19;
y=rand()%19;
if (x!=0 && y !=0)
{
break;
}
} //防止x=0或y=0
food.hang=x;
food.lie =y;
food.next=NULL;
printw("Next Food is: x:%d y:%d\n",food.hang,food.lie);
}
void initScr()//初始化curse界面
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i,int j)//检测链表节点
{
struct Snake * point=head;
while(point!=NULL)
{
if(point->hang==i && point->lie==j)
{
return 1;
}
point=point->next;
}//历遍链表判断当前位置是否为蛇身节点
return 0;
}
int hasFood(int i,int j)//判断当前位置是否为食物位置
{
if(food.hang==i && food.lie==j)
{
return 1;
}
return 0;
}
void gamPic()//初始化地图
{
int hang=0;
int lie;
move(0,0);
while(hang<21){
if(hang==0)
{
for(lie=0;lie<20;lie++)
{
printw("--");
}
}else
{
if(hang==20)
{
for(lie=0;lie<20;lie++)
{
printw("--");
}
}else
{
for(lie=0;lie<21;lie++){
if (lie==0||lie==20)
{
printw("|");
}else
{
if(hasSnakeNode(hang,lie))
{
printw("[]");
}else if(hasFood(hang,lie))
{
printw("##");
}
else{
printw(" ");
}
}
}
}
}
printw("\n");
hang++;
}
}
void addNode()//增加节点
{
struct Snake *new =(struct Snake*)malloc(sizeof(struct Snake));
switch(dir)
{
case UP:
new->hang=tail->hang-1;
new->lie=tail->lie;
new->next=NULL;
break;
case DOWN:
new->hang=tail->hang+1;
new->lie=tail->lie;
new->next=NULL;
break;
case LEFT:
new->hang=tail->hang;
new->lie=tail->lie-1;
new->next=NULL;
break;
case RIGHT:
new->hang=tail->hang;
new->lie=tail->lie+1;
new->next=NULL;
break;
}
tail->next =new;
tail=new;
}
void initSnake()//蛇身的初始化
{
initFood();
dir=RIGHT;
struct Snake *p;
while(head!=NULL){
p=head;
head=head->next;
free(p);
}
head =(struct Snake *)malloc(sizeof(struct Snake));
head->hang=2;
head->lie=2;
head->next=NULL;
tail=head;
head->next=tail;
addNode();
addNode();
addNode();
addNode();
addNode();
}
int ifSnakedie()//判断蛇是否撞到自己
{
struct Snake *p;
p=head;
while(p->next!=NULL)
{
if (p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p=p->next;
}
return 0;
}
void movesnake()//蛇身移动,没吃食物移动为尾增节点、头减节点;吃食物后不删除头部节点即可!
{
addNode();
if(hasFood(tail->hang,tail->lie)){
initFood();
fen=fen+1;
}else{
head=head->next;//删除节点
}
if((tail->hang==0||tail->lie==0||tail->hang==20||tail->lie==20) ||(ifSnakedie()) )//撞墙、撞自己检测
{
initSnake();
fen=0;
}
}
void* refreshjiemian()//刷新界面
{
while(1)
{
movesnake();
gamPic();
refresh();
printw("Game score is:%d \n",fen*10);
usleep(100000);
}
}
void* changeDirction() //检测键入的方向
{
int c;
while(1)
{ c=getch();
switch(c){
case 0402:
if (dir!=UP)
{
dir=DOWN;
}
// printw("DOWN\n");
break;
case 0403:
if(dir!=DOWN)
{
dir=UP;
}
// printw("UP\n");
break;
case 0404:
if(dir!=RIGHT)
{
dir=LEFT;
}
// printw("LEFT\n");
break;
case 0405:
if (dir!=LEFT)
{
dir=RIGHT;
}
// printw("RIGTH\n");
break;
}
}
}
int main()
{
pthread_t th1;
pthread_t th2;
initScr();
initSnake();
gamPic();
//多线程
pthread_create(&th1,NULL,changeDirction,NULL);
pthread_create(&th2,NULL,refreshjiemian,NULL);
while(1);
getch();
endwin();
return 0;
}
git:https://github.com/Ryj233/My_Program
编译和运行:
ren@ren-virtual-machine:~$ gcc cures7.c -lcurses -lpthread
ren@ren-virtual-machine:~$ ./a.out