c++ 实现贪吃蛇(附详细讲解) 您所在的位置:网站首页 蛇画什么 c++ 实现贪吃蛇(附详细讲解)

c++ 实现贪吃蛇(附详细讲解)

2024-07-08 10:28| 来源: 网络整理| 查看: 265

更新 更新了不会刷屏的贪吃蛇版本, 链接为:snake2。修复星星出现在墙上的bug。 需要用到的知识点 条件判断循环函数数组多cpp文件调用(不然你也可以直接写在一个文件里)指针结构体链表(采用头插法) 整个程序的实现流程 1. 画图

这一步是最简单的,没错,我的习惯就是从最简单的开始。

我们的目标是画一个框框,然后这个框框里有条蛇, 还有个星星,根据这个目标,最后的代码就会书写成这样子:

void draw(int **map, int *star, int height, int weight){ // 这里有一个需要关注的地方, 那就是边界是墙,那么到时候在判定的时候, // 我们要将墙体的大小算进到整个地图大小中,也就是height是包含了墙体了的 /* 清空屏幕 */ system("cls"); for(int i = 0; i < height; i++){ for(int j = 0; j < weight; j++){ if(map[i][j] == 1){ cout j] = 1; p = p->next; } return; }

然后绘图时,就还是一次画完就可以了, 不用不断遍历蛇是否在某个点上,没错,强迫症真的很可怕。

以上代码你可以简单理解为 map = map + snake(当然,这个加可不是真的数值运算哈,我再偷偷提醒你一下吧。)。

也许你会问我, 那为什么不把星星也直接加到map里呢?问得好,这是因为蛇是变长的, 蛇最长能达到map的大小,假如我们不把蛇先用以上方式加到map中的话,在绘图时进行检查,最糟糕情况会是O(N*N),N=height*weight的计算复杂度,这我能忍吗?这我不能忍,直接融入地图吧, 你这个蛇儿。那星星呢?星星只是一个点,他永远都是一个点而已,按照时间复杂度的计算法来看, 都没有改变O(N),所以我就把它保留了,免得再写个函数。

没想到第一步就要知道这么多东西吧。(看到这里的都是热爱学习的好宝宝呀)。

2. 地图刷新

这个地图刷新其实上面已经讲过了, 并且也说了为什么要那么做,我们就不再细讲,我们可以在绘制好地图和蛇之后做这个地图刷新,至于星星,如果你现在就想画可以直接跳到后面,不过因为星星的特殊功能,我们现在可以先不管他。

3. 运动

我想,动起来,是整个游戏最最最重要的事情,不会动, 有什么用呢?没错,没有任何用处。

我们先来明确运动部分的代码我们需要做的事情。

当按下方向键后, 蛇会按照按下的方向键运动(注意最终的目标不是马上运动,不过我们是开发者,可以先写个立刻执行的)。当没有输入任何命令时,蛇按照上一次的运动方向继续运动。(这个是第二阶段的目标,我们先来看看怎么实现第一阶段的目标吧。)

确定方向键:w:上,s:下,a:左,d:右。

确定ascii值:w:119, s:115,a:97, d:100。

ok,确定之后就可以直接撸代码了。

int move(struct snake *s, int *star){ int sign = 0; switch(ch){ case 119: /* 向上走 */ sign = _move(s, star, -1, 0); break; case 115: /* 向下走 */ sign = _move(s, star, 1, 0); break; case 97: /* 向左走 */ sign = _move(s, star, 0, -1); break; case 100: /* 向右走 */ sign = _move(s, star, 0, 1); break; } if(sign == 2){ // 吃到了星星 return 2; } /* 死亡检查 */ return check_dead(s); }

看到这个如果你不去深究_move这个函数到底怎么做的话,我相信你还是相当清晰到底是怎么回事的,那么我就接着将_move这个函数吧。

int _move(struct snake *s, int* star, int x, int y){ /* 运动检查 */ if(!check_action(s, x, y)){ // return 0; x = direct_x; y = direct_y; } /* 修改目前运动方向 */ direct_x = x; direct_y = y; /* 增加新节点 */ struct snake *top = s->next; struct snake *p = s->next; struct snake *Ntop; Ntop = (struct snake*)malloc(sizeof(struct snake)); Ntop->i = top->i + x; Ntop->j = top->j + y; s->next = Ntop; Ntop->next = top; /* 检查是否吃到星星 */ if(check_star(s, star) == 1){ return 2; } /* 删除旧节点 */ while(p->next->next != NULL){ p = p->next; } p->next = NULL; return 0; }

简单讲一下我没有提出来的代码(我也不准备后面贴出来),运动检查是看看这个输入的运动方向是否合理,比如不能倒退。还有更上面的死亡检查,就是看看是否这么走之后,蛇会不会撞死,我们知道它不能撞到自己,也不能撞到墙。

ok,这部分讲完我们开始讲上面贴出来的代码,如果方向错误的话,我们会选择使用原前进方向,也就说,如果你要倒退走的话,我们就直接照着之前的方向往前走,那么运动到底是怎么做到的,其实这里就需要你有个更加大局的看法了,我们之前把蛇融入到地图中去了,那么蛇的每个节点中的元素i,j其实代表的就是map中i,j坐标有蛇,且值是1或者2(当时蛇头时),所以,我们只需要把蛇前进方向的格子的坐标变成蛇头,变成蛇的新节点,然后删除尾节点就可以了。(这是没有吃到星星的情况下),如果吃到了星星,尾节点是不删除的,这样就可以实现长度+1。

好了,第一部分的运动我们说完了, 那么怎么做一个自动运动的蛇呢?其实这也很简单,只需要定时运动一下就好了, 前面我们知道我们有一个获取运动方向的函数,如下:

void getAction(){ int sign = 0; // 是否已输入按键 double duration; // 已检测时长 clock_t start, finish; start = clock(); while(1){ if(_kbhit() && sign == 0){ int _ch = _getch(); if(_ch == 119 || _ch == 115 || _ch == 97 || _ch == 100){ ch = _ch; sign = 1; } }else if(_kbhit() && sign == 1){ _getch(); } finish = clock(); duration = (double)(finish - start); if(duration > speed) break; } return; }

我们监视一定时长的键盘输入,并且将第一个有效输入保留到全局变量中的方向键ch中去,然后一直等到时间到了, 如果没有有效输入,就不做任何修改,我们会使用上次的方向键ch。这样子我们就得到了一个会执行一定时长的获取方向的模块了, 接下来就是调用这个模块的事情了,(没错,循环调用就好了, 这样就会没过一定时间获取一次,然后获取后运动,也就可以实现一段时间运动一次的目的了)。

一开始是想用多线程的, 后来发现定时的方式更加稳定且简单(毕竟多线程是一个很麻烦的东西,弱鸡伤不起)。

####4. 生成星星

生成星星的方式可以采用随机生成坐标的方式,然后检查是否会不会撞墙,也可以采用我代码中的方式,时间复杂度是相同的。

/* 生成星星 */ void generateStar(int **map, int *star, int height, int weight){ int sum = 0; int index = 0; // 计算有多少空格 for(int i = 0; i < height; i++) for(int j = 0; j < weight; j++) if(map[i][j] == 0) sum += 1; // 随机生成空格下标 index = random(sum); // 选中index个空格作为星星的生成位置 for(int i = 0; i < height; i++) for(int j = 0; j < weight; j++){ if(map[i][j] == 0) index -= 1; if(index == 0){ star[0] = i; star[1] = j; cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

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