C++编写贪吃蛇(使用类与对象) 您所在的位置:网站首页 scratch30贪吃蛇 C++编写贪吃蛇(使用类与对象)

C++编写贪吃蛇(使用类与对象)

2023-08-17 23:46| 来源: 网络整理| 查看: 265

C++编写贪吃蛇(使用类与对象)

完成了一个学期的C++学习后我决定用C++编写一个贪吃蛇。本以为是一个十分基础的问题,网上应该有很多代码教程,但找了好久都没有合适的。要么没有使用类和对象来编写,要么连编译都通不过。无奈只能从头开始自己摸索着写。本人水平有限,代码算法可能存在着一些问题,欢迎读到这篇文章有其他想法的人在评论区理性讨论,大家互相学习。

内容开始前:欢迎来我的博客

思路

贪吃蛇游戏主要有这样三个部分:地图、蛇和食物。地图是蛇运动和食物生成的地方且在地图周围存在着墙作为边界。游戏运行的过程大致如下:

1.游戏开始,生成地图并生成墙; 2.生成蛇,包括初始化蛇位置、长度和初始运动方向; 3.生成食物; 4.蛇朝着当前方向移动,直到接收到玩家改变运动方向的指令或蛇撞到墙或自身使游戏结束; 5.当蛇头下一步就要吃到食物时,蛇的长度加一,同时生成新的食物; 6.当蛇的长度达到最大值或死亡时游戏结束。

代码实现 点类

在贪吃蛇中,不论是蛇本身还是食物都是由点组成的,在代码编写过程中,需要进行大量的对点的操作。 在编写游戏本体之前,我首先声明定义了点(Point)类,并编写了一些函数以便后面使用。

