【用C++面向对象的思想以及STL完成贪吃蛇游戏】

1. 前言

1.1 目的

以前对STL的知识不怎么关心,最近在工作中觉得工程代码用到STL的地方太多了,没看懂,再不补一补就快秃头了,于是秉承着学习的态度,重温了C++ STL,然后就想到了可以写个贪吃蛇来现学现卖,加深映像。

1.2 建议

建议在阅读本文之前,对STL陌生的同学可以先温习下STL。

1.3 开发环境

语言:C++
环境:Visual Stdio 2022 、 Windows

2. 成果展示

不墨迹,先展示成果。
在这里插入图片描述
在这里插入图片描述

3. 项目需求

1.初始化游戏,生成地图,蛇,食物
2.蛇吃到食物身体变长
3.蛇不能反向移动
4.食物被吃后立即生产新的
5.记录吃到的食物个数(得分)
6.食物不能生成在蛇身
7.蛇不能撞自己
8.设置游戏难度
9.蛇如果撞墙,直接游戏结束

4. 详细设计

4.1 概览

前面介绍了,是用的面向对象的思想来写的,那必然要生成蛇类、食物类、点类,为啥不是地图类,因为我是先用的画点的方法,然后用点来画方形地图。而且还可以用点类来画蛇,画食物。

蛇类:

在这里插入图片描述

食物类:
在这里插入图片描述

点类:
在这里插入图片描述

4.2 初始化

4.2.1 地图

在介绍怎么初始化地图之前,我们先了解下,怎么在终端用代码输出一个小方块。
没错,就是cout打印出来就可以了,但是我想在一个已知的坐标打印出来呢?
那就需要用到光标定位:

void gotoxy(int x, int y) //光标定位
{
    
    
	COORD pos = {
    
     x,y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

然后再输出方块就行,用某狗输入法里面的符号大全:
在这里插入图片描述
然后就可以在特定的坐标打印方块,那么也就可以继续实现打印出自定义宽高的地图。
需要强调一点,终端的光标的高 = 2 * 宽,所以我们方块的坐标想在终端输出(4,4)的位置,就需要在代码中用(8,4)来代替。

4.2.1 蛇和食物

那我们继续了解下怎么初始化蛇和食物,初始化肯定是需要用随机坐标来产生,这就需要用到rand()这个函数,然后可以产生一个【a,b】区间的整数:

randNum = rand() % (b - a + 1) + a;

那么久可以简单的在终端上随机生产一个蛇头和食物了。

整条蛇我是用容器 vector< pair< int, int > >来存储坐标的,食物我是用 pair< int, int >

4.3 蛇吃到食物身体变长

首先需要实现蛇的移动逻辑:初始化蛇向右移动,假设蛇头坐标(x , y)那把这个坐标(x-1 , y) 添加到 vector的尾元素,然后删除头元素,及 "插尾删头"

这里我们还需要定义一个oldSnack vector来打印空格,来抹去上一次蛇的身体,不然蛇是会一直覆盖,你会发现,蛇尾看起来没有被删除。

然后就可以实现蛇吃到食物身体变长,这个逻辑是通过添加食物坐标到vector的尾元素,且不删除头元素,及“只插尾”

4.4 蛇不能反向移动

也就是,假如蛇移动方向是右,我不小心按了A键(左),蛇是不能后退的。
需要定义一个变量oldDirection,记录上一次的移动方向,来和当前获取的方向进行判断。
在这里插入图片描述

4.5 食物被吃后刷新

刷新很简单,重新生成一个就好,但是怎么判断食物被吃呢,可以当前蛇头的坐标是否等于食物坐标,
如果不等,说明没吃到,那就继续移动蛇身,如果吃到了,重新产生食物。
在这里插入图片描述
记录吃到的食物个数,也在这一块儿,上面截图里的myCount,如果想做个分数,自己乘以一个食物分数系数就可以实现。

4.6 食物不能生成在蛇身

这里需要先重新生成,然后再进行判断
在这里插入图片描述
这里把判断食物是否产生在蛇身封装了一下,用STL算法里的find():

bool IsFoodEqualSnack(vector<pair<int, int>> vec, pair<int, int> food)
{
    
    
	if (!vec.empty())
	{
    
    
		auto result = find(begin(vec), end(vec), food);
		if ( result != vec.end() )
		{
    
    
			return true;
		}
		else
		{
    
    
			return false;
		}
	}
	else
	{
    
    
		cout << "vector is empty" << endl;
		return false;
	}
	cout << endl;

}

4.7 设置游戏难度

目前是通过Sleep() 来控制移动速度的,逻辑是当前蛇长度减初始化时候的长度,然后对2取余,也就是每吃到两个食物,移动速度增加一档。

void setSpeed(Snack objSnack)
{
    
    
	Sleep( 1000 / speedLevel ); 
	if ( (objSnack.mV.size() - 3 != 0))
	{
    
    
		if ( (oldspeed != (objSnack.mV.size() - 3) % 2) &&
			 ((objSnack.mV.size() - 3) % 2 == 0) )
		{
    
    
			speedLevel++;
		}
	}
	oldspeed = (objSnack.mV.size() - 3) % 2;
}

4.8 蛇不能撞自己

这里我们需要的逻辑是,蛇身vector中不能中重复的元素,这怎么实现呢?
很简单,利用set容器的唯一特性,我们把蛇身vector赋值给一个set容器,判断set的size和蛇身vector的size是否相同,是则不重复,就是没有撞到自己,反之则是装到自己了,GameOver。

bool Snack::isHitItself()
{
    
    
    mSet = {
    
     mV.begin(),mV.end() };
    if (mSet.size() != mV.size())//蛇头撞到了蛇身
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}

4.8 蛇不能撞墙

这个很简单,没啥好说的。

bool Snack::isHitWall()
{
    
    
    if ( (mV.end()-1)->first == MAP_X *2  ||
         (mV.end() - 1)->first == 0       ||
         (mV.end() - 1)->second == MAP_Y  ||
         (mV.end() - 1)->second == 0)
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}

5. 流程图

在这里插入图片描述

6. 代码

环境对了就绝对能正常运行,开发过程中已经解决了99.99%的bug,已经没有bug了。
代码下载

7. 结语

本文内容完全原创,不允许转载!!!

猜你喜欢

转载自blog.csdn.net/qq_45458713/article/details/129410974