1. 程式人生 > >C++練習例項————貪吃蛇(OO思想)

C++練習例項————貪吃蛇(OO思想)

這是一款在vs上直接控制檯輸出的貪吃蛇遊戲,沒有使用任何圖形庫。遊戲具有多個介面,狀態轉換如下:

下面直接上程式碼:

Position類,用來記錄遊戲中的座標位置:

#include<iostream>
using namespace std;

#ifndef POSITION_H
#define POSITION_H

class Position
{
public:
	Position(int x = 0, int y = 0) : m_x(x), m_y(y) {};
	~Position() {};
	Position& operator=(const Position &p)
	{
		m_x = p.m_x;
		m_y = p.m_y;
		return *this;
	}
	bool operator==(const Position &p)const {
		if (m_x == p.m_x&&m_y == p.m_y)
			return true;
		else return false;
	}
	void Set(const int x, const int y) { m_x = x; m_y = y; }
	void SetX(const int x) { m_x = x; }
	void SetY(const int y) { m_y = y; }
	int GetX() const { return m_x; }
	int GetY()const { return m_y; }

private:
	int m_x;
	int m_y;
};
#endif

GameObject類,遊戲中所有物件都要繼承的基類,其中HANDLE為控制代碼:

#include "Position.h"
#include <Windows.h>

#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H

class GameObject 
{
protected:
	Position m_firstpos;
public:
	GameObject() {}
	virtual ~GameObject() {}

	virtual void Display(HANDLE) = 0;
	virtual void Clear(HANDLE) = 0;
};
#endif // GAMEOBJECT_H

Field類,用於遊戲邊框的輸出和處理:

#include "GameObject.h"

#ifndef FIELD_H
#define FIELD_H

class Field:GameObject
{
public:
	Field(int x,int y){m_firstpos.Set(x, y);}
	~Field() {}

	void Display(HANDLE handle);
    void Clear(HANDLE){}
	Position GetFirstPos()const { return m_firstpos; }

	static int GetWidth();
	static int GetHeight();
};
#endif
#include "Field.h"

void Field::Display(HANDLE handle)
{
	{
		COORD coord;
		coord.X = m_firstpos.GetX();
		coord.Y = m_firstpos.GetY();
		for (int r = 0; r < Field::GetHeight()+1; ++r)
		{
			coord.X = m_firstpos.GetX();
			SetConsoleCursorPosition(handle, coord);
			cout << "#";
			coord.X = Field::GetWidth() + m_firstpos.GetX();
			SetConsoleCursorPosition(handle, coord);
			cout << "#";
			coord.Y++;
		}
		coord.X = m_firstpos.GetX();
		coord.Y = m_firstpos.GetY();
		for (int r = 0; r < Field::GetWidth()+1; ++r)
		{
			coord.Y = m_firstpos.GetY();
			SetConsoleCursorPosition(handle, coord);
			cout << "#";
			coord.Y = Field::GetHeight() + m_firstpos.GetY();
			SetConsoleCursorPosition(handle, coord);
			cout << "#";
			coord.X++;
		}
	}
}

int Field::GetWidth()
{
	static const int width = 75;
	return width;
}

int Field::GetHeight()
{
	static const int height =20;
	return height;
}

Food類,遊戲中食物的顯示和管理:

#include "GameObject.h"
#include "Field.h"

#ifndef FOOD_H
#define FOOD_H

class Food:GameObject
{
private:
	char m_symbol;

public:
	Food(int x,int y) : m_symbol('$')
	{
		m_firstpos.Set(x,y);
	}

	void Setpos(int x, int y){m_firstpos.Set(x, y);}
	void Reposition(int leftupx,int leftupy) 
	{
		m_firstpos.Set( 
			(rand() % (Field::GetWidth()-leftupx-2)+leftupx+1),
			(rand() %( Field::GetHeight()-leftupy-2)+leftupy+1));
	}