class Point { private: COORD pos; public: Point () { pos.X = 0; pos.Y = 0; } Point (UINT x , UINT y) { pos.X = x; pos.Y = y; } Point (const Point &p) :pos (p.pos) { } void setPos (UINT x , UINT y) { pos.X = x; pos.Y = y; } void setPosPoint (const Point &p) { pos = p.pos; } void setPosRandom (int isHead = 0) //生成随机位置的点,主要用于蛇的初始化和食物的生成 //和食物不同,蛇头具有特殊性,它不能过于靠近地图的边缘,所以这里设计了isHead参数 //当isHead不填时默认为零,此时用于生成食物的位置 //当isHead传入参数为1时生成蛇头位置 { if (isHead == 1) { srand (time (NULL)); pos.X = rand () % 18 + 1; pos.Y = rand () % 10 + 7; } else { srand (time (NULL)); pos.X = rand () % 18 + 1; pos.Y = rand () % 18 + 1; } } void movePoint (int d) { if (d == 1) { pos.Y--; } else if (d == 2) { pos.Y++; } else if (d == 3) { pos.X--; } else if (d == 4) { pos.X++; } } COORD getPos (void) { return pos; } void printPoint (char c) { HANDLE hOutput = GetStdHandle (STD_OUTPUT_HANDLE); SetConsoleCursorPosition (hOutput , pos); cout lenth = 3; p[0].setPosRandom (1); p[1].setPos (p[0].getPos ().X , p[0].getPos ().Y + 1); p[2].setPos (p[1].getPos ().X , p[1].getPos ().Y + 1); dirction = 1; } };

蛇这个类本身十分简单,成员函数只有一个无参构造函数。设计这个类的目的主要是储存与蛇本身有关的一些变量,并对蛇进行初始化。为了方便后面的操作,我将另一个Game类声明为Snake类的友元,这样Game中的函数也可以对Snake类中的变量直接进行操作了。

游戏功能 class Game { private: Snake s; int map[20][20] = {0};//0为空地 1为蛇身 2为食物 -1为墙 Point food; int score; public: Game () { for (int i = 0; i map[s.p[i].getPos ().X][s.p[i].getPos ().Y] = 1; } for (int i = 0; i Point food; while (map[food.getPos ().X][food.getPos ().Y] != 0)//只有当随机生成的位置在map中为0,也就是为空地时才进行下一步操作 { food.setPosRandom (); } map[food.getPos ().X][food.getPos ().Y] = 2; food.printPoint ('$'); } void eatFood (void) { score = score + 100; Point temph (s.p[0]); Point tempt (s.p[s.lenth - 1]); s.p[0].movePoint (s.dirction); map[s.p[0].getPos ().X][s.p[0].getPos ().Y] = 1; s.p[0].printPoint ('O'); for (int i = s.lenth - 1; i > 1; i--) { s.p[i].setPosPoint (s.p[i - 1]); } s.p[1].setPosPoint (temph); s.lenth++; s.p[s.lenth - 1].setPosPoint (tempt); Sleep (500); } void snakeGo (void) { Point temp (s.p[0]); s.p[0].movePoint (s.dirction); map[s.p[0].getPos ().X][s.p[0].getPos ().Y] = 1; s.p[0].printPoint ('O'); map[s.p[s.lenth - 1].getPos ().X][s.p[s.lenth - 1].getPos ().Y] = 0; s.p[s.lenth - 1].printPoint (' '); for (int i = s.lenth - 1; i > 1; i--) { s.p[i].setPosPoint (s.p[i - 1]); } s.p[1].setPosPoint (temp); Sleep (500); } int isEatorEnd (Point q , int dirc) //传入蛇头坐标和此时蛇行进的方向,然后判断蛇下一次运动后是否吃到食物或撞到墙壁或自身,这里利用map进行判断 { q.movePoint (dirc); if (map[q.getPos ().X][q.getPos ().Y] == -1 || map[q.getPos ().X][q.getPos ().Y] == 1) { return -1; } else if (map[q.getPos ().X][q.getPos ().Y] == 2) { return 1; } else { return 0; } } void setDirection (char d) //注意到蛇在运动过程中不能直接向反方向移动 //比如当蛇向上走时只能向左右转而不能直接向后转 { if (d == 'w' && s.dirction != 2) { s.dirction = 1; } else if (d == 's' && s.dirction != 1) { s.dirction = 2; } else if (d == 'a' && s.dirction != 4) { s.dirction = 3; } else if (d == 'd' && s.dirction != 3) { s.dirction = 4; } else { return; } } Point &getHead (void) { return s.p[0]; } int getDirc (void) { return s.dirction; } int getPoint (void) { return score; } int getLenth (void) { return s.lenth; } };

贪吃蛇游戏的主要功能都是在Game类中定义的。Game类中有一个int型的二维数组,里面储存着整个地图中各个位置目前的状态,方便后面条件判断使用。 一些简单的函数的说明都以注释的方式写到代码里了,这里主要说一下snakeGo和eatFood这两个函数。

snakeGo函数

snakeGo函数可以说是整个游戏的核心所在。这里简单示意一下蛇运动一次需要经历的步骤。 这里首先要说明一下,游戏中一共有三个地方储存蛇的位置:蛇类中Point类里储存的控制台坐标,变量map中相应位置储存的“1”和控制台窗口上实际打印出的蛇。蛇要完成一次移动,就要将这三个都进行改变。 第一步要先将表示蛇头点的控制台坐标暂时储存下来;第二步要将储存蛇头位置的三个地方进行相应的改动;第三步将蛇尾从map和控制台窗口中抹去;最后一步将中间蛇身的所有点的坐标变为其前一个点的坐标。 这里可以注意到在移动过程中控制台窗口只需关注蛇头和蛇尾的变化情况即可。 函数最后的Sleep函数实现了蛇移动后的短暂停顿。

eatFood函数

eatFood函数用来控制当蛇吃到食物时的运动状况。当判断到蛇在下一次运动时会吃到食物时(通过主函数调用isEatorEnd函数实现),主函数会调用eatFood函数进行蛇的运动而不是snakeGo并在执行完成后直接进入下一循环。 eatFood函数与snakeGo基本相同,不同点在于要在snake类中p数组中增加蛇尾位置的信息,lenth变量中增加蛇的长度。

主循环

接下来就是程序的执行部分,欢迎界面后便是主循环,游戏结束前的部分都将在这里完成。主循环内的二层循环以kbhit函数作为判断条件,函数的功能可以自行搜索,在用户改变蛇前进方向之前的部分都在这里进行。在每次循环进行时,会首先判断游戏是否结束或蛇是否将要吃到食物,然后再进行蛇的移动操作。

int main () { cout int i = g.isEatorEnd (g.getHead () , g.getDirc ()); if (i == 1) { g.eatFood (); if (g.getLenth () == 361) { goto L; } g.creatFood (); continue; } else if (i == -1) { goto L; } g.snakeGo (); } char d = _getch (); g.setDirection (d); } L:system ("cls"); cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

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