IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++实战贪食蛇,简单易理解 2021-08-16 -> 正文阅读

[C++知识库]C++实战贪食蛇,简单易理解 2021-08-16

C++实战贪食蛇

学了那么久的C++语法,没有点小项目来玩玩的话是会感觉挺枯燥的。这篇文章记录C++的贪食蛇,简单易懂。

首先使用的是多文件的写法,代码开起来不会太多。直接开始吧。

一、总体思路

把需要的蛇、食物、坐标都封装成类,然后在主函数中去调用蛇的形状就用多个矩形连起来来代替。食物是随机在界面中产生食物也是用矩形来代替。食物中有一个属性是用来标识蛇有没有吃掉食物的。蛇吃了食物就把标记变换,然后if判断判断标记是否改变得出食物是否被吃掉。食物被吃掉之后,在可见的界面中随机一个位置产生一个食物,然后蛇自己的身体增一。所以蛇中就必须有一个属性是用来表示蛇身长度的或者说大小。蛇和食物都是需要定位的,所以也封装了一个点类,当蛇头碰到食物的时候,也就是蛇头的坐标等于食物的坐标的时候,就是吃的过程。然后就是蛇的移动和改方向(改方向需要按键交互,只需改变蛇的头部方向即可,蛇身的节点直接使用上一个节点的坐标即可)。最后是结束游戏的判断,有主要的两个条件会使游戏结束:一是蛇头碰到墙,二是蛇头碰到自己的身体。第一个只需判断是否出界即可,第二个就比较有意思,先把蛇头的坐标记录下来,然后就用循环去判断蛇头的坐标是否等于蛇身中每个节点的坐标 。条件成立就结束游戏,不成立就继续。这里为什么要记录下来呢?因为不记录下来的话,蛇头和第一个要判断的蛇身节点判断结束后蛇头就变了,变的速度很快。所以要记录下来才行,不记录直接判断的话是有Bug的。

1.1 开发环境

Visual Studio 2019

1.2 插件

easyX 如果没有的话直接在网上下载就好,用不到五分钟。使用的时候直接包含头文件easyx.h

#include<easyx.h>

1.3 自定义的头文件和源文件

封装类,采用面向对象和面向过程的相互结合。

每个类写在一个头文件中。

已经分别创建三个头文件

头文件源文件
Pointpoint.hpoint.cpp
Snakesnake.hsnake.cpp
Foodfood.hfood.cpp

1.3.1 头文件

  • Point类(点,用来定位),在自定义头文件"point.h"中。
#pragma once
//创建一个点类,用于定位蛇和食物
class Point
{
public:
	//缺省的方式写构造函数
	Point(int x=0,int y=0):x(x),y(y){}
	int& getX();	//X坐标的接口
	int& getY();	//Y坐标的接口
protected:
	int x;
	int y;
};
  • Snake类(蛇,封装蛇的属性和行为),在自定义头文件"snake.h"中。
#pragma once
#include<easyx.h>	//包含图形库头文件,画图使用
#include"point.h"
#include<ctime>		//包含时间头文件,我们想让蛇的身体一闪一闪的
#include"food.h"	//包含食物头文件,因为等下的吃食物的函数中要用到Food的对象
class Snake
{
public:
	//枚举
	enum posNum
	{
		//分别是0	 1 2 3	的别名
		LEFT,RIGHT,UP,DOWN
	};
	//缺省的方式写构造函数,传的是点的个数
	Snake(int size = 100);
	void drawSnake();			//画蛇
	void moveSnake();			//移动蛇
	void keyDown();				//按键交互
	void eatFood(Food& food);	//吃食物
	bool GameOver();			//判断游戏是否结束
	int& getCurSize();			//获取当前的大小,主要用于统计最后的分数
protected:
	Point* pos;		//点类的指针
	int curSize;	//当前蛇身的大小
	int position;	//方向,按键交互的时候就是改变蛇头的方向即可
};
  • Food类(食物,封装行为和属性),在自定义头文件"food.h"中
#pragma once
#include<easyx.h>
#include"point.h"
#include<ctime>
class Food
{
	//声明Snake类是Food类的友元类,方便Snake直接访问到Food的成员
	friend class Snake;
public:
	Food();
	void drawFood();	//画食物
	int& getFlag();		//获取标记,再蛇吃完食物后需要改变标记
protected:
	Point* pos;
	int flag;
};

1.3.2源文件

  • point.cpp的实现
#include "point.h"

int& Point::getX()
{
	// TODO: 在此处插入 return 语句
	return x;
}

int& Point::getY()
{
	// TODO: 在此处插入 return 语句
	return y;
}
  • snake.cpp的实现
#include "snake.h"

//类外实现蛇的构造函数
Snake::Snake(int size)
{
	curSize = 3;			//初始化蛇身大小是3个节点
	pos = new Point[size];	//pos指针指向堆区中的内存
	position = RIGHT;		//初始化蛇头方向为右边
	pos[0].getX() = 0;		//pos[0]表示的就是蛇头
	pos[0].getY() = 0;
}

//画蛇
void Snake::drawSnake()
{
	for (int i = 0; i < curSize; i++)
	{
		setlinecolor(BLACK);		//设置线条是黑色
		setfillcolor(RGB(rand() % 256, rand() % 256, rand() % 256));	//随机颜色,让蛇一闪一闪的
		fillrectangle(pos[i].getX(), pos[i].getY(), pos[i].getX() + 10, pos[i].getY() + 10);	
		//蛇的节点是10*10大小
	}
}