	Position GetPos() const { return m_firstpos; }

	void Display(HANDLE handle)
	{
		COORD coord;
		coord.X = m_firstpos.GetX();
		coord.Y = m_firstpos.GetY();
		SetConsoleCursorPosition(handle, coord);
		cout << m_symbol;
	}
	void Clear(HANDLE handle)
	{
		COORD coord;
		coord.X = m_firstpos.GetX();
		coord.Y = m_firstpos.GetY();
		SetConsoleCursorPosition(handle, coord);
		cout << " ";
	}
};
#endif

Snake類,用於蛇體的顯示和玩法規則的表現:

#include <conio.h>
#include <vector>
#include "GameObject.h"
#include "Food.h"

#ifndef SNAKE_H
#define SNAKE_H

class Snake :GameObject
{
private:

	enum { UP, DOWN, LEFT, RIGHT } m_dir;
	vector<Position> m_snakepos;
	Position * m_headpos;
	char m_symbol, m_headsymbol;
	int m_speed;
	bool m_canturn;

public:

	Snake(int x, int y) :
		m_symbol('X'), m_headsymbol('@'), m_snakepos(),
		m_speed(1), m_dir(RIGHT),
		m_headpos(nullptr), m_canturn(true)
	{
		m_snakepos.push_back(Position(x, y));
		m_snakepos.push_back(Position(x - 1, y));
		m_snakepos.push_back(Position(x - 2, y));
		m_headpos = &m_snakepos[0];
	}
	~Snake()
	{
		if(!m_headpos)
		delete m_headpos;
	}

	void GetInput();
	Position Move();

	bool CheckFood(const Food & food)
	{
		if (food.GetPos() == *m_headpos)
		{
			m_snakepos.push_back(m_snakepos[m_snakepos.size() - 1]);
			return true;
		}
		return false;
	}
	bool CheckFoodInBody(Position food) 
	{
		for each (Position var in m_snakepos)
		{
			if (var == food)return true;
		}
		return false;
	}
    bool CheckHeadInBody()
	{
		int num(0);
		for each (Position var in m_snakepos)
		{
			if (var == *m_headpos)num++;
			if (num == 2)return true;
		}
		return false;
	}
	bool CheckHeadInWall(int leftupx, int leftupy)
	{
			if (m_headpos->GetX()==Field::GetWidth()+leftupx||
				m_headpos->GetX()== leftupx||
				m_headpos->GetY() == Field::GetHeight() + leftupy||
				m_headpos->GetY() == leftupy )
				return true;
			else return false;
	}

	void Display(HANDLE handle);
	void Clear(HANDLE){}
};

#endif
#include "Snake.h"

void Snake::GetInput()
{
	if (_kbhit())//得到鍵盤相應
	{
		int key = _getch();
		if (key == 72 && m_dir != DOWN)
			m_dir = UP;
		if (key == 80 && m_dir != UP)
			m_dir = DOWN;
		if (key == 75 && m_dir != RIGHT)
			m_dir = LEFT;
		if (key == 77 && m_dir != LEFT)
			m_dir = RIGHT;
}
}

Position Snake::Move()
{
	Position next = { 0, 0 };
	switch (m_dir) {
	case UP:
		next = Position(m_headpos->GetX(), m_headpos->GetY() - 1);
		break;
	case DOWN:
		next = Position(m_headpos->GetX(), m_headpos->GetY() + 1);
		break;
	case LEFT:
		next = Position(m_headpos->GetX() - 1, m_headpos->GetY());
		break;
	case RIGHT:
		next = Position(m_headpos->GetX() + 1, m_headpos->GetY());
	}
	m_snakepos.insert(m_snakepos.begin(), next);
	Position temp = m_snakepos[m_snakepos.size() - 1];
	m_snakepos.pop_back();

	m_headpos = &m_snakepos[0];

	return temp;
}

