C++实现贪吃蛇

C++实现贪吃蛇

该项目用到一个叫作eaxyX的图形库,具体使用参考以下链接
EasyX使用入门

效果截图

菜单界面
在这里插入图片描述
贪吃蛇
(红色为蛇头,绿色为蛇身,蓝色为水果,每吃到一个水果,蛇身会变长,蛇的移动速度会变快)
在这里插入图片描述
分数界面在这里插入图片描述

源代码

#include<graphics.h>
#include<conio.h>
#include<vector>
#include<map>
#include<string>
#include<list>
#include<cmath>
using namespace std;
//蛇方块长度
const int snakelength = 10;
//界面宽度
const int windowLength = 720;
//界面高度
const int windowHeight = 640;
//水果大小
const int fruitsize = 15;
//水果数量
const int fruitnum = 10;
//水果数量最小值,小于该数时添加水果
const int minfruitnum = 4;
//坐标更新使用的数组
int arr[4][4] = {
	{-snakelength,0,-snakelength,0},//左移
	{0,-snakelength,0,-snakelength},//上移
	{snakelength,0,snakelength,0},//右移
	{0,snakelength,0,snakelength}//下移
};
//记录蛇转弯时的坐标和下一个状态
struct Point
{
	int x;
	int y;
	int flag;
	Point(int xx, int yy, int ff)
		:x{ xx }, y{ yy }, flag{ ff }
	{}
};
//蛇类
class Snake
{
private:
	//蛇方块结构体
	struct snake
	{
		//左,上,右,下坐标
		int left;
		int top;
		int right;
		int bottom;
		//方向默认向右
		int flag;
		//记录蛇转弯时的坐标和方向
		list<Point>l;
		snake(int left, int top)
			:left{ left }, top{ top }, flag{ 2 }
		{
			right = left + snakelength;
			bottom = top + snakelength;
		}
	};
	//水果结构体
	struct Fruit
	{
		//坐标
		int x;
		int y;
		//是否显示
		bool ispicture;
	};
	//蛇方块数组
	vector<snake>s;
	//将键盘输入转换为数字
	map<string, int>mp;
	//水果数组
	vector<Fruit>fruit;
	//水果数量
	int picturefruitnum;
	//记录分数
	int score;
public:
	Snake()
	{
		//初始化蛇方块
		for (int i = 5; i > 0; --i)
		{
			s.push_back(snake(i * snakelength, snakelength));
		}
		//初始化水果
		fruit.resize(fruitnum);
		for (int i = 0; i < fruit.size(); ++i)
		{
			fruit[i].x = 30 + rand() % windowLength - 30;
			fruit[i].y = 30 + rand() % windowHeight - 30;
			fruit[i].ispicture = true;
		}
		//初始化水果数量
		this->picturefruitnum = fruit.size();
		//初始化分数
		this->score = 0;
		this->initmp();
	}
	Snake(int left, int top)
	{
		//初始化蛇方块
		s.push_back(snake(left, top));
		for (int i = 3; i >= 0; --i)
			s.push_back(snake(left - (4 - i) * snakelength, snakelength));
		//初始化水果
		fruit.resize(fruitnum);
		for (int i = 0; i < fruit.size(); ++i)
		{
			fruit[i].x = 30 + rand() % windowLength - 30;
			fruit[i].y = 30 + rand() % windowHeight - 30;
			fruit[i].ispicture = true;
		}
		//初始化水果数量
		this->picturefruitnum = fruit.size();
		//初始化分数
		this->score = 0;
		this->initmp();
	}
	//初始化map,便于后续将用户输入转换为对应的方向
	void initmp()
	{
		this->mp.insert(make_pair("a", 0));
		this->mp.insert(make_pair("A", 0));
		this->mp.insert(make_pair("w", 1));
		this->mp.insert(make_pair("W", 1));
		this->mp.insert(make_pair("d", 2));
		this->mp.insert(make_pair("D", 2));
		this->mp.insert(make_pair("s", 3));
		this->mp.insert(make_pair("S", 3));
	}
	//菜单
	void Menu()
	{
		//设置背景
		setbkmode(TRANSPARENT);
		setbkcolor(RGB(114, 107, 112));
		cleardevice();
		//鼠标
		MOUSEMSG msg;
		//设置填充颜色
		setfillcolor(RGB(131, 83, 47));
		//填充矩形
		fillrectangle(windowLength / 2 - 100, windowHeight / 2 - 100, windowLength / 2 + 100, windowHeight / 2 - 50);
		fillrectangle(windowLength / 2 - 100, windowHeight / 2 + 50, windowLength / 2 + 100, windowHeight / 2 + 100);
		//设置文字颜色和字体
		settextcolor(WHITE);
		settextstyle(40, 0, "楷体");
		outtextxy(windowLength / 2 - 75, windowHeight / 2 - 95, "开始游戏");
		outtextxy(windowLength / 2 - 75, windowHeight / 2 + 55, "退出游戏");
		while (1)
		{
			//获得鼠标信息
			msg = GetMouseMsg();
			BeginBatchDraw();
			//画选项按钮
			if (msg.x<windowLength / 2 + 100 && msg.x>windowLength / 2 - 100
				&& msg.y > windowHeight / 2 - 100 && msg.y < windowHeight / 2 - 50)
			{
				setfillcolor(RGB(248, 168, 95));
				fillrectangle(windowLength / 2 - 100, windowHeight / 2 - 100, windowLength / 2 + 100, windowHeight / 2 - 50);
				outtextxy(windowLength / 2 - 75, windowHeight / 2 - 95, "开始游戏");
				EndBatchDraw();
				//左击鼠标时,进入游戏
				if (msg.uMsg == WM_LBUTTONDOWN)
				{
					this->Game();
					break;
				}
			}
			else if (msg.x<windowLength / 2 + 100 && msg.x>windowLength / 2 - 100
				&& msg.y > windowHeight / 2 + 50 && msg.y < windowHeight / 2 + 100)
			{
				setfillcolor(RGB(248, 168, 95));
				fillrectangle(windowLength / 2 - 100, windowHeight / 2 + 50, windowLength / 2 + 100, windowHeight / 2 + 100);
				outtextxy(windowLength / 2 - 75, windowHeight / 2 + 55, "退出游戏");
				EndBatchDraw();
				//左击鼠标时,退出游戏
				if (msg.uMsg == WM_LBUTTONDOWN)
				{
					break;
				}
			}
			else
			{
				setfillcolor(RGB(131, 83, 47));
				fillrectangle(windowLength / 2 - 100, windowHeight / 2 - 100, windowLength / 2 + 100, windowHeight / 2 - 50);
				fillrectangle(windowLength / 2 - 100, windowHeight / 2 + 50, windowLength / 2 + 100, windowHeight / 2 + 100);
				settextcolor(WHITE);
				settextstyle(40, 0, "楷体");
				outtextxy(windowLength / 2 - 75, windowHeight / 2 - 95, "开始游戏");
				outtextxy(windowLength / 2 - 75, windowHeight / 2 + 55, "退出游戏");
				EndBatchDraw();
			}
		}
	}
	//更新水果位置和数量
	void UpdateFruit()
	{
		for (int i = 0; i < fruit.size(); ++i)
		{
			if (fruit[i].ispicture == false)
			{
				//通过伪随机数设置水果位置
				fruit[i].x = 30 + rand() % windowLength - 30;
				fruit[i].y = 30 + rand() % windowHeight - 30;
				fruit[i].ispicture = true;
			}
		}
		this->picturefruitnum = fruit.size();
	}
	//更新蛇方块坐标
	void UpdateSnake()
	{
		//用前面的arr数组更新蛇方块坐标
		s[0].left += arr[s[0].flag][0];
		s[0].top += arr[s[0].flag][1];
		s[0].right += arr[s[0].flag][2];
		s[0].bottom += arr[s[0].flag][3];
		for (int i = 1; i < s.size(); ++i)
		{
			s[i].left += arr[s[i].flag][0];
			s[i].top += arr[s[i].flag][1];
			s[i].right += arr[s[i].flag][2];
			s[i].bottom += arr[s[i].flag][3];
			if (!s[i].l.empty())
			{
				//到达转弯处,更新方向
				if (s[i].left == s[i].l.front().x && s[i].top == s[i].l.front().y)
				{
					s[i].flag = s[i].l.front().flag;
					//删除转弯位置
					s[i].l.pop_front();
				}
			}
		}
	}
	//开始游戏
	void Game()
	{
		//用于接受用户键盘输入
		char ch;
		//模拟蛇移动速度
		int speed = 120;
		while (1)
		{
			cleardevice();
			BeginBatchDraw();
			//画墙,蛇,水果
			this->DrawWall();
			this->DrawSnake();
			this->DrawFruit();
			//如果有键盘输入时
			if (_kbhit())
			{
				//获取键盘输入
				ch = _getch();
				//判断键盘输入
				if (ch == 'a' || ch == 'A' || ch == 'd' || ch == 'D'
					|| ch == 's' || ch == 'S' || ch == 'w' || ch == 'W')
				{
					//不允许突然从左变为右,从上变为下,反之亦然
					//转向成功后,添加list<Point>l,即添加蛇的转弯坐标
					string tmp(1, ch);
					if (mp[tmp] == 0 && s[0].flag != 2)
					{
						//更新蛇头方向
						s[0].flag = 0;
						//添加转弯处坐标
						for (int i = 1; i < s.size(); ++i)
							s[i].l.push_back(Point(s[0].left, s[0].top, s[0].flag));
					}
					else if (mp[tmp] == 1 && s[0].flag != 3)
					{
						//更新蛇头方向
						s[0].flag = 1;
						//添加转弯处坐标
						for (int i = 1; i < s.size(); ++i)
							s[i].l.push_back(Point(s[0].left, s[0].top, s[0].flag));
					}
					else if (mp[tmp] == 2 && s[0].flag != 0)
					{
						//更新蛇头方向
						s[0].flag = 2;
						//添加转弯处坐标
						for (int i = 1; i < s.size(); ++i)
							s[i].l.push_back(Point(s[0].left, s[0].top, s[0].flag));
					}
					else if (mp[tmp] == 3 && s[0].flag != 1)
					{
						//更新蛇头方向
						s[0].flag = 3;
						//添加转弯处坐标
						for (int i = 1; i < s.size(); ++i)
							s[i].l.push_back(Point(s[0].left, s[0].top, s[0].flag));
					}
				}
			}
			//蛇撞到墙时
			if (s[0].left<0 || s[0].right >windowLength
				|| s[0].top<0 || s[0].bottom>windowHeight)
			{
				EndBatchDraw();
				int cur = 1;
				while (cur < this->s.size())
				{
					BeginBatchDraw();
					setfillcolor(RED);
					for (int i = 0; i < cur; ++i)
						fillrectangle(s[i].left, s[i].top, s[i].right, s[i].bottom);
					setfillcolor(GREEN);
					for (int i = cur; i < s.size(); ++i)
						fillrectangle(s[i].left, s[i].top, s[i].right, s[i].bottom);
					++cur;
					Sleep(100);
					EndBatchDraw();
				}
				Sleep(100);
				//exit(0);
				break;
			}
			//判断蛇是否有吃到水果
			for (int i = 0; i < this->fruit.size(); ++i)
			{
				if (!fruit[i].ispicture)
					continue;
				if (pow(s[0].left + snakelength / 2 - fruit[i].x, 2) +
					pow(s[0].top + snakelength / 2 - fruit[i].y, 2) <= fruitsize * fruitsize)
				{
					//添加蛇尾
					if (s[s.size() - 1].flag == 0)
					{
						snake ss(s[s.size() - 1].left + snakelength, s[s.size() - 1].top);
						ss.flag = s[s.size() - 1].flag;
						s.push_back(ss);
					}
					else if (s[s.size() - 1].flag == 1)
					{
						snake ss(s[s.size() - 1].left, s[s.size() - 1].top + snakelength);
						ss.flag = s[s.size() - 1].flag;
						s.push_back(ss);
					}
					else if (s[s.size() - 1].flag == 2)
					{
						snake ss(s[s.size() - 1].left - snakelength, s[s.size() - 1].top);
						ss.flag = s[s.size() - 1].flag;
						s.push_back(ss);
					}
					else if (s[s.size() - 1].flag == 3)
					{
						snake ss(s[s.size() - 1].left, s[s.size() - 1].top - snakelength);
						ss.flag = s[s.size() - 1].flag;
						s.push_back(ss);
					}
					//为新蛇尾设置转弯链表
					for (list<Point>::iterator it = s[s.size() - 2].l.begin();
						it != s[s.size() - 2].l.end(); ++it)
					{
						s[s.size() - 1].l.push_back(*it);
					}
					//吃到的水果不再显示
					fruit[i].ispicture = false;
					//更新分数
					this->score += 10;
					//更新速度
					speed -= 2;
					//更新水果数量
					this->picturefruitnum--;
					if (this->picturefruitnum < minfruitnum)
						this->UpdateFruit();
					break;
				}
			}
			//更新蛇方块
			this->UpdateSnake();
			//睡眠,防止蛇走太快
			Sleep(speed);
			EndBatchDraw();
		}
	}
	//画墙
	void DrawWall()
	{
		setfillcolor(RGB(60, 83, 89));
		fillrectangle(0, 0, windowLength, snakelength);
		fillrectangle(0, 0, snakelength, windowHeight);
		fillrectangle(windowLength - snakelength, 0, windowLength, windowHeight);
		fillrectangle(0, windowHeight - snakelength, windowLength, windowHeight);
	}
	//画蛇
	void DrawSnake()
	{
		setfillcolor(RED);
		fillrectangle(s[0].left, s[0].top, s[0].right, s[0].bottom);
		setfillcolor(GREEN);
		for (int i = 1; i < s.size(); ++i)
			fillrectangle(s[i].left, s[i].top, s[i].right, s[i].bottom);
	}
	//画水果
	void DrawFruit()
	{
		setfillcolor(BLUE);
		for (int i = 0; i < this->fruit.size(); ++i)
		{
			if (fruit[i].ispicture)
				fillcircle(fruit[i].x, fruit[i].y, fruitsize);
		}
	}
	//显示分数
	void ShowScore()
	{
		//将int转化为string
		string Score = to_string(this->score);
		setbkmode(TRANSPARENT);
		setbkcolor(RGB(114, 107, 112));
		cleardevice();
		outtextxy(windowLength / 2 - 100, windowHeight / 2 - 50, "你的分数为:");
		outtextxy(windowLength / 2 - 100, windowHeight / 2, Score.c_str());
	}
};
int main()
{
	initgraph(windowLength, windowHeight);
	Snake s1;
	s1.Menu();
	s1.ShowScore();
	Sleep(1000);
	closegraph();
	return 0;
}