//移动蛇
void Snake::moveSnake()
{
	//蛇身
	for (int i = curSize - 1; i > 0; i--)
	{
		pos[i].getX() = pos[i - 1].getX();
		pos[i].getY() = pos[i - 1].getY();
	}

	//蛇头
	switch (position)
	{
	case LEFT:
		pos[0].getX() -= 10;
		break;
	case RIGHT:
		pos[0].getX() += 10;
		break;
	case UP:
		pos[0].getY() -= 10;
		break;
	case DOWN:
		pos[0].getY() += 10;
		break;
	}
}

//按键交互
void Snake::keyDown()
{
	//GetAsyncKeyState一般用来做游戏
	if (position != DOWN && (GetAsyncKeyState('W') || GetAsyncKeyState(VK_UP)))
	{
		position = UP;
	}
	if (position != UP && (GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN)))
	{
		position = DOWN;
	}
	if (position != LEFT&& (GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT)))
	{
		position = RIGHT;
	}
	if (position != RIGHT && (GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT)))
	{
		position = LEFT;
	}
}

//吃食物
void Snake::eatFood(Food& food)
{
	if (pos[0].getX() == food.pos->getX() && pos[0].getY() == food.pos->getY())
	{
		food.flag = 0;	//吃到食物后就将食物的标记置为0,原来是1,这样就有表示食物被吃了
		curSize++;		//吃完食物后蛇身增加1
	}
}

//判断游戏是否结束,即蛇碰到墙或者是碰到自己的身体
bool Snake::GameOver()
{
	bool bNum = false;
	//判断蛇是否碰到墙
	if (pos[0].getX() < 0 || pos[0].getX() > 800 || pos[0].getY() < 0 || pos[0].getY() > 600)
		return true;
	int x = pos->getX();	//把蛇头的X坐标记录下来
	int y = pos->getY();	//把蛇头的Y坐标记录下来
	//如果不记录就比较的话会出Bug,因为蛇头一直都在移动,刚和一个节点比较就变了这明显不是想要的
	for (int i = 1; i < curSize; i++)
	{
		//从第二个节点开始遍历蛇身,如果坐标相同就是碰到自己身体了
		if (pos[i].getX() == x && pos[i].getY() == y)
		{
			bNum = true;
		}
	}
	return bNum;
}

//获取当前大小
int& Snake::getCurSize()
{
	// TODO: 在此处插入 return 语句
	return curSize;
}

  • food.cpp的实现
#include "food.h"

Food::Food()
{
	//new出内存后才能使用
	pos = new Point;
	pos->getX() = rand() % 80 * 10;
	pos->getY() = rand() % 60 * 10;
	flag = 1;	//初始化为1,食物被吃后就赋值为0
}

//画食物
void Food::drawFood()
{
	setfillcolor(RGB(rand() % 256, rand() % 256, rand() % 256));
	fillrectangle(pos->getX(), pos->getY(), pos->getX() + 10, pos->getY() + 10);
}

int& Food::getFlag()
{
	// TODO: 在此处插入 return 语句
	return flag;
}

二、细节内容

使用到友元类的时候,分清楚哪个是哪个的友元,分清楚哪个头文件应该包含哪个头文件

在snake.h中因为,需要蛇身是一闪一闪的,所以需要随机函数rand(),所以snake.h中要包含时间头文件,利用时间来给播种,播种的函数srand()写在主函数所在的测试源文件即可。

//snake.h中
#pragma once	//表示只编译一次的意思
#include<easyx.h>
#include<ctime>	//或者写#include<time.h>
#include"point.h"
#include"food.h"	//因为在Food类中声明了Snake是Food的友元类,所以在snake.h要包含food.h

不能再在food.h头文件中包含snake.h,因为这样写程序会编译snake.h的时候先编译food.h,而在food.h中,编译food.h的时候又先编译snake.h,两者矛盾了。所以不要这么写

三、测试源代码

testApp.cpp

#include<iostream>
#include"snake.h"	//封装一个蛇类
#include"food.h"	//封装食物类
#include<conio.h>	//_getch()的头文件,用来卡屏

using namespace std;
void StartGameText();	//游戏开始前输出文字

int main()
{
	//播种,为食物随机产生做准备
	srand((unsigned int)time(NULL));
	initgraph(800, 600);	//创建窗口大小
	Snake* pSnake = new Snake;	//创建一只蛇
	Food* pFood = new Food;		//创建食物
	StartGameText();
	setbkcolor(WHITE);		//设置背景颜色为白色
	cleardevice();			//设置背景颜色的时候要刷新
	BeginBatchDraw();		//双缓冲
	while (1)
	{
		cleardevice();			//每次画图后就要刷新,要不然会留下痕迹
		pSnake->drawSnake();	//画蛇
		pSnake->moveSnake();	//移动蛇
		pSnake->keyDown();		//按键交互
		pFood->drawFood();		//画食物
		pSnake->eatFood(*pFood);	//吃食物
		if (pFood->getFlag() == 0)	//判断食物是否被吃
		{
			//食物被吃后就再产生一个新的食物,就是new出来
			pFood = new Food;
		}
		if (pSnake->GameOver())		//判断游戏是否结束
		{
			//结束就break跳出来
			break;
		}
		Sleep(50);		//延时50毫秒,要不然程序运行的太快了
		FlushBatchDraw();
	}
	EndBatchDraw();
	closegraph();	//关闭图形界面
	//最后输出分数
	cout << "游戏结束" << endl;
	cout << "最终分数:" << pSnake->getCurSize() << endl;
	return 0;
}

void StartGameText()
{
	setfillcolor(RGB(0, 255, 255));
	settextstyle(50, 0, L"华文行楷");
	outtextxy(200, 150, L"按任意键开始游戏");
	char key = _getch();
}

只需把上面遇到的自定义头文件和源文件还有测试源文件都写在一个项目里,然后运行就可以。

四、效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:11:24  更:2021-08-17 15:12:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 7:33:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码