本博客中部分代码并非原创,浅谈对于代码的理解,由于水平所限,在此作为个人回忆整理所用.
原创地址: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<iostream> #include<cstdlib>//食物的随机分配rand() #include<ctime>//蛇的速度,等级clock() #include<conio.h>//控制台_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; }