总结

技术不行,设计很简陋,而且功能比较少,后续有时间会继续完善。

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 C++ 贪吃蛇游戏代码,仅供参考: ```c++ #include <iostream> #include <conio.h> #include <windows.h> using namespace std; bool gameOver; const int width = 20; const int height = 20; int x, y, fruitX, fruitY, score; int tailX[100], tailY[100]; int nTail; enum eDirection { STOP = 0, LEFT, RIGHT, UP, DOWN }; eDirection dir; void Setup() { gameOver = false; dir = STOP; x = width / 2; y = height / 2; fruitX = rand() % width; fruitY = rand() % height; score = 0; } void Draw() { system("cls"); // 清屏 // 画上边界 for (int i = 0; i < width + 2; i++) cout << "#"; cout << endl; // 画中间部分 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (j == 0) cout << "#"; if (i == y && j == x) cout << "O"; else if (i == fruitY && j == fruitX) cout << "F"; else { bool print = false; for (int k = 0; k < nTail; k++) { if (tailX[k] == j && tailY[k] == i) { cout << "o"; print = true; } } if (!print) cout << " "; } if (j == width - 1) cout << "#"; } cout << endl; } // 画下边界 for (int i = 0; i < width + 2; i++) cout << "#"; cout << endl; // 输出得分 cout << "Score:" << score << endl; } void Input() { if (_kbhit()) { switch (_getch()) { case 'a': dir = LEFT; break; case 'd': dir = RIGHT; break; case 'w': dir = UP; break; case 's': dir = DOWN; break; case 'x': gameOver = true; break; } } } void Logic() { // 记录尾巴的位置 int prevX = tailX[0]; int prevY = tailY[0]; int prev2X, prev2Y; tailX[0] = x; tailY[0] = y; for (int i = 1; i < nTail; i++) { prev2X = tailX[i]; prev2Y = tailY[i]; tailX[i] = prevX; tailY[i] = prevY; prevX = prev2X; prevY = prev2Y; } // 根据方向移动蛇头 switch (dir) { case LEFT: x--; break; case RIGHT: x++; break; case UP: y--; break; case DOWN: y++; break; default: break; } // 判断是否吃到了水果 if (x == fruitX && y == fruitY) { score += 10; fruitX = rand() % width; fruitY = rand() % height; nTail++; } // 判断是否撞墙或撞到自己的尾巴 if (x < 0 || x >= width || y < 0 || y >= height) gameOver = true; for (int i = 0; i < nTail; i++) { if (tailX[i] == x && tailY[i] == y) gameOver = true; } } int main() { Setup(); while (!gameOver) { Draw(); Input(); Logic(); Sleep(50); // 控制游戏速度 } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值