贪吃蛇代码学习心得(C语言) 您所在的位置:网站首页 贪吃蛇c语言 贪吃蛇代码学习心得(C语言)

贪吃蛇代码学习心得(C语言)

2023-07-12 22:10| 来源: 网络整理| 查看: 265

贪吃蛇代码学习心得(C语言) 前言

以前自学C语言时,学了点控制台光标的相关知识,就一直想弄个小游戏出来,第一个想到的就是贪吃蛇了。上下左右控制方向,随机生成食物,吃上食物长度加一,碰到墙壁或咬到自己结束游戏......想到了很多,甚至当时兴奋的睡不着觉,可是自己一上手的时候,发现无从下手。最核心的如何让“蛇”动起来我都不知如何实现。我按照以前的按个键刷新屏幕的思路发现走不通。因为不按键的时候,蛇也是在动的,然后我就在网上看了些代码。当时没有静下心看明白,时隔几年再次学习C时,又想到了它。这次总算把它啃下来了。记录些心得,经验。

定义 #define U 1#define D 2#define L 3#define R 4       //蛇的状态,U:上 ;D:下;L:左 R:右​typedef struct SNAKE //蛇身的一个节点{    int x;    int y;    struct SNAKE *next;}snake;​//全局变量//int score = 0, add = 10;//总得分与每次吃食物得分。int status, sleeptime = 150,length = 4;//状态与每次运行的时间间隔与长度snake *head, *food;//蛇头指针,食物指针snake *q;//遍历蛇的时候用到的指针int endGamestatus = 0; //游戏结束的情况,1:撞到墙;2:咬到自己;3:主动退出游戏。

定义4个宏,这个好理解,方便以后判断条件

蛇身采用的是链表的结构,其实发现这个结构对于这个游戏来说相当完美的结构。蛇是连续结构,一个连一个,但最重要的是头,可以通过头结点遍历整个蛇身。而且x,y坐标可以同时存储在结构体当中,比较方便。

status和sleeptime是以后蛇动起来的关键,没有按键的情况下。

食物food和蛇身采用同一个结构体。

为了方便还引进了一个q指针指向头部。

函数解析 Pos(); void Pos(int x, int y)//设置光标位置{    COORD pos;    HANDLE hOutput;    pos.X = x;    pos.Y = y;    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄    SetConsoleCursorPosition(hOutput, pos);​    SetConsoleTextAttribute(hOutput,0x01|0x02); //设置颜色}

输入x,y坐标参数,将光标设置在此处。这里面几个东西要记住,以后用到的多。句柄的作用,在网上查说是windows为了控制虚拟内存里的元素所用的标识符号,是内部的东西(就当死记住的东西好啦)。我又在最后加了一句源代码没有的,改变颜色,聊胜于无吧~如果能蛇身一种颜色,食物一种颜色,边框,计分板,随着蛇身变长还能变换。不知道能不能实现。

creatMap(); void creatMap()//创建地图{    int i;    for (i = 0; iy = 5;    tail->next = NULL;    for(i=0;inext = tail;        head->x = 24 + 2 * i;//一个蛇身占两个格子        head->y = 5;        tail = head;   }    q = head;    while(q->next != NULL)   {        Pos(q->x,q->y);        printf("■");        q = q->next;   }}

头插法创建蛇身,这样头指针最后指向头部,最后将指针q指向蛇头。

我这里将源代码稍微改了下,初始化蛇身时就将q引进,而且输出蛇身时的while()里面的判断条件改了下,这下长度就是设置值了,源代码总是多一节。。。仔细分析,假设蛇身初始化长度为1,先创建了一个没有尾指针的tail,然后for()执行一次,head->next指向tail,tail再指向head,这里其实就有两个结点了,一个head和一个没有指向NULL的尾结点。如果按原代码的 tail != NULL,其实while()要执行两次,这样就比预想的长度多了一节。

bieteSelf(); int biteSelf()//判断是否咬到了自己{    snake *self;    self = head->next;    while (self != NULL)   {        if (self->x == head->x && self->y == head->y)       {            return 1;       }        self = self->next;   }    return 0;}

其实这个没什么好说的,本以为很好想,自己敲的时候才发现没那么容易。首先定义一个self指向head->next,然后遍历一遍,看和头部坐标有没有重合。这一个函数贯穿整个过程,每动一下,就要遍历一遍,其实计算量挺大的。(不过都计算机来说小儿科,都没有卡顿)

createFood(); void createFood()//随机出现食物{    snake *food_1;    srand((unsigned)time(NULL));    food_1 = (snake*)malloc(sizeof(snake));​    food_1->x = rand()% 52 + 2;    while((food_1->x % 2) != 0)   {        food_1->x = rand()% 52 + 2;   }    food_1->y = rand() % 24+1;   // q = head;    while(q != NULL)//食物不能和身体重合   {        if(q->x ==food_1->x && q->y == food_1->y)       {            free(food_1);            createFood();       }        q = q->next;   }​    Pos(food_1->x,food_1->y);    food = food_1;    printf("○");}

这里用了srand(),用于产生随机数。

因为蛇身的x轴方向占两个,所以food_1->x 必须为偶数,while()用来筛选。

