基于Linux Ncurses图形库的贪吃蛇小游戏
我大一开始学习C语言,从一个连二三十行代码都写不清楚的菜鸟带现在可以编写和调试一个有几百行代码的小游戏,这期间经历了许多困难,同时也得到了许多人的帮助,这里要感谢上官可编程团队对我的帮助。
游戏怎么做
我在学习C语言一开始用的都是DevC++来练习,后面学到指针和链表的时候就开始在Ubantu终端上练习写代码,一开始对vi 的编辑模式很不适应,鼠标在终端上几乎没啥用,复制要nyy,剪切是ndd,粘贴是p,觉得很繁琐,后面用多才发现,真香!游戏主要用到的就是C语言的结构体和链表的知识,还有Linux线程的一些应用,以及用Ncurses图形库制作游戏的界面:
- 游戏界面 ,玩过贪吃蛇的都知道,做出一个实用且美观的界面很重要,那怎么做呢?;
- 蛇的移动 ,贪吃蛇的身子需要先初始化大小,在移动时,遇到食物就“吃”掉,蛇的身子就长长一截,那么如何实现蛇的移动和蛇身长度的增加呢;
- 食物刷新 ,贪吃蛇吃完一个食物就要刷新新的食物位置了,那么如何随机刷新呢?;
- 撞墙后从新开始 ,蛇撞墙后重新开始并且清空得分;
一、初始化游戏界面
/*游戏界面设计用Ncurses图形库来做,定义gamePic函数初始化游戏界面*/
void gamePic(void)
{
int hang;
int lie;
move(0,0); //调用Ncurses图形库自带的move函数实现按键控制蛇的走位
/*实现游戏界面的上下左右边框*/
for(hang=0;hang<20;hang++)
{
if(hang==0) //判断是否在第一行
{
for(lie=0;lie<20;lie++)
{
printw("--"); //打印上边框
}
printw("\n");
}
if(hang>=0 && hang<=19)
{
for(lie=0;lie<=20;lie++)
{
if(lie == 0 || lie==20)
{
printw("|"); //打印左右边框
}else if(hasSnackNode(hang,lie)){
printw("[]"); //打印蛇的身子
}else if(hasSnackFood(hang,lie)){
printw("##"); //随机刷新食物
}
else{
printw(" "); //打印空格
}
}
printw("\n");
}
if(hang == 19)
{
for(lie=0;lie<20;lie++)
{
printw("--"); //打印下边框
}
printw("\n");
printw("by YTZ,score = %d\n",score);
}
}
}
二、初始化蛇的身子
//头节点head初始化函数
int hasSnackNode(int i,int j)
{
struct Snack *p;
p = head;
while(p != NULL) //判断头节点是否指向NULL
{
if(p->hang == i && p->lie == j) //判断蛇的初始化坐标和hang,lie的坐标是否相等
{
return 1; //为1,打印蛇身节点
}
p = p->next; //方便下一次循环判断是否为NULL
}
return 0; //节点为NULL,返回0
}
void initSnack()
{
/*使用链表的尾插法,移动时*/
struct Snack *p;
dir = RIGHT;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
initFood(); //刷新食物
head = (struct Snack *)malloc(sizeof(struct Snack));
head->hang = 2;
head->lie = 2;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
//头节点删除,尾节点增加
void deleNode()
{
struct Snack *p;
p = head;
head = head->next;
free(p);
}
三、蛇移动和撞墙
int ifSnackDie()
{
struct Snack *p;
p = head;
if(tail->hang<0 || tail->lie==0 || tail->hang==20 || tail->lie==20) //判断蛇是否撞墙
{
return 1;
}
while(p->next != NULL){
if(p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p = p->next;
}
return 0;
}
void moveSnack()
{
addNode();
if(hasSnackFood(tail->hang,tail->lie)){
initFood();
score+=1; //食物重新刷新后,得分加一
}else{
deleNode();
}
if(ifSnackDie())
{
initSnack();
score = 0; //撞墙后得分清零
}
}
//刷新游戏界面
void* refreshJieMian()
{
while(1){
moveSnack();
gamePic();
refresh();
usleep(100000);
}
}
void turn(int direc){
if(abs(dir) != abs(direc)){
dir = direc;
}
}
void* changeDir()
{
while(1){
key = getch();
switch(key){
case KEY_RIGHT:
turn(RIGHT);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_DOWN:
turn(DOWN);
break;
case KEY_UP:
turn(UP);
break;
}
}
}
四、全部源码
#include <curses.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
//宏定义贪吃蛇上下左右移动的方向键
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snack *head = NULL; //贪吃蛇的头节点
struct Snack *tail = NULL; //贪吃蛇的尾节点
int key; //键值
int dir = 4; //初始化蛇移动的方向
int score = 0; //成绩
/*游戏布局界面用结构体来初始化*/
struct Snack
{
int hang;
int lie;
struct Snack *next;
};
struct Snack food; //定义结构体food
void initFood()
{
int x = rand()%20; //随机生成数取余,随机刷新食物的位置
int y = rand()%20;
if(x >= 1 && x < 20 && y >= 1 && y < 20){
food.hang = x;
food.lie = y;
}
}
//初始化按键操作
void initNcurse(void)
{
initscr();
keypad(stdscr,1);
noecho();
}
//判断随机生成食物的坐标与游戏框内的随机坐标
int hasSnackFood(int i,int j)
{
if(food.hang == i && food.lie == j)
{
return 1; //相等返回1
}
return 0;
}
//头节点head初始化函数
int hasSnackNode(int i,int j)
{
struct Snack *p;
p = head;
while(p != NULL) //判断头节点是否指向NULL
{
if(p->hang == i && p->lie == j) //判断蛇的初始化坐标和hang,lie的坐标是否相等
{
return 1; //为1,打印蛇身节点
}
p = p->next; //方便下一次循环判断是否为NULL
}
return 0; //节点为NULL,返回0
}
/*游戏界面设计用Ncurses图形库来做,定义gamePic函数初始化游戏界面*/
void gamePic(void)
{
int hang;
int lie;
move(0,0); //调用Ncurses图形库自带的move函数实现按键控制蛇的走位
/*实现游戏界面的上下左右边框*/
for(hang=0;hang<20;hang++)
{
if(hang==0) //判断是否在第一行
{
for(lie=0;lie<20;lie++)
{
printw("--"); //打印上边框
}
printw("\n");
}
if(hang>=0 && hang<=19)
{
for(lie=0;lie<=20;lie++)
{
if(lie == 0 || lie==20)
{
printw("|"); //打印左右边框
}else if(hasSnackNode(hang,lie)){
printw("[]"); //打印蛇的身子
}else if(hasSnackFood(hang,lie)){
printw("##"); //随机刷新食物
}
else{
printw(" "); //打印空格
}
}
printw("\n");
}
if(hang == 19)
{
for(lie=0;lie<20;lie++)
{
printw("--"); //打印下边框
}
printw("\n");
printw("by YTZ,score = %d\n",score);
}
}
}
void addNode()
{
struct Snack *new;
new = (struct Snack *)malloc(sizeof(struct Snack));
new->next = NULL;
//上下左右键控制蛇的走向
switch(dir){
case UP:
new->hang = tail->hang-1; //向上,头节点减一
new->lie = tail->lie;
break;
case DOWN:
new->hang = tail->hang+1; //向下,头节点加一
new->lie = tail->lie;
break;
case LEFT:
new->hang = tail->hang;
new->lie = tail->lie-1;
break;
case RIGHT:
new->hang = tail->hang;
new->lie = tail->lie+1;
break;
}
tail->next = new;
tail = new; //新加入的节点重新指向tail
}
void initSnack()
{
/*使用链表的尾插法,移动时*/
struct Snack *p;
dir = RIGHT;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
initFood(); //刷新食物
head = (struct Snack *)malloc(sizeof(struct Snack));
head->hang = 2;
head->lie = 2;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
//头节点删除,尾节点增加
void deleNode()
{
struct Snack *p;
p = head;
head = head->next;
free(p);
}
int ifSnackDie()
{
struct Snack *p;
p = head;
if(tail->hang<0 || tail->lie==0 || tail->hang==20 || tail->lie==20) //判断蛇是否撞墙
{
return 1;
}
while(p->next != NULL){
if(p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p = p->next;
}
return 0;
}
void moveSnack()
{
addNode();
if(hasSnackFood(tail->hang,tail->lie)){
initFood();
score+=1; //食物重新刷新后,得分加一
}else{
deleNode();
}
if(ifSnackDie())
{
initSnack();
score = 0; //撞墙后得分清零
}
}
//刷新游戏界面
void* refreshJieMian()
{
while(1){
moveSnack();
gamePic();
refresh();
usleep(100000);
}
}
void turn(int direc){
if(abs(dir) != abs(direc)){
dir = direc;
}
}
void* changeDir()
{
while(1){
key = getch();
switch(key){
case KEY_RIGHT:
turn(RIGHT);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_DOWN:
turn(DOWN);
break;
case KEY_UP:
turn(UP);
break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnack();
gamePic();
pthread_create(&t1,NULL,refreshJieMian,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}