void Snake::Display(HANDLE handle)
{
	Position temppos = Move();
	COORD coord;
	coord.X = temppos.GetX(); coord.Y = temppos.GetY();
	SetConsoleCursorPosition(handle, coord);
	cout << " ";//消除最後一個節點

	for (int i = 0; i < m_snakepos.size(); i++)
	{
		coord.X = m_snakepos[i].GetX();
		coord.Y = m_snakepos[i].GetY();
		SetConsoleCursorPosition(handle, coord);
		cout << m_symbol;
	}
	coord.X = m_snakepos[0].GetX();
	coord.Y = m_snakepos[0].GetY();
	SetConsoleCursorPosition(handle, coord);
	std::cout << m_headsymbol;//輸出頭節點
}

DataManager類,用於管理遊戲資料並記錄遊戲最高分(檔案操作),採用單例模式:

#include<iostream>
#include<fstream>
#include <string>

using namespace std;

#ifndef DATAMANAGER_H
#define DATAMANAGER_H

class Datamanager
{
private:
	int m_curscore;
	int m_highscore;
	bool m_isend;
public:
	Datamanager(int curscore):
	m_curscore(curscore),m_isend(false)
	{}
	~Datamanager(){	}

	static	Datamanager*Instance();//單例模式

	void AddCurScore() {m_curscore++;}
	int GetCurScore(){ return m_curscore; }
	bool IsEnd() { return m_isend; }
	void SetEnd(bool end) { m_isend = end; }
	int GetHighScore(){return m_highscore;}
	string GetHighFromFile();
	void StoreHighScore(string);
	void ReStart()
	{
		m_curscore = 0;
		m_highscore = 0;
	    m_isend = false;
	}
};
#endif
#include "DataManager.h"

string Datamanager::GetHighFromFile()
{
	ifstream fin("data.txt");
	string name;
	if (!fin)
	{
		ofstream fout("data.txt");//若沒有該檔案,建立一個
		//return NULL;
	}
	do {
		fin >> name;
		fin >> m_highscore;
		if (fin.fail())//如果檔案為空
		{
			//cout << "Data error!" << endl;
			return "*the file is empty";
		}

	} while (!fin.eof());
	fin.close();
	return name;
}
void Datamanager::StoreHighScore(string name)
{
	ofstream fout("data.txt");
	if (!fout)
	{
		cerr << " file error!" << endl;
		return;
	}
	fout << name << endl;
	fout << m_curscore;

	fout.close();
}
Datamanager* Datamanager::Instance()
{
	static Datamanager instance(0);
	return &instance;
}

最後是main函式,主要用於遊戲更個介面的切換:

#include <iostream>
#include <time.h>
#include "Snake.h"
#include "DataManager.h"
#include <string>
#define MYDATA Datamanager::Instance()

using namespace std;

static HANDLE handle;  //控制檯控制代碼

static CONSOLE_SCREEN_BUFFER_INFO info; //控制檯資訊結構

enum OutPutState
{
	MENU,
	PLAYING,
	END,
	INSCRUCTION
};