cantCrossWall(); void cantCrossWall()//不能穿墙{    if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26)   {        endGamestatus = 1;        endGame();   }} snakeMove();【核心代码】 void snakeMove()//蛇前进,上U,下D,左L,右R{    snake * nexthead;    cantCrossWall();    nexthead = (snake*)malloc(sizeof(snake));    if(status == U)   //状态保持向上,即一直向上运动   {        nexthead->x = head->x;        nexthead->y = head->y - 1;        if(nexthead->x == food->x&&nexthead->y == food->y) //如果有食物       {            nexthead->next = head;            head = nexthead;            q= head;            while(q->next != NULL)           {                Pos(q->x,q->y);                printf("■");                q = q->next;           }            score += add;            createFood();       }​        else             //没有食物           {                nexthead->next = head;                head = nexthead;                q= head;                while(q->next->next != NULL)               {                    Pos(q->x,q->y);                    printf("■");                    q = q->next;               }                Pos(q->x,q->y);                printf(" ");                free(q->next);                q->next = NULL;     //将指针置空​           }   }    if (status == D)   {        nexthead->x = head->x;        nexthead->y = head->y + 1;        if (nexthead->x == food->x && nexthead->y == food->y)  //有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            score = score + add;            createFood();       }        else           //没有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q->next->next != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            Pos(q->x, q->y);            printf(" ");            free(q->next);            q->next = NULL;       }   }    if (status == L)   {        nexthead->x = head->x - 2;      //横向占两格        nexthead->y = head->y;        if (nexthead->x == food->x && nexthead->y == food->y)//有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            score = score + add;            createFood();       }        else                                //没有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q->next->next != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            Pos(q->x, q->y);            printf(" ");            free(q->next);            q->next = NULL;       }   }    if (status == R)   {        nexthead->x = head->x + 2;      //横向占两格        nexthead->y = head->y;        if (nexthead->x == food->x && nexthead->y == food->y)//有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            score = score + add;            createFood();       }        else                                         //没有食物       {            nexthead->next = head;            head = nexthead;            q = head;            while (q->next->next != NULL)           {                Pos(q->x, q->y);                printf("■");                q = q->next;           }            Pos(q->x, q->y);            printf(" ");            free(q->next);            q->next = NULL;       }   }    if (biteSelf() == 1)       //判断是否会咬到自己   {        endGamestatus = 2;        endGame();   }}

这里非常巧秒的引入了一个nexthead的指针来表示蛇的下一步,而蛇的下一步和最后按键又有关系,所以最开声明了status变量。然后根据if()判断条件来选择对应的走法。真的秒!

进入if()后,又分为两种情况,下一步有食物和下一步没有食物。若有食物,吃掉它,食物消失,长度加一;若没有,就保持原状态继续移动。

先来看看有食物:nexthead的坐标和food的坐标重合,就可以满足。然后使用头插法,创建头结点,最后再将q指向新的头结点,之后while()执行输出,这里判断条件为q->next !=NULL;如果按源代码的 q != NULL;长度又会多一节。原因还是初始化(假设长度为1)时,内存中期是有两个结点,而吃掉食物后,创建了一个新的结点,这时内存中有3个,而我们只需要打印两个。吃完之后,相应的分数增加,在生成新的食物。

没有食物:懂了上面的,很显然while这里要用 q->next->next!=NULL 来作判断条件,打印完蛇身,这时你会发现多了一节,(调试过程可以很明显的看出)因为开始的那个位置的图像还存留着,所以这是要用Pos来设置光标位置,while()走完时,q指向q->next,即头最开始位置(这里都假设蛇身长度为一),然后有空格覆盖,注意时两个空格!之后在释放掉 q->next,即初始化时只有数据域的结点,而q变为新的没有尾指针的结点

上下左右,四种状态,只要把坐标变化值稍作改变,其余都一样。需注意的是:x轴方向每次变化值为2。

最后,每走一步需要判断是否会咬到自己。

pause(); void pause()//暂停{    while (1)       //使用无限循环达成   {        Sleep(300);        if (GetAsyncKeyState(VK_SPACE))       {            break;       }   }}

 

runGame();【核心代码】 void runGame()//控制游戏{​    Pos(64, 15);    printf("不能穿墙,不能咬到自己\n");    Pos(64, 16);    printf("用↑.↓.←.→分别控制蛇的移动.");    Pos(64, 17);    printf("F1 为加速,F2 为减速\n");    Pos(64, 18);    printf("ESC :退出游戏.space:暂停游戏.");    Pos(64, 20);    //printf("C语言研究中心 www.clang.cc");    status = R;     //初始状态    while (1)   {        Pos(64, 10);        printf("得分:%d ", score);        Pos(64, 11);        printf("每个食物得分:%d分", add);        if (GetAsyncKeyState(VK_UP) && status != D)       {            status = U;       }        else if (GetAsyncKeyState(VK_DOWN) && status != U)       {            status = D;       }        else if (GetAsyncKeyState(VK_LEFT) && status != R)       {            status = L;       }        else if (GetAsyncKeyState(VK_RIGHT) && status != L)       {            status = R;       }        else if (GetAsyncKeyState(VK_SPACE))       {            pause();       }        else if (GetAsyncKeyState(VK_ESCAPE))       {            endGamestatus = 3;            break;       }        else if (GetAsyncKeyState(VK_F1))       {            if (sleeptime >= 50)           {                sleeptime = sleeptime - 30;                add = add + 2;                if (sleeptime == 320)               {                    add = 2;//防止减到1之后再加回来有错               }           }       }        else if (GetAsyncKeyState(VK_F2))       {            if (sleeptime


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有