网上好多贪吃蛇的源码,不过大部分都是用C面向过程的.自己用面向对象写了一个贪吃蛇,不足之处还请大家见谅.
我定义了3个类.
地图类:包括地图的抽象数组, 地图里面包含一个食物的坐标.用户可以自定义地图的起始坐标和宽度高度.
蛇类: 类中有蛇的出生长度,所属地图,方向,是否存活. 蛇可以吃掉食物,左右上下移动,死亡.
控制类: 记录分数,并操纵蛇移动,控制游戏进度暂停/结束.
"GreedySnake.h" //包含了蛇类和地图类.
#ifndef __GREEDYSNAKE_H_
#define __GREEDYSNAKE_H_
#define MAXLENGTH 200
#define MAXMAPWIDTH 200
#define MAXMAPHEIGHT 200
enum Direction{ LEFT = 'a', RIGHT = 'd', UP = 'w', DOWN = 's' };
struct Point
{
int x, y;
};
void setWindowSize(int lines, int cols); //设置窗口大小
void gotoXY(int x, int y); //光标跳转到指定位置
void setColor(int colorId); //设置输出颜色
bool isOverlap(Point &p1, Point &p2); //判断两个坐标是否重叠
class Map
{
private:
bool _mapObj[MAXMAPWIDTH][MAXMAPHEIGHT]; //地图数组模型
Point _mapStartingCoord, //地图起始坐标
_food; //食物坐标
int _mapWidth, //地图宽度
_mapHeight; //地图高度
public:
Map(const int x, const int y, const int width, const int height); //绘制地图
void refreshFood(); //刷新食物
int getWidth()const { return _mapWidth; }
int getHeight()const { return _mapHeight; }
Point getMapStartingCoord()const { return _mapStartingCoord; }
Point getFoodCoord()const { return _food; }
~Map(){}
};
class Snake
{
private:
Map *_belongs; //所属地图
Direction _direction; //方向
int _length; //长度
bool _isLife; //是否存活
public:
Snake(Map *map, int defaultLenth);
bool eatFood();
void draw()const;
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
void dead();
Map * getBelongsMap()const{ return _belongs; }
int getLength()const { return _length; }
bool isLife()const { return _isLife; }
~Snake();
int _x[MAXLENGTH], _y[MAXLENGTH];
};
#endif
实现代码 "GreedySnake.cpp" 该文件中还包含了几个画图操作必要的函数
#include "GreedySnake.h"
#include <Windows.h>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <ctime>
using std::cout; using std::endl;
int i, j;
void setWindowSize(int cols, int lines) //设置窗口大小
{
char cmd[30];
sprintf_s(cmd, "mode con cols=%d lines=%d", cols * 2, lines);
system(cmd);
}
void gotoXY(const int x, const int y) //光标跳转到指定位置
{
COORD point;
point.X = 2 * x;
point.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
}
void gotoXY(const Point &p) //光标跳转到指定位置
{
COORD point;
point.X = 2 * p.x;
point.Y = p.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
}
void setColor(int colorId) //设置输出颜色
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorId);
}
Map::Map(const int x, const int y, const int width, const int height)
{
_mapStartingCoord.x = x;
_mapStartingCoord.y = y;
_mapWidth = width + 2;
_mapHeight = height + 2;
memset(_mapObj, 0, sizeof(_mapObj));
for (i = 1; i <= width; ++i) //初始化抽象地图数组
{
for (j = 1; j <= height; ++j)
_mapObj[i][j] = 1;
}
gotoXY(x, y);
setColor(11);
for (i = 0; i != _mapWidth; ++i) //根据抽象地图数组把地图画出来
{
for (j = 0; j != _mapHeight; ++j)
{
if (_mapObj[i][j])
cout << "■";
else
cout << "□";
}
cout << endl;
}
refreshFood();
}
void Map::refreshFood() //刷新食物
{
_food.x = rand() % (_mapWidth - 2) + 1 + _mapStartingCoord.x;
_food.y = rand() % (_mapHeight - 2) + 1 + _mapStartingCoord.y;
gotoXY(_food.x, _food.y);
setColor(12);
cout << "●" << endl;
}
Snake::Snake(Map *map, int defaultLenth)
{
_belongs = map;
_isLife = true;
_direction = DOWN;
_x[0] = _belongs->getWidth() / 2 + _belongs->getMapStartingCoord().x; //蛇出现在地图最中央
_y[0] = _belongs->getHeight() / 2 + _belongs->getMapStartingCoord().y;
_length = defaultLenth;
int i;
for (i = 1; i != _length; ++i)
{
_x[i] = _x[0];
_y[i] = _y[i - 1] - 1;
}
draw();
}
bool Snake::eatFood()
{
if (_x[0] == _belongs->getFoodCoord().x && _y[0] == _belongs->getFoodCoord().y) //判断是否吃到食物
{
_x[_length] = _x[_length - 1]; //先让新增加的蛇屁股坐标等与进食前的蛇屁股,移动后会自动刷新
_y[_length] = _y[_length - 1];
++_length;
_belongs->refreshFood();
return true;
}
return false;
}
void Snake::draw()const
{
setColor(14);
for (int i = 0; i < _length; ++i)
{
gotoXY(_x[i], _y[i]);
cout << "★" << endl;
}
}
void Snake::moveUp()
{
gotoXY(_x[_length - 1], _y[_length - 1]);
setColor(11);
cout << "■";
for (i = _length - 1; i != 0; --i)
{
_x[i] = _x[i - 1];
_y[i] = _y[i - 1];
}
_y[0] -= 1;
draw();
}
void Snake::moveDown()
{
gotoXY(_x[_length - 1], _y[_length - 1]);
setColor(11);
cout << "■";
for (i = _length - 1; i != 0; --i) //蛇的移动算法, 让后一节的坐标等于前一节的坐标
{
_x[i] = _x[i - 1];
_y[i] = _y[i - 1];
}
_y[0] += 1;
draw();
}
void Snake::moveLeft()
{
gotoXY(_x[_length - 1], _y[_length - 1]);
setColor(11);
cout << "■";
for (i = _length - 1; i != 0; --i)
{
_x[i] = _x[i - 1];
_y[i] = _y[i - 1];
}
_x[0] -= 1;
draw();
}
void Snake::moveRight()
{
gotoXY(_x[_length - 1], _y[_length - 1]);
setColor(11);
cout << "■";
for (i = _length - 1; i != 0; --i)
{
_x[i] = _x[i - 1];
_y[i] = _y[i - 1];
}
_x[0] += 1;
draw();
}
void Snake::dead()
{
if (_x[0] <= _belongs->getMapStartingCoord().x //判断是否碰到地图边框
|| _x[0] >= _belongs->getWidth() + _belongs->getMapStartingCoord().x - 1
|| _y[0] <= _belongs->getMapStartingCoord().y
|| _y[0] >= _belongs->getHeight() + _belongs->getMapStartingCoord().y - 1)
_isLife = false;
for (i = 3; i <= _length - 1; ++i) //判断是否碰到自己,从第4节开始算,因为头肯定碰不到第2/3节
{
if (_x[0] == _x[i] && _y[0] == _y[i])
_isLife = false;
}
}
控制类头文件 "Controller.h"
#ifndef __CONTROLLER_H_
#define __CONTROLLER_H_
#include "GreedySnake.h"
class Controller
{
private:
Snake *_snake; //要控制的蛇
char _dir; //存储方向键
int _gameSpeed; //游戏速度
int _score; //分数
public:
Controller(Snake *s, int speed = 200, char dir = 'q') :_snake(s), _gameSpeed(speed), _dir(dir), _score(0){ }
void start(); //开始游戏
void pause(); //暂停
void stop(); //停止
void showScore(); //显示分数
~Controller(){}
};
#endif
控制类实现代码 "Controller.cpp"
#include "Controller.h"
#include <conio.h>
#include <Windows.h>
#include <iostream>
using std::cout;
void Controller::start()
{
char key = '\0';
showScore();
while (1)
{
if (_snake->isLife())
{
if (_kbhit())
{
gotoXY(0, _snake->getBelongsMap()->getHeight() + 2);
_dir = _getche();
}
switch (_dir)
{
case UP:
if (key != DOWN) //判断是否朝相反方向移动,下同
{
_snake->moveUp();
key = _dir;
}
else
_snake->moveDown(); break;
case DOWN:
if (key != UP)
{
_snake->moveDown();
key = _dir;
}
else
_snake->moveUp(); break;
case LEFT:
if (key != RIGHT)
{
_snake->moveLeft();
key = _dir;
}
else
_snake->moveRight(); break;
case RIGHT:
if (key != LEFT)
{
_snake->moveRight();
key = _dir;
}
else
_snake->moveLeft(); break;
case 8: pause(); break; //空格键暂停
default:break;
}
_snake->dead();
Sleep(_gameSpeed);
if (_snake->eatFood())
{
++_score;
showScore();
}
}
else
{
stop();
break;
}
}
}
void Controller::pause()
{
while (1)
{
if (_kbhit())
{
gotoXY(0, _snake->getBelongsMap()->getHeight() + 2);
_dir = _getche();
}
if (_dir == 8)
start();
}
}
void Controller::stop()
{
int x = _snake->getBelongsMap()->getWidth() / 2;
int y = _snake->getBelongsMap()->getHeight() / 2;
gotoXY(x, y);
setColor(30);
cout << "game over!";
Sleep(3000);
}
void Controller::showScore()
{
gotoXY(3, 1);
setColor(14);
cout << _score;
}
最后是 "main.cpp"
/* GreedySnake by ian*/
#include "GreedySnake.h"
#include "Controller.h"
#include <cmath>
#include <iostream>
#include <ctime>
void initWindow(int windowWidth, int windowHeight)
{
setWindowSize(windowWidth, windowHeight);
gotoXY(windowWidth / 2 - 3, 0);
setColor(23);
std::cout << "Greedy Snake" << std::endl;
setColor(90);
std::cout << "Score:" << std::endl;
gotoXY(windowHeight - 7, 1);
std::cout << "by ian." << std::endl;
}
int main()
{
srand((unsigned)time(0));
initWindow(28, 30);
Map *map = new Map(0,2,25, 25);
Snake *snake = new Snake(map, 3);
Controller c(snake);
c.start();
delete map;
}
以下是游戏界面.
.
个人感觉控制类的实现还不是太好, 也希望大家能指出