• C++基于EasyX制作贪吃蛇游戏(六)第三版代码与程序


    本文首发于我的个人博客www.colourso.top,欢迎来访。

    上接C++基于EasyX制作贪吃蛇游戏(五)第三版文档 ,本文是代码的实现。

    开发环境

    工具:VS2017

    依赖:EasyX Library for C++ (Ver:20200520(beta))

    源代码

    源代码可能有点多,但还是决定粘一份上来,底部会贴出网盘地址以及GIthub地址的。

    common.h

    #pragma once
    
    #include <string>
    #include <conio.h>
    #include <graphics.h>
    
    //蛇的节点半径
    #define SNAKE_RADIU 9
    //食物的半径
    #define FOOD_RADIU 8
    
    //蛇的节点宽度
    #define SNAKE_WIDTH 20
    
    //COLORREF BG_COLOR = RGB(0,0,0);		//背景颜色为黑色
    
    #define BG_COLOR 0
    
    
    //方向的枚举
    enum class Dir { DIR_UP = 1, DIR_RIGHT = 2, DIR_DOWN = 3, DIR_LEFT = 4 };
    
    //点的结构体
    struct Point {
    	int x;
    	int y;
    
    	Point() :x(-1), y(-1) {}
    	Point(int dx, int dy) :x(dx), y(dy) {}
    	Point(const Point& point) :x(point.x), y(point.y) {}
    
    	bool operator==(const Point& point)
    	{
    		return (this->x == point.x) && (this->y == point.y);
    	}
    
    };
    
    //记录游玩信息
    struct PlayerMsg
    {
    	int id;
    	int score;
    	int len;
    	std::string r_time;	//记录时间
    
    	PlayerMsg()
    	{
    		id = 99;
    		score = 0;
    		len = 0;
    		r_time = "";
    	}
    };
    
    struct SortPlayerMsg 
    {
    	bool operator()(const PlayerMsg &msg1, const PlayerMsg &msg2)
    	{
    		if (msg1.score == msg2.score)
    		{
    			return msg1.r_time > msg2.r_time;
    		}
    		else return msg1.score > msg2.score;
    	}
    };
    

    Snake.h

    #pragma once
    
    #include "common.h"
    #include <list>
    
    class Snake
    {
    public:
    	const int MinSpeed = 1;			//蛇的最小速度
    	const int MaxSpeed = 25;		//蛇的最大速度
    	const int OrgSpeed = 15;		//蛇的原始速度
    
    private:
    	int m_len;						//蛇的长度
    	int m_speed;					//蛇的速度
    	Dir m_direction;				//蛇的方向
    	std::list<Point> m_snakelist;	//蛇的链表
    	Point m_tail;					//蛇移动过后的尾部节点,主要用于吃食物
    
    public:
    	Snake();
    
    	int getLen();					//获取长度
    	int getSpeed();					//获取速度
    	Dir getDirection();				//获取方向
    	bool setSpeed(int speed);		//设置速度,设置成功返回true
    
    	void Move();					//移动一节
    	void EatFood();					//吃食物
    	void ChangeDir(Dir dir);		//改变方向
    	void Dead();					//死亡
    
    	bool ColideWall(int left, int top, int right, int bottom);	//碰撞到墙
    	bool ColideSnake();										//碰撞到了自身
    	bool ColideFood(Point point);							//碰到了食物
    
    	void DrawSnake();				//绘制蛇
    	void DrawSnakeHead(Point pos);	//绘制蛇头
    	void DrawSnakeNode(Point pos);	//绘制蛇的身体结点
    
    	std::list<Point> GetSnakeAllNode();
    };
    

    Snake.cpp

    #include "Snake.h"
    #include <stdio.h>
    #include <ctime>
    #include <graphics.h>
    
    Snake::Snake()
    {
    	Point pos0(210, 230);
    	Point pos1(190, 230);
    	Point pos2(170, 230);
    
    	this->m_snakelist.push_back(pos0);
    	this->m_snakelist.push_back(pos1);
    	this->m_snakelist.push_back(pos2);
    
    	this->m_direction = Dir::DIR_RIGHT;
    	this->m_len = 3;
    	this->m_speed = this->OrgSpeed;
    }
    
    int Snake::getLen()
    {
    	return this->m_len;
    }
    
    int Snake::getSpeed()
    {
    	return this->m_speed;
    }
    
    Dir Snake::getDirection()
    {
    	return this->m_direction;
    }
    
    bool Snake::setSpeed(int speed)
    {
    	if (speed > this->MaxSpeed)
    	{
    		if (this->m_speed != this->MaxSpeed)
    		{
    			this->m_speed = this->MaxSpeed;
    			return true;
    		}
    		return false;
    	}
    	else if (speed < this->MinSpeed)
    	{
    		if (this->m_speed != this->MinSpeed)
    		{
    			this->m_speed = this->MinSpeed;
    			return true;
    		}
    		return false;
    	}
    	else
    	{
    		this->m_speed = speed;
    		return true;
    	}
    }
    
    void Snake::Move()
    {
    	this->m_tail = this->m_snakelist.back();
    
    	//移除最后一个节点,复制第一个节点两份
    	this->m_snakelist.pop_back();
    	Point headPos = this->m_snakelist.front();
    	this->m_snakelist.push_front(headPos);
    
    	switch (this->m_direction)
    	{
    	case Dir::DIR_UP:
    		this->m_snakelist.begin()->y -= SNAKE_WIDTH;
    		break;
    
    	case Dir::DIR_RIGHT:
    		this->m_snakelist.begin()->x += SNAKE_WIDTH;
    		break;
    
    	case Dir::DIR_DOWN:
    		this->m_snakelist.begin()->y += SNAKE_WIDTH;
    		break;
    	case Dir::DIR_LEFT:
    		this->m_snakelist.begin()->x -= SNAKE_WIDTH;
    		break;
    	}
    }
    
    void Snake::EatFood()
    {
    	//吃到食物尾部增长
    	this->m_snakelist.push_back(this->m_tail);
    
    	this->m_len += 1;
    }
    
    void Snake::ChangeDir(Dir dir)
    {
    	switch (dir)
    	{
    	case Dir::DIR_UP:
    	case Dir::DIR_DOWN:
    		if (m_direction != Dir::DIR_UP && m_direction != Dir::DIR_DOWN)
    		{
    			m_direction = dir;
    		}
    		break;
    	case Dir::DIR_RIGHT:
    	case Dir::DIR_LEFT:
    		if (m_direction != Dir::DIR_RIGHT && m_direction != Dir::DIR_LEFT)
    		{
    			m_direction = dir;
    		}
    		break;
    	}
    }
    
    void Snake::Dead()
    {
    	//TODO
    	int x = 0;
    	int y = 0;	//x、y表示坐标的该变量
    	int z = 0;	//z表示改变方向
    
    	//使用随机函数
    	srand(time(0));
    
    	std::list<Point>::iterator it = this->m_snakelist.begin();
    	for (; it != this->m_snakelist.end(); ++it)
    	{
    		x = (rand() % 4) * SNAKE_WIDTH;
    		y = (rand() % 4) * SNAKE_WIDTH;
    		z = (rand() % 8);
    
    		switch (z)
    		{
    		case 0:
    			//右
    			(*it).x += x;
    			break;
    		case 1:
    			//下
    			(*it).y += y;
    			break;
    		case 2:
    			//左
    			(*it).x -= x;
    			break;
    
    		case 3:
    			//上
    			(*it).y -= y;
    			break;
    		case 4:
    			//右下
    			(*it).x += x;
    			(*it).y += y;
    			break;
    		case 5:
    			//左下
    			(*it).x -= x;
    			(*it).y += y;
    			break;
    		case 6:
    			//左上
    			(*it).x -= x;
    			(*it).y -= y;
    			break;
    
    		case 7:
    			//右上
    			(*it).x += x;
    			(*it).y -= y;
    			break;
    		}
    	}
    }
    
    bool Snake::ColideWall(int left, int top, int right, int bottom)
    {
    	int x = this->m_snakelist.front().x;
    	int y = this->m_snakelist.front().y;
    	return (x < left || x > right || y < top || y > bottom);
    }
    
    bool Snake::ColideSnake()
    {
    
    	if (m_len <= 3) return false;
    	std::list<Point>::iterator it = this->m_snakelist.begin();
    
    	Point pos = *it;
    	Point next_pos;
    	it++;
    
    	while(it != this->m_snakelist.end())
    	{
    		next_pos = *it;
    
    		if (pos == next_pos)
    		{
    			return true;
    		}
    
    		it++;
    	}
    	return false;
    }
    
    bool Snake::ColideFood(Point point)
    {
    	if (this->m_snakelist.front() == point)
    	{
    		return true;
    	}
    	return false;
    }
    
    void Snake::DrawSnake()
    {
    	std::list<Point>::iterator it = this->m_snakelist.begin();
    	for (; it != this->m_snakelist.end(); ++it)
    	{
    		DrawSnakeNode(*it);
    	}
    	DrawSnakeHead(this->m_snakelist.front());
    }
    
    void Snake::DrawSnakeHead(Point pos)
    {
    	//紫色,全填充,无边框的正方形
    	setfillcolor(0xAA00AA);
    	setfillstyle(BS_SOLID);
    	solidrectangle(pos.x - SNAKE_RADIU, pos.y + SNAKE_RADIU, pos.x + SNAKE_RADIU, pos.y - SNAKE_RADIU);
    }
    
    void Snake::DrawSnakeNode(Point pos)
    {
    	//绿色,全填充,无边框的正方形
    	setfillcolor(GREEN);
    	setfillstyle(BS_SOLID);
    	solidrectangle(pos.x - SNAKE_RADIU, pos.y + SNAKE_RADIU, pos.x + SNAKE_RADIU, pos.y - SNAKE_RADIU);
    }
    
    std::list<Point> Snake::GetSnakeAllNode()
    {
    	return this->m_snakelist;
    }
    

    Food.h

    #pragma once
    
    #include "common.h"
    #include "Snake.h"
    
    class Food
    {
    private:
    	Point m_pos;
    	bool m_state;
    
    public:
    	Food();
    
    	bool getState();
    	void setState(bool state);
    	Point getPos();				//获取食物坐标
    
    	void Generate(Snake *snake);//产生新的食物
    
    	void DrawFood();
    };
    

    Food.cpp

    #include "Food.h"
    #include <ctime>
    #include <stdlib.h>
    #include <graphics.h>
    #include <algorithm>
    
    Food::Food()
    {
    	//初始化食物的数据
    	this->m_state = true;
    	this->m_pos = Point(310, 230);
    }
    
    bool Food::getState()
    {
    	return this->m_state;
    }
    
    void Food::setState(bool state)
    {
    	this->m_state = state;
    }
    
    Point Food::getPos()
    {
    	return this->m_pos;
    }
    
    void Food::Generate(Snake * snake)
    {
    	//产生食物要求获取蛇的身体节点来检查是否生成的食物出现在了蛇的身上
    	int x = 0;
    	int y = 0;
    	bool isOk = false;
    
    	while (true)
    	{
    		//使用随机函数产生食物
    		srand(time(0));
    		x = (rand() % 24) * 20 + 10;
    		y = (rand() % 24) * 20 + 10;
    
    		//检查是否在蛇的身上
    		isOk = 1;
    		
    		std::list<Point> pos = snake->GetSnakeAllNode();
    		std::list<Point>::iterator it = find(pos.begin(),pos.end(),Point(x,y));
    		//find()需要用到Point的重载 == 操作符
    
    
    		if (it == pos.end())//不在
    		{
    			this->m_pos = Point(x,y);//修改坐标
    			this->m_state = true;
    			return;
    		}
    	}
    }
    
    void Food::DrawFood()
    {
    	//红色,全填充,无边框的圆
    	setfillcolor(RED);
    	setfillstyle(BS_SOLID);
    	solidcircle(this->m_pos.x, this->m_pos.y, FOOD_RADIU);
    }
    

    RankList.h

    #pragma once
    
    #include "common.h"
    #include <vector>
    
    class RankList
    {
    private:
    	std::vector<PlayerMsg> m_msg;
    	const std::string m_rankfile = "retro";
    	const int MAX_RANK = 10;
    public:
    	RankList();
    
    	void SaveMsg(PlayerMsg msg);
    	std::vector<PlayerMsg> getRankList();
    	void SaveToRank();
    
    private:
    	void WriteTime(PlayerMsg &msg);
    	void ReadFile();
    	void WriteFile();
    };
    

    RankList.cpp

    #include "RankList.h"
    #include <ctime>
    #include <algorithm>
    #include <sstream>
    #include <fstream>
    #include <string>
    
    RankList::RankList()
    {
    	if (!this->m_msg.empty())
    	{
    		this->m_msg.clear();
    	}
    
    	ReadFile();
    }
    
    void RankList::SaveMsg(PlayerMsg msg)
    {
    	WriteTime(msg);		//写入时间
    
    	m_msg.push_back(msg);
    	std::sort(m_msg.begin(), m_msg.end(), SortPlayerMsg());
    
    	if (m_msg.size() > this->MAX_RANK)
    	{
    		m_msg.pop_back();
    	}
    
    	std::vector<PlayerMsg>::iterator it = m_msg.begin();
    
    	//修改id
    	for (int i = 0; i < m_msg.size(); i++, it++)
    	{
    		it->id = i + 1;
    	}
    }
    
    std::vector<PlayerMsg> RankList::getRankList()
    {
    	return this->m_msg;
    }
    
    void RankList::SaveToRank()
    {
    	WriteFile();
    }
    
    void RankList::WriteTime(PlayerMsg & msg)
    {
    	// 基于当前系统的当前日期/时间
    	time_t now = time(0);		//1970 到目前经过秒数
    
    	tm *ltm = localtime(&now);
    
    	std::stringstream sstream;
    
    	// 输出 tm 结构的各个组成部分
    	sstream << ltm->tm_year + 1900 - 2000 << "-";	//年
    	sstream << ((ltm->tm_mon + 1 >= 10) ? "" : "0") << ltm->tm_mon + 1 << "-";	//月
    	sstream << ((ltm->tm_mday >= 10) ? "" : "0") << ltm->tm_mday << "_";	//日
    	sstream << ((ltm->tm_hour >= 10) ? "" : "0") << ltm->tm_hour << ":";	//时
    	sstream << ((ltm->tm_min >= 10) ? "" : "0") << ltm->tm_min;	//分
    
    	msg.r_time = sstream.str();
    }
    
    void RankList::ReadFile()
    {
    	std::ifstream infile;
    
    	infile.open(m_rankfile, std::ios::in | std::ios::binary);
    
    	if (!infile)
    	{
    		//如果文件不存在,则创建
    		std::ofstream os;
    		os.open(m_rankfile);		//默认会创建
    
    		if (!os)
    		{
    			//如果创建失败,只能结束程序
    			exit(0);
    		}
    
    		os.close();
    	}
    	else 
    	{
    		std::string line;
    		std::stringstream stream;
    		int index = 0;
    		PlayerMsg msg;
    
    		while (std::getline(infile, line))
    		{
    			stream.clear();
    			stream.str(line);
    
    			stream >> msg.id >> msg.score >> msg.len >> msg.r_time;
    
    			m_msg.push_back(msg);
    
    			index++;
    		}
    
    		std::sort(m_msg.begin(), m_msg.end(), SortPlayerMsg());
    	}
    
    	infile.close();
    }
    
    void RankList::WriteFile()
    {
    	std::ofstream outfile;
    	outfile.open(m_rankfile, std::ios::out | std::ios::binary); //每次写文件都重新写一遍
    
    	if (!outfile)
    	{
    		return;
    	}
    
    	for (int i = 0; i < m_msg.size(); ++i)
    	{
    		outfile << m_msg[i].id << " " << m_msg[i].score << " " << m_msg[i].len
    			<< " " << m_msg[i].r_time << std::endl;
    	}
    
    	outfile.close();
    }
    

    Game.h

    #pragma once
    
    #include "common.h"
    #include "Snake.h"
    #include "Food.h"
    #include "RankList.h"
    
    class Game
    {
    private:
    	int m_GameState;			//游戏状态,0在主UI,1在游戏中,2在排行榜,3在游戏规则中
    	PlayerMsg m_msg;			//游玩数据
    	Snake *m_snake;				//蛇
    	Food *m_food;				//食物
    	RankList *m_ranklist;		//排行榜
    
    public:
    	Game();
    
    	void Init();			//初始化
    	void Run();				//控制程序
    	void Close();			//关闭程序,释放资源
    
    private:
    	void InitData();		//初始化数据
    
    	void PlayGame();		//开始游戏
    
    	void ShowMainUI();		//展示主UI
    	void ShowRank();		//排行榜展示
    	void ShowRule();		//展示规则界面
    
    	void DrawGamePlay();	//绘制初始游戏界面
    	void DrawScore();		//绘制分数
    	void DrawSnakeLen();	//绘制长度
    	void DrawSpeed();		//绘制速度
    	void DrawRunning();		//绘制正在运行
    	void DrawPause();		//绘制暂停提示
    	void DrawRebegin();		//绘制重新开始
    	void DrawGameOver();	//绘制游戏结束
    
    	void ChangeChooseUI(int left, int top, int right, int bottom, int kind);//修改选中的选项颜色
    	void ClearRegion(int left, int top, int right, int bottom);		//使用黑色清除指定区域
    };
    

    Game.cpp

    #include "Game.h"
    #include <conio.h>
    #include <graphics.h>
    
    Game::Game()
    {
    	this->m_snake = nullptr;
    	this->m_food = nullptr;
    
    	this->m_GameState = 0;
    	this->m_msg = PlayerMsg();
    	this->m_ranklist = new RankList();
    }
    
    void Game::Init()
    {
    	initgraph(640, 480);
    }
    
    void Game::Run()
    {
    	ShowMainUI();
    
    	while (true)
    	{
    		if (m_GameState == 0 && MouseHit())	//在主界面点击鼠标
    		{
    			MOUSEMSG mouse = GetMouseMsg();//获取鼠标点击消息
    			if (mouse.mkLButton)
    			{
    				if (mouse.x >= 240 && mouse.x <= 400 && mouse.y >= 195 && mouse.y <= 235)
    				{
    					//如果点击到了开始选项
    					ChangeChooseUI(240, 195, 400, 235, 1);
    					m_GameState = 1;
    
    					FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    
    					Sleep(500);
    					PlayGame();
    				}
    				else if (mouse.x >= 240 && mouse.x <= 400 && mouse.y >= 255 && mouse.y <= 295)
    				{
    					//排行榜选项
    					ChangeChooseUI(240, 255, 400, 295, 2);
    					m_GameState = 2;
    
    					FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    
    					Sleep(500);
    					ShowRank();
    				}
    				else if (mouse.x >= 240 && mouse.x <= 400 && mouse.y >= 315 && mouse.y <= 355)
    				{
    					//帮助选项
    					ChangeChooseUI(240, 315, 400, 355, 3);
    					m_GameState = 3;
    
    					FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    
    					Sleep(500);
    					ShowRule();
    				}
    				else if (mouse.x >= 240 && mouse.x <= 400 && mouse.y >= 375 && mouse.y <= 415)
    				{
    					//退出选项
    					ChangeChooseUI(240, 375, 400, 415, 4);
    					Sleep(1000);
    					return;
    				}
    			}
    		}
    
    		if ((m_GameState == 2 || m_GameState == 3) && MouseHit()) //在排行榜或者游戏帮助中点击
    		{
    			MOUSEMSG mouse = GetMouseMsg();//获取鼠标点击消息
    			if (mouse.mkLButton)
    			{
    				if (mouse.x >= 20 && mouse.x <= 63 && mouse.y >= 20 && mouse.y <= 43)
    				{
    					//点击返回选项
    					ChangeChooseUI(20, 20, 63, 43, 5);
    					Sleep(500);
    
    					FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    
    					m_GameState = 0;
    					ShowMainUI();
    				}
    			}
    		}
    	}
    }
    
    void Game::Close()
    {
    	closegraph();
    }
    
    void Game::InitData()
    {
    	if (this->m_snake != nullptr)
    	{
    		delete(this->m_snake);
    		this->m_snake = nullptr;
    	}
    	if (this->m_food != nullptr)
    	{
    		delete(this->m_food);
    		this->m_food = nullptr;
    	}
    
    	this->m_msg = PlayerMsg();
    	this->m_snake = new Snake();
    	this->m_food = new Food();
    }
    
    void Game::PlayGame()
    {
    	InitData();
    	DrawGamePlay();
    
    	BeginBatchDraw();
    
    	bool backMainUI = false;
    	bool rePlayGame = false;
    	bool changeShowData = false;
    
    	while (true)
    	{
    		changeShowData = false;
    		//食物
    		if (this->m_food->getState() == false)
    		{
    			m_food->Generate(this->m_snake);
    			m_food->DrawFood();
    		}
    
    		//按键检测
    		if (_kbhit())
    		{
    			char key = _getch();
    			switch (key)
    			{
    			case 72:  //↑
    			case 119: //w
    				m_snake->ChangeDir(Dir::DIR_UP);
    				break;
    			case 80:  //↓
    			case 115: //s
    				m_snake->ChangeDir(Dir::DIR_DOWN);
    				break;
    			case 75: //←
    			case 97: //a
    				m_snake->ChangeDir(Dir::DIR_LEFT);
    				break;
    			case 77:  //→
    			case 100: //d
    				m_snake->ChangeDir(Dir::DIR_RIGHT);
    				break;
    			case 99: //c 加速
    				changeShowData = m_snake->setSpeed(m_snake->getSpeed() + 1);
    				break;
    			case 120://x 减速
    				changeShowData = m_snake->setSpeed(m_snake->getSpeed() - 1);
    				break;
    			case 122://z 回归原速
    				changeShowData = m_snake->setSpeed(m_snake->OrgSpeed);
    				break;
    			case 32: //空格暂停
    				DrawPause();	//状态区显示
    				FlushBatchDraw();//立即批量绘制
    				while (true)
    				{
    					if (_kbhit())
    					{
    						char key = _getch();
    						if (key == 32)		//按空格继续
    						{
    							ClearRegion(505, 240, 640, 480);
    							DrawRunning();	//绘制程序正在运行中
    							FlushBatchDraw();//立即批量绘制
    							break;
    						}
    						else if (key == 27) //esc键 退出
    						{
    							ChangeChooseUI(510, 347, 588, 373, 6);
    							FlushBatchDraw();//立即批量绘制
    							Sleep(500);
    
    							backMainUI = true;
    							break;
    						}
    					}
    
    					if (MouseHit())
    					{
    						MOUSEMSG mouse = GetMouseMsg();//获取鼠标点击消息
    						if (mouse.mkLButton)
    						{
    							if (mouse.x >= 510 && mouse.x <= 588 && mouse.y >= 347 && mouse.y <= 373)
    							{
    								//点击返回选项
    								ChangeChooseUI(510, 347, 588, 373, 6);
    								FlushBatchDraw();//立即批量绘制
    								Sleep(500);
    
    								backMainUI = true;
    								break;
    							}
    						}
    					}
    				}
    				break;
    			}
    
    			//如果要回到主UI
    			if (backMainUI)
    			{
    				FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    				break;		//跳出主循环
    			}
    		}
    
    		//更新显示数据
    		if (changeShowData)
    		{
    			ClearRegion(505, 115, 640, 200);
    			DrawScore();
    			DrawSnakeLen();
    			DrawSpeed();
    		}
    
    		//移动
    		m_snake->Move();
    
    		//吃食物
    		if (m_snake->ColideFood(m_food->getPos()))
    		{
    			m_snake->EatFood();
    
    			this->m_food->setState(false);
    
    			//分数增加,长度增加
    			this->m_msg.score += 10;
    
    			//更新数据
    			ClearRegion(505, 115, 640, 200);
    			DrawScore();
    			DrawSnakeLen();
    			DrawSpeed();
    		}
    
    		//碰撞检测
    		if (m_snake->ColideWall(10, 10, 470, 470) || m_snake->ColideSnake())
    		{
    			////游戏结束
    			m_snake->Dead();
    
    			//清空游戏区,重绘死去的蛇
    			ClearRegion(0, 0, 479, 480);
    
    			m_snake->DrawSnake();
    
    			//绘制重新开始
    			DrawRebegin();
    
    			//绘制GameOVer
    			DrawGameOver();
    
    			FlushBatchDraw();//批量绘制
    
    			while (true)
    			{
    				if (_kbhit())
    				{
    					char key = _getch();
    					if (key == 32)		//按空格继续
    					{
    						//点击再来一局选项
    						ChangeChooseUI(510, 397, 588, 423, 7);
    						FlushBatchDraw();//立即批量绘制
    						Sleep(500);
    
    						rePlayGame = true;
    						break;
    					}
    					else if (key == 27) //esc键 退出
    					{
    						ChangeChooseUI(510, 347, 588, 373, 6);
    						FlushBatchDraw();//立即批量绘制
    						Sleep(500);
    
    						backMainUI = true;
    						break;
    					}
    				}
    
    				if (MouseHit())
    				{
    					MOUSEMSG mouse = GetMouseMsg();//获取鼠标点击消息
    					if (mouse.mkLButton)
    					{
    						if (mouse.x >= 510 && mouse.x <= 588 && mouse.y >= 347 && mouse.y <= 373)
    						{
    							//点击返回选项
    							ChangeChooseUI(510, 347, 588, 373, 6);
    							FlushBatchDraw();//立即批量绘制
    							Sleep(500);
    
    							backMainUI = true;
    							break;
    						}
    						else if (mouse.x >= 510 && mouse.x <= 588 && mouse.y >= 397 && mouse.y <= 423)
    						{
    
    							//点击再来一局选项
    							ChangeChooseUI(510, 397, 588, 423, 7);
    							FlushBatchDraw();//立即批量绘制
    							Sleep(500);
    
    							rePlayGame = true;
    							break;
    						}
    					}
    				}
    			}
    
    			//如果要回到主UI
    			if (backMainUI)
    			{
    				FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    				break;		//跳出主循环
    			}
    
    			//如果要重新开始游戏
    			if (rePlayGame)
    			{
    				FlushMouseMsgBuffer();//清空鼠标消息缓冲区。
    				break;		//跳出主循环
    			}
    		}
    
    		//清空屏幕
    		setbkcolor(BG_COLOR);//设置背景色
    		ClearRegion(0, 0, 479, 480);
    
    		this->m_snake->DrawSnake();
    
    		if (this->m_food->getState() == true)
    		{
    			m_food->DrawFood();
    		}
    
    		FlushBatchDraw();//立即批量绘制
    
    		int sleep = 30 - m_snake->getSpeed();
    		Sleep(sleep * 10);
    	}
    
    	EndBatchDraw();//结束批量绘制
    
    	//如果要回到主UI
    	if (backMainUI)
    	{
    		m_msg.len = m_snake->getLen();
    
    		m_ranklist->SaveMsg(m_msg);
    		m_ranklist->SaveToRank();
    
    		m_GameState = 0;
    		ShowMainUI();
    	}
    
    	//如果要重新开始游戏
    	if (rePlayGame)
    	{
    		m_msg.len = m_snake->getLen();
    
    		m_ranklist->SaveMsg(m_msg);
    		m_ranklist->SaveToRank();
    
    		PlayGame();
    	}
    
    }
    
    void Game::ShowMainUI()
    {
    	//清空屏幕
    	setbkcolor(BG_COLOR);//设置背景色
    	cleardevice();
    
    	/*--------------绘制标题----------*/
    	settextstyle(80, 0, L"微软雅黑");
    	settextcolor(0XFFFFFF);
    	outtextxy(230, 60, L"贪吃蛇");
    
    	settextstyle(18, 0, L"微软雅黑");
    	outtextxy(380, 140, L"By Colourso");
    
    	settextstyle(20, 0, L"微软雅黑");
    	settextcolor(0XFF5555);
    	outtextxy(365, 160, L"(www.colourso.top)");
    	settextcolor(0XFFFFFF);
    
    	/*--------------绘制选项----------*/
    
    	setlinecolor(0xFF00FF);
    	rectangle(240, 195, 400, 235);
    	rectangle(240, 255, 400, 295);
    	rectangle(240, 315, 400, 355);
    	rectangle(240, 375, 400, 415);
    
    
    	settextstyle(28, 0, L"微软雅黑");
    	settextcolor(0x55FFFF);//黄色
    
    	outtextxy(297, 200, L"开始");
    	outtextxy(287, 260, L"排行榜");
    	outtextxy(297, 320, L"帮助");
    	outtextxy(297, 380, L"退出");
    
    	settextstyle(24, 0, L"微软雅黑");
    	settextcolor(0xAAAAAA);
    	outtextxy(255, 450, L"(鼠标点击选项)");
    }
    
    void CharToTCHAR(const char * _char, TCHAR * tchar)
    {
    	int iLength;
    	iLength = MultiByteToWideChar(CP_UTF8, 0, _char, strlen(_char) + 1, NULL, 0);
    	MultiByteToWideChar(CP_UTF8, 0, _char, strlen(_char) + 1, tchar, iLength);
    
    }
    
    void Game::ShowRank()
    {
    	//清空屏幕
    	setbkcolor(BG_COLOR);//设置背景色
    	cleardevice();
    
    	/*--------------绘制规则----------*/
    	settextstyle(60, 0, L"微软雅黑");
    	settextcolor(0XFFFFFF);
    	outtextxy(160, 40, L"贪吃蛇排行榜");
    
    	/*--------------绘制返回键----------*/
    	setlinecolor(0xFFFFFF);
    	rectangle(20, 20, 63, 43);
    
    	settextcolor(0XFFFFFF);
    	settextstyle(20, 0, L"宋体");
    	outtextxy(22, 22, L" ← ");
    
    	/*---------------绘制排行榜信息---------------*/
    	settextcolor(0XFFFFFF);
    	settextstyle(20, 0, L"宋体");
    	outtextxy(140, 120, L"排名");
    	outtextxy(240, 120, L"分数");
    	outtextxy(340, 120, L"长度");
    	outtextxy(490, 120, L"日期");
    
    	std::vector<PlayerMsg> msg = m_ranklist->getRankList();
    	if (msg.size() == 0)
    	{
    		settextcolor(0x5555FF);
    		settextstyle(40, 0, L"宋体");
    		outtextxy(230, 200, L"暂无排名");
    	}
    	for (int i = 0; i < msg.size(); ++i)
    	{
    		if (i == 0)
    		{
    			settextcolor(0x5555FF);
    			settextstyle(15, 0, L"宋体");
    		}
    		else
    		{
    			settextcolor(0XFFFFFF);
    			settextstyle(15, 0, L"宋体");
    		}
    		TCHAR t[40];
    		_stprintf_s(t, _T("%d"), msg[i].id);
    		outtextxy(145, 150 + 30 * i, t);
    
    		_stprintf_s(t, _T("%d"), msg[i].score);
    		outtextxy(245, 150 + 30 * i, t);
    
    		_stprintf_s(t, _T("%d"), msg[i].len);
    		outtextxy(345, 150 + 30 * i, t);
    
    		//_stprintf_s(t, _T("%s"), msg[i].r_time.c_str());
    		CharToTCHAR(msg[i].r_time.c_str(), t);
    		outtextxy(450, 150 + 30 * i, t);
    
    		settextcolor(0XFFFFFF);
    	}
    }
    
    void Game::ShowRule()
    {
    	//清空屏幕
    	setbkcolor(BG_COLOR);//设置背景色黑色
    	cleardevice();
    
    	/*--------------绘制规则----------*/
    	settextstyle(60, 0, L"微软雅黑");
    	settextcolor(0XFFFFFF);
    	outtextxy(160, 40, L"贪吃蛇按键介绍");
    
    	settextcolor(0XFFFFFF);
    	settextstyle(20, 0, L"宋体");
    	outtextxy(230, 150, L"↑ ← ↓ → 控制方向");
    	outtextxy(230, 180, L"w  a  s  d  控制方向");
    	outtextxy(230, 210, L"速度等级1-25,默认12");
    	outtextxy(240, 240, L"c键加速,x键减速");
    	outtextxy(240, 270, L"z键恢复原始速度");
    	outtextxy(240, 300, L"空格键暂停/继续");
    
    	outtextxy(180, 350, L"请将输入法调至英文输入法状态下");
    
    	/*--------------绘制返回键----------*/
    
    	setlinecolor(0xFFFFFF);
    	rectangle(20, 20, 63, 43);
    
    	settextcolor(0XFFFFFF);
    	settextstyle(20, 0, L"宋体");
    	outtextxy(22, 22, L" ← ");
    }
    
    void Game::DrawGamePlay()
    {
    	//清空屏幕
    	setbkcolor(BG_COLOR);//设置背景色
    	cleardevice();
    
    	//画宽度为2的棕色实线,分割游戏区
    	setlinecolor(BROWN);
    	setlinestyle(PS_SOLID, 2);
    	line(482, 0, 482, 480);
    
    	//画蛇和食物
    	this->m_snake->DrawSnake();
    
    	//绘制分数
    	DrawScore();
    
    	//绘制蛇身长度
    	DrawSnakeLen();
    
    	//绘制速度
    	DrawSpeed();
    
    	//绘制游戏状态
    	DrawRunning();
    }
    
    void Game::DrawScore()
    {
    	TCHAR s[] = _T("获得分数:");
    	settextstyle(16, 0, _T("宋体"));
    	settextcolor(0xFFFFFF);//白
    	outtextxy(510, 120, s);
    
    	settextcolor(0xFF5555);//亮蓝
    	TCHAR t[5];
    	_stprintf_s(t, _T("%d"), m_msg.score); // 高版本 VC 推荐使用 _stprintf_s 函数
    	outtextxy(590, 120, t);
    	settextcolor(0xFFFFFF);//白
    }
    
    void Game::DrawSnakeLen()
    {
    	settextstyle(16, 0, L"宋体");
    	outtextxy(510, 150, L"蛇身长度:");
    
    	settextcolor(0xFF55FF);//亮紫
    	TCHAR t[5];
    	_stprintf_s(t, _T("%d"), m_snake->getLen()); // 高版本 VC 推荐使用 _stprintf_s 函数
    	outtextxy(590, 150, t);
    	settextcolor(0xFFFFFF);//白
    }
    
    void Game::DrawSpeed()
    {
    	TCHAR s[] = _T("当前速度:");
    	settextstyle(16, 0, _T("宋体"));
    	outtextxy(510, 180, s);
    
    	int speed = m_snake->getSpeed();//速度等级显示为1 - 25
    	if (speed <= 9)
    	{
    		settextcolor(0x00AA00);//禄
    	}
    	else if (speed >= 18)
    	{
    		settextcolor(0x0000AA);//红
    	}
    	else
    	{
    		settextcolor(0x55FFFF);//黄
    	}
    	TCHAR t[5];
    	_stprintf_s(t, _T("%d"), speed); // 高版本 VC 推荐使用 _stprintf_s 函数
    	outtextxy(590, 180, t);
    	settextcolor(0xFFFFFF);//白
    }
    
    void Game::DrawRunning()
    {
    	settextcolor(0x55FF55);//亮绿
    	settextstyle(16, 0, L"宋体");
    	outtextxy(510, 250, L"游戏 运行");
    	outtextxy(510, 280, L"祝玩的开心");
    	settextcolor(0xFFFFFF);//白
    }
    
    void Game::DrawPause()
    {
    	settextcolor(0xFF55FF);//亮紫
    	settextstyle(16, 0, L"宋体");
    	outtextxy(510, 250, L"游戏 暂停");
    	outtextxy(510, 280, L"空格键继续");
    	outtextxy(510, 310, L"Esc键退出");
    	settextcolor(0xFFFFFF);//白
    
    	setlinecolor(0xFFFFFF);
    	rectangle(510, 347, 588, 373);
    
    	settextcolor(0XFFFFFF);
    	settextstyle(16, 0, L"宋体");
    	outtextxy(517, 352, L"退出游戏");
    }
    
    void Game::DrawRebegin()
    {
    	settextcolor(0x5555FF);//亮红
    	settextstyle(16, 0, L"宋体");
    	outtextxy(510, 250, L"游戏 结束");
    	outtextxy(510, 280, L"空格键再来一局");
    	outtextxy(510, 310, L"Esc键退出");
    	settextcolor(0xFFFFFF);//白
    
    	setlinecolor(0xFFFFFF);
    	rectangle(510, 347, 588, 373);
    	rectangle(510, 397, 588, 423);
    
    	settextcolor(0XFFFFFF);
    	settextstyle(16, 0, L"宋体");
    	outtextxy(517, 352, L"退出游戏");
    	outtextxy(517, 402, L"再来一局");
    }
    
    void Game::DrawGameOver()
    {
    	settextcolor(0xFFFFFF);//亮红
    	settextstyle(48, 0, L"宋体");
    	outtextxy(170, 210, L"GAMEOVER");
    }
    
    void Game::ChangeChooseUI(int left, int top, int right, int bottom, int kind)
    {
    	setfillcolor(0XFFFFFF);				//使用白色填充这一区域
    	fillrectangle(left, top, right, bottom);
    
    	setlinecolor(0x55FF55);				//画线边框设置为亮绿色
    	rectangle(left, top, right, bottom);
    
    	settextstyle(28, 0, L"微软雅黑");
    	settextcolor(0xFF5555);//亮蓝色
    
    	setbkcolor(0XFFFFFF);				//设置背景色为白色,文字的背景色就会变成白色
    	switch (kind)
    	{
    	case 1:
    		outtextxy(297, 200, L"开始");
    		break;
    	case 2:
    		outtextxy(287, 260, L"排行榜");
    		break;
    	case 3:
    		outtextxy(297, 320, L"帮助");
    		break;
    	case 4:
    		outtextxy(297, 380, L"退出");
    		break;
    	case 5:
    		settextcolor(0);
    		settextstyle(20, 0, L"宋体");
    		outtextxy(22, 22, L" ← ");
    		break;
    	case 6:
    		settextcolor(0);
    		settextstyle(16, 0, L"宋体");
    		outtextxy(517, 352, L"退出游戏");
    		break;
    	case 7:
    		settextcolor(0);
    		settextstyle(16, 0, L"宋体");
    		outtextxy(517, 402, L"再来一局");
    		break;
    	}
    	setbkcolor(BG_COLOR);	//恢复背景色
    }
    
    void Game::ClearRegion(int left, int top, int right, int bottom)
    {
    	//黑色,全填充,无边框的矩形
    	setfillcolor(BG_COLOR);
    	setfillstyle(BS_SOLID);
    	solidrectangle(left, top, right, bottom);
    }
    

    main.cpp

    #include "Snake.h"
    #include "Game.h"
    
    int main()
    {
    	Game game;
    	game.Init();
    	game.Run();
    	game.Close();
    
    	return 0;
    }
    

    程序展示

    以下是B站视频

    上面视频不能播放请移步:https://www.bilibili.com/video/BV1fZ4y1T7xo/

    资源下载

    百度链接:https://pan.baidu.com/s/1mUupX0KVOY7W3W2G_OyjmQ
    提取码:p7qi

    Github地址:https://github.com/Colourso/Simple-CPP-project-by-Colourso/

  • 相关阅读:
    jsp学习之——关于请求转发和重定向的形象理解
    java多线程学习之——多线程中几种释放锁和不释放锁的操作
    DBUtils学习之——使用ResultSetHandler接口的各个实现类实现数据库的增删改查
    java网络编程学习之——构建基于多线程的网络通信模型1
    web后端学习过程中技巧总结(持续更新。。。)
    关于表单form元素中onsubmit事件处理机制的认识
    Java小案例——使用双重for循环实现杨辉三角的输出
    Android工具大杂烩
    基于上一篇AS项目依赖库问题的优化解决方案
    Gradle脚本打包AndroidStudio依赖库的问题
  • 原文地址:https://www.cnblogs.com/colourso/p/13438729.html
Copyright © 2020-2023  润新知