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为句柄:
(关于句柄可访问:https://www.cnblogs.com/yymn/p/7400443.html
http://www.cppblog.com/mymsdn/archive/2009/02/19/handle-in-windows.html)
#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;
}
游戏画面截图:
蟹蟹观看:)