int main() {
	//field.clear();
	handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//獲取相關資訊(主要是緩衝區大小)
	GetConsoleScreenBufferInfo(handle, &info);
	CONSOLE_CURSOR_INFO cursor;           //游標結構 
	cursor.dwSize = 10;
	cursor.bVisible = 0;                  //0為隱藏游標
	SetConsoleCursorInfo(handle, &cursor);//設定隱藏游標函式 

	srand((unsigned)time(0));

	OutPutState m_state = MENU;
	while (1)
	{
		if (m_state == MENU)
		{
			cout << endl; cout << endl; cout << endl;
			cout << "                         |WELECOME   TO   SNAKE|            " << endl;
			cout << "        *****************************************************" << endl;
			cout << "        *                                                   *" << endl;
			cout << "        *                press (1) to start                 *" << endl;
			cout << "        *                press (2) for inscruction          *" << endl;
			cout << "        *                press (3) to quit                  *" << endl;
			cout << "        *                                                   *" << endl;
			cout << "        *****************************************************" << endl;
			int c(0);
			cin >> c;
			if (c == 1)m_state = PLAYING;
			if (c == 2)m_state = INSCRUCTION;
			if (c == 3)exit(0);
			system("cls");
		}

		if (m_state == PLAYING)
		{
			Food food(35, 15);
			Snake snake(20, 10);
			Field field(10, 5);
			MYDATA->ReStart();
			while (1)
			{
				snake.GetInput();

				if (snake.CheckFood(food))
				{
					MYDATA->AddCurScore();
					food.Clear(handle);
					do
					{
						food.Reposition(field.GetFirstPos().GetX(), field.GetFirstPos().GetY());
					} while (snake.CheckFoodInBody(food.GetPos()));//如果刷新出的新食物在蛇的身體上,重新重新整理
				}
				if (snake.CheckHeadInBody() ||
					snake.CheckHeadInWall(field.GetFirstPos().GetX(), field.GetFirstPos().GetY()))
					MYDATA->SetEnd(true);

				if (MYDATA->IsEnd())
				{
					m_state = END;
					break;
				};

				snake.Display(handle);
				field.Display(handle);
				food.Display(handle);

				Sleep(250);
			}
			system("cls");
		}

		if (m_state == END)
		{
			string highname = MYDATA->GetHighFromFile();
			if (highname=="*the file is empty"|| MYDATA->GetCurScore() > MYDATA->GetHighScore())
			{
				cout << endl; cout << endl; cout << endl;
				cout << "                            GAME    OVER                " << endl;
				cout << "        *****************************************************" << endl;
				cout << "        *                                                   *" << endl;
				cout << "        *               You broke the record !!!            *" << endl;
				cout << "        *                 your score: " << MYDATA->GetCurScore()
					 << "                     *" << endl;
				cout << "        *                                                   *" << endl;
				cout << "        *                                                   *" << endl;
				cout << "        *****************************************************" << endl;
				cout << "        please write your name:";
				string name(" ");
				cin >> name;
				MYDATA->StoreHighScore(name);
				cout << "        *****************************************************" << endl;
				cout << "        *                press (r) to play again            *" << endl;
				cout << "        *                press (e) to menu                  *" << endl;
				cout << "        *****************************************************" << endl;
			}
			else
			{
				cout << endl; cout << endl; cout << endl;
				cout << "                            GAME    OVER                " << endl;
				cout << "        *****************************************************" << endl;
				cout << "        *                                                   *" << endl;
				cout << "                  The record holder is :" << highname << " "
					 << MYDATA->GetHighScore() << endl;
				cout << "        *                 your score: " << MYDATA->GetCurScore()
					 << "                     *" << endl;
				cout << "        *                press (r) to play again            *" << endl;
				cout << "        *                press (e) to menu                  *" << endl;
				cout << "        *****************************************************" << endl;
			}
			char c(' ');
			cin >> c;
			if (c == 'r')m_state = PLAYING;
			if (c == 'e')m_state = MENU;
			system("cls");
		}

		if (m_state == INSCRUCTION)
		{
			cout << endl; cout << endl; cout << endl;
			cout << "        ******************************INSTRUCTION****************************" << endl;
			cout << "        *       1.Use keyboard to control the direction of the snake        *" << endl;
			cout << "        *       2.Eat Fruit to make your snake longer                       *" << endl;
			cout << "        *       3.Avoid hitting the boarder or yourself                     *" << endl;
			cout << "        *       4.One fruit will give you 1 point as score                  *" << endl;
			cout << "        *                        | press 's' to start |                     *" << endl;
			cout << "        *********************************************************************" << endl;
			char c(' ');
			cin >> c;
			if (c == 's')m_state = PLAYING;
			system("cls");
		}

	}
	return 0;
}

遊戲畫面截圖:

蟹蟹觀看:)