今天,我们开始用面向对象的方法编写一个简易的控制台版的贪吃蛇。本人能力有限,若有纰漏还请及时指出,多多包涵。
在编写程序之前,我们得有一个较为清晰的思路,即:如何才能编出这个游戏。我想,可以遵循以下几条来明确以下思路。
1.想思路:首先你得知道贪吃蛇是怎么个玩法。毕竟,若游戏规则都不知道的话,想编出来也不切合实际。
2.找出技术点
(1)通过什么技术能将地图、蛇什么的显示出来
(2)蛇需要人来控制,那么控制蛇需要什么方式来实现?其实就是不断地绘制和检测状态(捕获按键)的过程。
3.构建关系(功能):蛇吃食物会变长,碰到墙壁会死去。
(1)由此可以想到应该构建这么几个类(class)——食物类、地图类、蛇类。同时,你需要一个loop循环,如果不符合条件了(比如说:蛇死了),就跳出循环。
(2)具体用什么方式来存储食物、地图还有蛇呢?你可以用vector去存储每一块。把食物,地图还有蛇统统压入vector容器当中,同时可以想办法将这三种不同的东西打上标记(TYPE_)区分开来。然后遍历整个容器,通过不同的标记绘制不同的图案,用这种方法即可实现在东西再控制台上的显示。
(3)Loop循环:每一次循环,你都需要遍历容器,检测食物、蛇还有地图的坐标。为什么这样做?因为你需要用这种方式检测蛇头和另外两个东西是否重合,坐标相同了,就重合了。若蛇头的坐标和墙的坐标相同了,说明撞墙了,此时Game Over;若蛇头的坐标和食物的坐标重合了,说明吃到食物了,此时再额外生成食物,同时蛇身增加。
(4)如何控制蛇的移动?你可以把这个类写进蛇类里面,但是这样工作量更大,你可以单独写一个控制类,专门控制蛇的移动操作。
4.逐个实现。
5.关联调试。
我们已经有了以上的这些思路,那么我们就开始一一实现吧。但是一一实现的过程也需要一些具体的逻辑,这些逻辑主要是体现在:先做什么,后做什么,该怎么解决这些问题!
一、首先得把框架搭建出来,最起码能够在控制台上显示出来蛇、地图和食物。
根据以上的思路,我们需要蛇类(Snake)、地图类(Map),食物类(Food)。我们采取这样的一种思路:用C++的vector容器来存储各个“结点”,如果这个结点存储的是地图类的点,我们就用“*”表示;如果是蛇类的点,我们就用“o”表示;如果是食物类,我们就用“$”表示。
不过,这个“结点”并不是一维的,毕竟控制台上显示的东西都是二维的,有x值和y值。所以,我们还得额外定义一个结点类(baseNode),专门用来定义“结点”。
为了方便显示以及其它的一些操作,我们专门定义一个控制类(Controller),这个类所起的作用是对整个游戏控制的一个“调配”。
所以,具体实现代码的时候,你最先应该实现的是哪个类呢?显然是结点类,因为如果没有具体的结点的话,不管地图、蛇还是食物的话你都无从定义(毕竟蛇、地图和食物是在二维空间里)。其次是控制类,没有控制类的话,你就无法实现对蛇、地图等进行显示等操作。这两个类写好之后,蛇类、地图类和食物类将会比较容易实现。
最开始实现的是结点类:
/*
baseNode.h
*/
#pragma once
enum nodeType //定义了结点的类型
{
TYPE_SNAKE, //蛇类
TYPE_MAP, //地图类
TYPE_FOOD //食物类
};
class BaseNode
{
public:
BaseNode();
BaseNode(int x, int y, nodeType type);
~BaseNode();
int x;
int y;
nodeType type;
};相应的,baseNode.h里具体函数的实现过程如下:
//baseNode.cpp
#include "baseNode.h"
BaseNode::BaseNode()
{
}
BaseNode::BaseNode(int x, int y, nodeType type)
{
this->x = x;
this->y = y;
this->type = type;
}
BaseNode::~BaseNode()
{
}
baseNode类实现之后,开始写控制类,目前的情况下,我们只需只需实现两个功能:能够将结点显示在控制台上,以及捕获控制台上的坐标。
/*
控制类:从某些方面讲,这个类是对整个游戏的“调配”
*/
#pragma once
#include<vector>
#include"baseNode.h"
using namespace std;
class Controller
{
public:
static void moveXY(int x, int y); //移动光标的位置
static void showBaseNode(vector<BaseNode*> v); //把地图,蛇,食物什么的在控制台上显示出来
};说明:在Controller.cpp文件中我定义的是:如果是map类型的结点(TYPE_MAP),就画“*”;如果是蛇类结点(TYPE_SNAKE),就画“o”;如果是食物类结点(TYPE_FOOD),就画“$”。//Controller.cpp
#include "Controller.h"
#include"baseNode.h"
#include<iostream>
#include<Windows.h>
#include<conio.h>
using namespace std;
/*
moveXY这个函数通过SetConsoleCursorPosition这个函数可以定位到(x,y)在控制台上的位置
这样,我在showBaseNode函数里面调用这个函数,就可以在控制台上相应的位置上输出我想要的样式。
*/
void Controller::moveXY(int x, int y)
{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void Controller::showBaseNode(vector<BaseNode*> v)
{
for (int i = 0; i < v.size(); i++)
{
BaseNode* node = v.at(i);
Controller::moveXY(node->x, node->y); //moveXY方法的作用在这里体现了出来,若去掉这个语句的话
//在控制台上显示的图像是不管是啥都是按控制上的行依次输出的。
if (node->type == TYPE_SNAKE)
{
cout << "o";
}
else if (node->type == TYPE_MAP)
{
cout << "*";
}
else if (node->type == TYPE_FOOD)
{
cout << "#";
}
}
}
以上这两个类实现完毕后,我们依次实现蛇类,地图类和食物类。比较容易了。
蛇类:
//Snake.h
#pragma once
#include<vector>
#include"Controller.h"
#include"baseNode.h"
using namespace std;
class Snake
{
public:
Snake(int x, int y); //构造函数是用来初始化蛇的位置的
~Snake();
void showSnake();
private:
vector<BaseNode*> m_snake;
};//Snake.cpp
#include "Snake.h"
#include"Controller.h"
Snake::Snake(int x, int y)
{
m_snake.push_back(new BaseNode(x, y, TYPE_SNAKE));//创建一个蛇,就是将相应的结点压入vector,地图和食物同理。
}
Snake::~Snake()
{
while (m_snake.size())
{
delete m_snake.back();
m_snake.pop_back();
}
}
void Snake::showSnake()
{
Controller::showBaseNode(m_snake);
}
食物类,实现道理和蛇类基本一样:
//Food.h
#pragma once
#include"Controller.h"
#include"baseNode.h"
class Food
{
public:
Food(int x,int y);
~Food();
void showFood();
private:
vector<BaseNode*> m_food;
};//Food.cpp
#include "Food.h"
Food::Food(int x,int y)
{
m_food.push_back(new BaseNode(x, y, TYPE_FOOD));
}
Food::~Food()
{
while (m_food.size())
{
delete m_food.back();
m_food.pop_back();
}
}
void Food::showFood()
{
Controller::showBaseNode(m_food);
}
最后是地图类:
//Map.h
#pragma once
#include"baseNode.h"
#include"Controller.h"
using namespace std;
class Map
{
public:
Map();
~Map();
void showMap();
private:
vector<BaseNode*> m_map;
};//Map.cpp
#include "Map.h"
Map::Map() //最早初始化地图的时候,显然是周围的四面墙
{
for (int i = 0; i < 60; i++)
{
m_map.push_back(new BaseNode(i, 0, TYPE_MAP));
m_map.push_back(new BaseNode(i, 19, TYPE_MAP));
}
for (int i = 0; i < 20; i++)
{
m_map.push_back(new BaseNode(0, i, TYPE_MAP));
m_map.push_back(new BaseNode(59, i, TYPE_MAP));
}
}
Map::~Map()
{
while (m_map.size())
{
delete m_map.back();
m_map.pop_back();
}
}
void Map::showMap()
{
Controller::showBaseNode(m_map);
}
最后我们写一个主函数检验一下,在现在这个阶段,我们暂时不管随机生成食物等功能。所以我们先把坐标写成定值。只是看看能否实现第一阶段的目标——在控制台中能够显示出地图、蛇和食物。
Main.cpp
#include<iostream>
#include"Controller.h"
#include"baseNode.h"
#include"Map.h";
#include"Snake.h"
#include"Food.h"
void main()
{
{
Map* map = new Map();
Snake* snake = new Snake(5, 6);
Food* food = new Food(30, 15);
snake->showSnake();
food->showFood();
map->showMap();
}
system("pause");
}运行结果:
本文介绍了一种使用C++面向对象的方式实现控制台版贪吃蛇游戏的方法。通过定义结点类、控制类以及蛇、地图和食物类,实现了游戏的基本框架。文章详细讲解了每个类的设计和实现细节。
1万+

被折叠的 条评论
为什么被折叠?



