main函数:对制作小蛇的过程做了大体的注释;
后面有作者自己遇到一些坑,可以参考 T.T ;
总之把能解释的都做了一定的注释,源码里see see;
@ 源码(可以先看main函数,就不会凌乱QAQ):
#include <curses.h>
#include <stdlib.h>
#include <pthread.h>
#define UP 1 //注意宏定义时不能加分号";"
#define DOWN -1 //反方向一正一负,方便后面abs()取绝对值,让蛇不能在一条直线原地掉头;
#define LEFT 2
#define RIGHT -2
struct Snake
{
int hang;
int lie;
struct Snake* next;
};
struct Snake* head = NULL;
struct Snake* tail = NULL;
struct Snake food;
int direct;
int key; //保存 getch()获取到的方向键返回值;便于后面打印实时方向 详见:gamePic();
void addSnake()
{
struct Snake *new;
struct Snake *p = tail;
new = (struct Snake *)malloc(sizeof(struct Snake));
new->next = NULL;
switch(direct){
case UP:
new->hang = p->hang-1;
new->lie = p->lie;
break;
case DOWN:
new->hang = p->hang+1;
new->lie = p->lie;
break;
case LEFT:
new->hang = p->hang;
new->lie = p->lie-1;
break;
case RIGHT:
new->hang = p->hang;
new->lie = p->lie+1;
break;
}
tail->next = new;
tail = new;
}
void testInitscr()
{
initscr();
keypad(stdscr,1);
noecho(); //吸收 getch()时特殊键位返回的多余的键值;
}
int haveSnake (int hang,int lie)
{
struct Snake* p = head;
while(p != NULL){
if(p->hang == hang && p->lie == lie){
return 1;
}
p = p->next;
}
return 0;
}
void initFood()
{
int x;
int y;
x = rand()%19+1;
y = rand()%19+1;
food.hang = x;
food.lie = y;
}
int ifFood(int hang,int lie)
{
if(hang == food.hang && lie == food.lie ){
return 1;
}
return 0;
}
void lieIoI_Pic(int hang)
{
int lie;
for(lie=0;lie<=20;lie++){
if(lie == 0 || lie ==20 ){
printw("|");
}
else if(haveSnake(hang,lie)){
printw("[]");
}
else if(ifFood(hang,lie)){
printw("##");
}
else{
printw(" ");
}
}
printw("\n");
}
void hangGang_Pic()
{
int lie;
for(lie=0;lie<20;lie++){
printw("--");
}
}
void gamePic()
{
int hang;
move(0,0); //下一次光标的初始位置;
for(hang=0;hang<20;hang++){
if(hang == 0){
hangGang_Pic();
printw("\n");
}
if(hang >= 0 && hang <= 19){
lieIoI_Pic(hang);
}
if(hang == 19){
hangGang_Pic();
}
}
printw("\n");
printw("by: TianYang10.0\n");
switch(key){
case 0403:
printw("UP\n");
break;
case 0402:
printw("DOWN\n");
break;
case 0404:
printw("LEFT\n");
break;
case 0405:
printw("RIGHT\n");
break;
}
}
void initSnake(){
int i;
struct Snake* p;
direct = RIGHT; //复活时,默认方向为右,具体可看addSnake;
//遍历蛇的链表,注意释放内存空间;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 2;
head->lie = 1;
head->next = NULL;
tail = head;
for(i=0;i<3;i++){
addSnake();
}
}
void delectSnakeHead()
{
struct Snake *p = head;
head = p->next;
free(p);
}
int ifSnakeDie()
{
struct Snake *p = head;
//碰到边界就噶;
if(tail->hang < 0||tail->lie == 0 ||tail->hang == 20||tail->lie == 20){
return 1;
}
//碰到蛇身就噶;
while(p->next != NULL){
if(tail->hang == p->hang && tail->lie == p->lie){
return 1;
}
p = p->next;
}
return 0;
}
void moveSnake()
{
addSnake(); //增加一节尾节点;
if(tail->hang == food.hang && tail->lie == food.lie){
//addSnake();
initFood();
}else{
delectSnakeHead(); //把头节点删了;
}
//delectSnakeHead();
//蛇死了,就初始化蛇;
if(ifSnakeDie()){
initSnake();
}
}
void * fereshJieMian()
{
while(1){
moveSnake(); //移动蛇的方向;
gamePic(); //打印地图;
usleep(100000); //1 s = 10^6 us; 不睡会,你想挑战while(1)的循环速度嘛?怕是快到看不清奥;
refresh(); //刷新窗口;
}
}
void adsDirection(int num)
{
if(abs(num) != abs(direct)){
direct = num;
}
}
void * getDirect()
{
while(1){
key = getch();
switch(key){
case KEY_UP:
adsDirection(UP);
break;
case KEY_DOWN:
adsDirection(DOWN);
break;
case KEY_LEFT:
adsDirection(LEFT);
break;
case KEY_RIGHT:
adsDirection(RIGHT);
break;
}
}
}
int main()
{
pthread_t t1; //创建2个线程的标识码变量为 t1,t2;
pthread_t t2; //让它们和主线程同时运行
//线程使用必要条件:
//pthread_t1; --创建线程的标识码变量
//pthread_create(&t1,NULL,&要执行的函数,NULL); --建立线程运行
//注意:使用线程在ubuntu系统编译时,需要链库-lpthread 才能正常使用;
//同样的:程序调用curses.h库,在编译时也要链库-lcurses;
testInitscr(); //curses.h库运行需要的必备函数:initscr();keypad(stdscr,1);
initSnake(); //初始化蛇;
initFood(); //初始化食物;
gamePic(); //打印首次的地图
//线程t1:定义方向键的返回的int型a变量;
//getch()获取键盘返回值给a变量;
//while(1){判断方向键后,执行赋值等操作 并break退出无限循环;}
pthread_create (&t1,NULL,getDirect,NULL);
//线程t2:while(1){刷新界面;移动蛇;打印地图;sleep()一会让人能看见;refresh()刷新一下;};
pthread_create(&t2,NULL,fereshJieMian,NULL);
while(1); //让主线程不会结束,和t1,t2一起无限循环下去,游戏才能正常玩;
endwin();
//正常在调用curses.h库时,在后面必加endwin();
//但是这个游戏程序被上面的while(1)拦住,所以不要也不影响贪吃蛇的运行;
return 0;
}
博主遇到的坑:
No.1:在Ubuntu系统编译时,如果在for写循环条件时,直接int 定义变量是不行滴,会提示需要 -sdt=c99;可即使编译时加了 -std=c99,结果依旧编译不过!最后将int i 写在循环之外才得以飞升。。0_0;
// 失败的形状:
for(int i=0;i<3;i++){
addSnake();
}
/*-------------------------------------------------------------------*/
// 成功的形状:
int i;
for(i=0;i<3;i++){
addSnake();
}
No.2:在判断蛇吃到食物时,少写了一个" = " ,但此时编译时也不会报错,导致调整了半天才降伏了虚空跳跃蛇; :#大胆妖孽,还不赶快束手就擒!
void moveSnake()
{
addSnake(); //增加一节尾节点;
// 这里 ⬇ 少了一个 "=",导致蛇变成了虚空跳跃蛇;
if(tail->hang = food.hang && tail->lie == food.lie){
//addSnake();
initFood();
}else{
delectSnakeHead(); //把头节点删了;
}
//delectSnakeHead();
//蛇死了,就初始化蛇;
if(ifSnakeDie()){
initSnake();
}
}
编译了:
虚空惊现:
99 81分钟,补上 "=" 号真言;
健康小蛇诞生: