本博客中部分代码并非原创,浅谈对于代码的理解,由于水平所限,在此作为个人回忆整理所用.
原创地址:https://blog.csdn.net/shawn_hou/article/details/27734217
根据以前玩的贪吃蛇游戏,它的核心在于实现动态的蛇:首先为了将蛇动态的描述出来,通过不断的输出,清屏,修改,再输出这样的循环来实现动画的效果,动画本身就是由一张张图片表现出来的。
下一步需要一个类,它能存储要输出的数据,游戏界面包括四周的墙壁以及蛇的展示,采用一个二维字符数组来完整表示,设定蛇头为‘#’,蛇身为‘*’,想一想,当要描述蛇在不断前进时,如何实现蛇下一步所在位置字符的变化?根据当前的方向值,以蛇向右直行为例,计算出下一步的蛇头坐标,x方向加1,y不变,更新新蛇头(变为'#'),旧蛇头('#'变为'*'),旧蛇尾(‘*’变为‘‘)所在位置的字符。在这更新的过程中,需要得到旧蛇头旧蛇尾的坐标,新蛇尾仍然还是'*',保持不变,但下一次又需要新蛇尾的坐标(变为空字符),以此类推,需要将蛇的所有坐标都存储起来。
创建一个只包含坐标x和坐标y的简单类,用这个类的数组来保存蛇身上每个点的坐标值,并不关心当前坐标上的字符是'#'还是'*',只起到存储作用。蛇头和蛇尾是这两个类之间的纽带。按照数组序号依次递增,记录了蛇不断爬行的坐标,成为蛇爬行的痕迹。
注意:
1,全局变量的声明应放在other.cpp(存放类方法定义的文件)中,放在.h文件中会造成多重定义,因为main.cpp和other.cpp中均包含头文件。
2,使用getch()函数(在vs中强制使用_getch(),效果不变)来读取用户的输入,如果要使用getchar()函数,那就需要在每一次使用之前清除缓冲区。因为每次从键盘输入一个字符时,换行符残留在缓冲区中,会干扰下一次的读取。
3,在up_data方法中,为什么采用两次_getch()来读取键盘上的方向键,因为根据键盘扫描码,实际键盘的一次按键并非是想象中如ASC||码中的8位,而是16位。 第一次_getch()接受低8位的数值,第二次_getch()接受高8位的值,例如:字符'a' 0x1e61, 'b' 0x3062, 'c' 0x2e63 普通字符按键的低8位值和其ASC||码相同。但是四个方向键的值和ASC码值不同 ↑ 0x4800, ↓ 0x5000, ← 0x4b00, → 0x4d00 ,因此主要获取第二次的_getch()返回值。
4,友情提示:按一次空格键暂停,按两次空格键恢复。
5,可以与原创文章结合在一起进行理解,欢迎留言和讨论~~,
头文件:
#ifndef HEADER_H_
#define HEADER_H_
#define N 22
#include
#include//食物的随机分配rand()
#include//蛇的速度,等级clock()
#include//控制台_getch(),_kbhit()函数的使用
class positions//保存坐标,有一点要清楚,走的是留下的痕迹
{
public:
int x, y;
void initialize(int&);//存储蛇最初的位置坐标
};
class Snake//可以再进行添加别的元素以使得游戏更加有趣
{
private:
char s[N][N];//用于保存游戏界面信息
char direction;
int head, tail;
int gamespeed,grade;
public:
Snake(int h = 4, int t = 1, char d = 77, int g = 400) :
head(h), tail(t), direction(d), gamespeed(400){}
void initialize();
void setfood();
void getgrade();
void show_game();
bool updata_game();
};
#endif
other.cpp文件
#include"header.h"
using std::cout;
using std::endl;
using std::cin;
bool gameover;
char key;
int xh, yh;//蛇头位置坐标
int xf, yf;//食物坐标点
positions snakexy[(N - 2)*(N - 2)];//建立一个position类的数组
void positions::initialize(int& a)
{
x = 1;
y = a;
}
void Snake::initialize()
{
int i, j;
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
{
if (i == 0 || i == N - 1)
s[i][j] = '-';
else
s[i][j] = ' ';
}
for (i = 1; i < N - 1; i++)
{
s[i][0] = '|';
s[i][N - 1] = '|';
}
for (j = 1; j < 4; j++)
s[1][j] = '*';
s[1][4] = '#';
}
void Snake::setfood()
{
srand(time(0));
while (1)
{
xf = rand() % N;
yf = rand() % N;
if (s[xf][yf] == ' ')
{
s[xf][yf] = '*';
break;
}
}
}
void Snake::getgrade()
{
cout << "\n\n\n\n\t\t\t1:Grade speed 500"
<< "\n\n\t\t\t1:Grade speed 250" << "\n\n\t\t\t3:Grade speed 125" << "\n\n\t\t\t4:Grade speed 50";
cout << "\n\n\n\t\tPlease choose the grade(1 or 2 or 3 or4)";
while (cin >> grade)
{
if (grade == 1 || grade == 2 || grade == 3 || grade == 4)
break;
else
{
cout << "Input error!and input again..\n";
}
}
switch (grade)
{
case 1:
gamespeed = 500;
break;
case 2:
gamespeed = 250;
break;
case 3:
gamespeed = 125;
break;
case 4:
gamespeed = 50;
}
}
void Snake::show_game()
{
system("CLS");
cout << endl;
cout << "\t\tHello,snake is waiting for you!!\n\n";
int i, j;
for (i = 0; i < N; i++)
{
cout << '\t';
for (j = 0; j < N; j++)
{
cout << s[i][j]<< ' ';
}
if (i == 6)
cout << "\t Speed: " << gamespeed;
if (i == 10)
cout << "\t Pause: press the space key!";
if (i == 14)
cout << "\t Recover: press the space key twice!!";
cout << endl;
}
}
bool Snake::updata_game()
{
gameover = 1;
key = direction;
long tt = clock();
while ((gameover=(clock() - tt) <= gamespeed) && !_kbhit());
//gamespeed实际上是作为每次蛇自动前进的时间间隔,gamespeed越小,速度越快。
//在while语句中,若存在击键,此时产生的时间之差肯定是小于设定的时间间隔的,gameover仍为1(真)继而读取键盘输入;
//否则等待时间之差越来越大,便会跳出while循环,gameover=0,使用上一次的方向;
if (gameover)
{
_getch();
key = _getch();//获取击键的值
}
if (key == ' ')//空格暂停
{
while (_getch() != ' ')
continue;
}
else
direction = key;
switch (direction)//计算下一步要显示的蛇头的坐标
{
case 72:xh = snakexy[head].x - 1;
yh = snakexy[head].y;
break;
case 80:xh = snakexy[head].x + 1;
yh = snakexy[head].y;
break;
case 77:xh = snakexy[head].x;
yh = snakexy[head].y + 1;
break;
case 75: xh = snakexy[head].x;
yh = snakexy[head].y - 1;
}
if (direction != 72 && direction != 80 && direction != 75 && direction != 77)
gameover = 0;
else if (xh == 0 || xh == N - 1 || yh == 0 || yh == N - 1)//碰到墙壁
gameover = 0;
else if (s[xh][yh] != ' '&&xh != xf&&yh != yf)//碰到蛇自己
gameover = 0;
else if (xh == xf&&yh == yf)//吃到食物
{
s[xh][yh] = '#';
s[snakexy[head].x][snakexy[head].y] = '*';
head = (head + 1) % ((N - 1)*(N - 2));
snakexy[head].x = xh;
snakexy[head].y = yh;
setfood();
gameover = 1;
}
else
{
//snakexy[head].x = xh;
//snakexy[head].y = yh;
//s[snakexy[head].x][snakexy[head].y] = '#';
s[xh][yh] = '#';
s[snakexy[head].x][snakexy[head].y] = '*';
head = (head + 1) % ((N - 1)*(N - 2));
snakexy[head].x = xh;
snakexy[head].y = yh;
s[snakexy[tail].x][snakexy[tail].y] = ' ';
tail = (tail + 1) % ((N - 1)*(N - 2));
gameover = 1;
}
return gameover;
}
main.cpp文件:
#include"header.h"
int main()
{
using namespace std;
extern positions snakexy[(N - 2)*(N - 2)];
char input = 'y';
bool live = 1;
while (input == 'y')
{
system("CLS");
cout << "\n\n\n\n\n\n\n\n\n\t\t\tWelcome to the snakes.\n";
cout << "\n\n\n\t\tAny key you push and the game will go...\n";
_getch();
system("CLS");
Snake snake;
snake.getgrade();
snake.initialize();
for (int i = 1; i <= 4; i++)
snakexy[i].initialize(i);//存储最初蛇的坐标。
snake.setfood();
while (live)
{
snake.show_game();
live = snake.updata_game();
}
system("CLS");
cout << "\n\n\n\t\t\tGameover!\n\n" << endl;
cout << "\n\n\n\t\t Want to continue ? y or n.\n\t\t\t\t";
cin>> input;
live = 1;
}
return 0;
}