游戏中元素分为:墙壁,蛇,事物以及蛇的可行区域和右侧的版本号和游戏玩法提示
墙壁
*
号表示,代表一个区域范围,也就是蛇的可移动区域,蛇如果碰到墙壁视为死亡,
蛇
分为蛇头,蛇身,蛇头用@
符号表示,蛇身用=
等号表示,当蛇吃到食物时候,蛇身+1,一意为着长度变长,贪吃蛇可以通过不断地吃食物来增加自己的身体
食物
#
井号表示,蛇碰到食物会将食物吃掉
可移动区域
空格 表示,代表蛇可以移动的区域
提示信息
右侧展示,可以显示当前贪吃蛇版本号,制作人员,游戏玩法等提示信息
当运行游戏时候,画面静止不动,可以默认让蛇头超右,游戏中设置w s a d 4个按键分别代表,上,下,左,右,也是用户比较常用的方向按键,当用户输入w或者s或者d时候激活游戏,注意输入a不可以激活,因为蛇不可以180°转弯,因此蛇的移动方向只可以一直向前或者920°旋转
当蛇吃掉食物时候,此时蛇会增加一个身段,另外食物需要重新随机的设置到屏幕上
游戏结束方式有两种:
当激活游戏后,也分为两种,一种是死亡,这个是当 蛇头碰到蛇身或者是碰到墙壁两种死亡,这个我们暂时先不考虑,第二种是正常移动,那么我们先分析下正常移动
在正常移动的时候,也分为两种状态
这个时候,蛇只是单纯的移动,没吃到食物的时候,蛇会更新蛇头的位置,并且将之前蛇尾巴的位置为空格,也就是表示向前移动
当吃到食物时,当前的蛇头的位置应该为之前食物的位置,那么蛇尾由于吃到了食物,就还是在原有位置,然后食物再重新分配到一个其他的位置,这个位置不能是蛇,也不能是墙。
我们维护游戏种墙模块的开发,首先经过分析,我们可以得出再墙模块中,我们需要维护一个二维数组,对整个游戏中得元素进行设置,所以我们可以声明一个二维数组,char gameArray[][]
,具体的行数和列数可以定义出一个枚举,比如本游戏中设置的是26行,26列,enum{ROW=26,COL=26};
那么墙模块开发阶段,需要提供得主要接口是 初始化墙initwall
,以及打印墙,也就是将二维数组中得内容打印到控制台中,draw
方法,当然对外还要提供出一个可以修改二维数组元素的方法以及根据索引获取二维数组元素的方法:getWall,setWall
wall.h文件
#include
using namespace std;
class Wall
{
public:
enum
{
ROW = 26, //行数
COL = 26//列数
};
//初始化墙壁
void initWall();
//画出墙壁
void drawWall();
//根据索引来设置 二维数组里的内容
//设置蛇的部分的时候和设置食物要用
void setWall(int x, int y, char c);
//根据索引来获取当前位置的符号
char getWall(int x, int y);
private:
char gameArray[ROW][COL];
};
#endif
wall.cpp文件
#include
#include"wall.h"
using namespace std;
void Wall::initWall() //初始化墙壁,用二维数组
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
//放墙壁的地方
if (i == 0 || j == 0 || i == ROW - 1 || j == COL - 1)
{
gameArray[i][j] = '*';
}
else
{
gameArray[i][j] = ' ';
}
}
}
}
void Wall::drawWall()
{
for (int i = 0; i < ROW; ++i)
{
for (int j = 0; j < COL; ++j)
{
//画的时候多加一个空格,看起来好看一些
cout << gameArray[i][j] << " ";
}
if (i == 4)
{
cout << "版本:1.0";
}
if (i == 5)
{
cout << "制作人:刘晓昱";
}
if (i == 6)
{
cout << "a:向左";
}
if (i == 7)
{
cout << "d:向右";
}
if (i == 8)
{
cout << "w:向上";
}
if (i == 9)
{
cout << "s:向下";
}
cout << endl;
}
}
void Wall::setWall(int x, int y, char c)
{
gameArray[x][y] = c;
}
char Wall::getWall(int x, int y)
{
return gameArray[x][y];
}
snake.h
#pragma once
#include
#include"wall.h"
using namespace std;
#include"food.h"
class Snake
{
public:
Snake(Wall &tempWall,Food&food);
enum
{
UP = 'w',
DOWN = 's',
LEFT = 'a',
RIGHT = 'd'
};
struct Point
{
//数据域
int x;
int y;
//指针域
Point *next;
};
//初始化结点
void InitSnake();
//销毁结点
void destroyPoint();
//添加结点
void addPoint(int x, int y);
//移动时删除结点
void delPoint();
//移动操作
//返回值代表是否成功
bool move(char key);
//设定难度
//获取刷屏时间
int getSleepTime();
//获取蛇的身段
int countList();
//获取分数
int getScore();
Point * pHead;
Wall &wall;
Food &food;
bool isRool;//循环的标识
};
snake.cpp
#include"snake.h"
#include"wall.h"
#include
void gotoxy1(HANDLE hOut1, int x, int y)
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut1, pos);
}
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
Snake::Snake(Wall &tempWall,Food& tempFood) : wall(tempWall), food(tempFood)
{
pHead = NULL;
isRool = false;
}
void Snake::destroyPoint()
{
Point * pCur = pHead;
while (pHead!=NULL)
{
pCur = pHead->next;
delete pHead;
pHead = pCur;
}
}
void Snake::addPoint(int x, int y)
{
//创建新结点
Point * newpoint = new Point;
newpoint->x = x;
newpoint->y = y;
newpoint->next = NULL;
//如果原来头不为空,改为身子
if (pHead != NULL)
{
wall.setWall(pHead->x, pHead->y, '=');
gotoxy1(hOut1, pHead->y * 2, pHead->x);
cout << "=";
}
newpoint->next = pHead;
pHead = newpoint;//更新头部
wall.setWall(pHead->x, pHead->y, '@');
gotoxy1(hOut1, pHead->y * 2, pHead->x);
cout << "@";
}
void Snake::InitSnake()
{
destroyPoint();
addPoint(5, 3);
addPoint(5, 4);
addPoint(5, 5);
}
//移动时删除结点
void Snake::delPoint()
{
//两个以上结点 才去做删除操作
if (pHead == NULL || pHead->next == NULL)
{
return;
}
//当前结点
Point *pCur = pHead->next;
//上一个结点
Point *pPre = pHead;
while (pCur->next!=NULL)
{
pPre = pPre->next;
pCur = pCur->next;
}
//删除尾结点
wall.setWall(pCur->x, pCur->y, ' ');
gotoxy1(hOut1, pCur->y * 2, pCur->x);
cout << " ";
delete pCur;
pCur = NULL;
pPre->next = NULL;
}
bool Snake::move(char key)
{
int x = pHead->x;
int y = pHead->y;
switch (key)
{
case UP:
x--;
break;
case DOWN:
x++;
break;
case LEFT:
y--;
break;
case RIGHT:
y++;
break;
default:
break;
}
//判断 如果下一步碰到的是尾巴,不应该死亡
Point *pCur = pHead->next;
//上一个结点
Point *pPre = pHead;
while (pCur->next != NULL)
{
pPre = pPre->next;
pCur = pCur->next;
}
if (pCur->x == x&&pCur->y == y)
{
//碰到尾巴的循环
isRool = true;
}
else
{
//判断用户要到达的位置是否成功
if (wall.getWall(x, y) == '*' || wall.getWall(x, y) == '=')
{
addPoint(x, y);
delPoint();
system("cls");
wall.drawWall();
cout << "得分:" << getScore() << "分" << endl;
cout << "GAME OVER" << endl;
return false;
}
}
//移动成功 分两种
//吃到食物,未吃到食物
if (wall.getWall(x, y) == '#')
{
addPoint(x, y);
//重新设置食物
food.setFood();
}
else
{
addPoint(x, y);
delPoint();
if (isRool == true)
{
wall.setWall(x, y, '@');
gotoxy1(hOut1, y * 2, x);
cout << "@";
}
}
return true;
}
int Snake::getSleepTime()
{
int sleepTime=0;
int size = countList();
if (size < 5)
{
sleepTime = 300;
}
else if (size >= 5 && size <= 10)
{
sleepTime = 200;
}
else
{
sleepTime = 100;
}
return sleepTime;
}
int Snake::countList()
{
int size = 0;
Point * curPoint = pHead;
while (curPoint!=NULL)
{
size++;
curPoint = curPoint->next;
}
return size;
}
int Snake::getScore()
{
int size = countList();
int score = (size-3) * 100;
return score;
}
food.h
#pragma once
#include
using namespace std;
#include"wall.h"
class Food
{
public:
Food(Wall & tempwall);
void setFood();
int FoodX;
int FoodY;
Wall &wall;
};
food.cpp
#include"food.h"
#include
void gotoxy2(HANDLE hOut2, int x, int y)
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut2, pos);
}
HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
Food::Food(Wall &tempwall) :wall(tempwall)
{
}
void Food::setFood()
{
while (true)
{
FoodX = rand() % (Wall::ROW - 2) + 1;
FoodY = rand() % (Wall::COL - 2) + 1;
//如果随机的位置是蛇头或蛇身,就重新生成随机数
if (wall.getWall(FoodX, FoodY) == ' ')
{
wall.setWall(FoodX, FoodY, '#');
gotoxy2(hOut2, FoodY * 2, FoodX);
cout << '#';
break;
}
}
}
game.cpp
#include
using namespace std;
#include"wall.h"
#include"snake.h"
#include"food.h"
#include
#include
#include
//解决光标问题
void gotoxy(HANDLE hOut, int x, int y)
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut, pos);
}
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
int main()
{
//添加随机种子
srand((unsigned int)time(NULL));
//是否死亡的标识
bool isDead = false;
char preKey = NULL;
Wall wall;
wall.initWall();
wall.drawWall();
Food food(wall);
food.setFood();
Snake snake(wall,food);
snake.InitSnake();
gotoxy(hOut, 0, Wall::ROW);
cout << "得分:" << snake.getScore() << "分" << endl;
//gotoxy(hOut, 10, 5);//y*2 x
//接受用户的输入
while (!isDead)
{
char key = _getch();
//判断如果是第一次按了,左键,才不能激活游戏
//判断上一次移动方向
if (preKey == NULL&&key == snake.LEFT)
{
continue;
}
do
{
if (key == snake.UP || key == snake.DOWN || key == snake.LEFT || key == snake.RIGHT)
{
//判断本次按键是否与上次冲突
if ((key == snake.LEFT&&preKey == snake.RIGHT )||
(key == snake.RIGHT&&preKey == snake.LEFT )||
(key == snake.UP&&preKey == snake.DOWN) ||
(key == snake.DOWN&&preKey == snake.UP)
)
{
key = preKey;
}
else
{
preKey = key;//不是冲突按键,可以更新按键
}
if (snake.move(key) == true)
{
//移动成功
//system("cls");
//wall.drawWall();
gotoxy(hOut, 0, Wall::ROW);
cout << "得分:" << snake.getScore() << "分" << endl;
Sleep(snake.getSleepTime());
}
else
{
isDead = true;
break;
}
}
else
{
key = preKey;//强制将错误按键变为上一次移动的方向
}
} while (!_kbhit());//当没有键盘输入的时候返回0
}
system("pause");
return 0;
}