贪吃蛇原型实现基本思路

贪吃蛇的原型是在windows控制台下即在字符模式下利用C++编写贪吃蛇。 

主要实现的效果就是,用户利用'w'、's'、'a'、'd'分别表示上下左右,当蛇碰到障碍物时通不过,当碰到食物时,分数加1,同时,在另外一个地方生成新的食物,并且身体会增加一个字符的长度。 

在实现该原型前必须实现两项技术: 

  • 字符上色,这个利用了网上的代码
  • 字符位置控制

什么意思呢?主要是因为考虑到在通常情况下,在控制台下编写的程序,默认都是黑白色的,颜色单一,而且蛇和障碍物、食物之间无法被互相区分,所以加上更多的颜色,视觉上会更舒服些。字符位置控制呢,是必不可缺的。这个是因为在命令行模式下,程序是默认顺序接收用户输入的,偶尔可以跳至同一行稍前些的位置。但是,由于蛇要到处爬,所以这就遇到一个问题,如何让输入光标跳至窗口下的任意位置成了开发的基础和前提。具体的我后面会讲到我的解决方案。 

我的实现思路: 

首先,我要创建一个游戏背景,具体指的是贪吃蛇的移动范围。这个呢,我用二维的数组来表示。每个单元格有几个参数---是否存在障碍物、是否存在食物,如果有的话,就用true,否则为false。 

 1 //这个是背景的单元格数据结构  
2 const length = 40;
3 const width = 20;
4 struct square{
5 bool blocked; //是否有障碍物
6 bool food; //是否有食物
7 int x; //单元格在背景中的相对横坐标
8 int y; //单元格在背景中的相对纵坐标
9 }bg[length][width]; //直接创建游戏背景
10
11 //设置背景
12 void setBG(int length, int width){
13 HANDLE hOut;
14 COORD OutChar;
15 OutChar.X = 10;
16 OutChar.Y = 10;
17 int i = 0;
18 int j = 0;
19 for(i = 0; i < width; i++){
20 for(j = 0; j < length; j++){
21 bg[i][j].x = i;
22 bg[i][j].y = j;
23 bg[i][j].blocked = false;
24 bg[i][j].food = false;
25 OutChar.X = j+10;
26 hOut = GetStdHandle(STD_OUTPUT_HANDLE);
27 SetConsoleCursorPosition(hOut,OutChar);
28 cout << col(BG_WHITE,true) << " ";
29 }
30 cout << endl;
31 OutChar.Y = i+10;
32 SetConsoleCursorPosition(hOut,OutChar);
33 }
34 }

接下来,就是要在背景上创建障碍物,在创建它们之前,首先得解决如何在指定的单元格式绘制单元。思路很简单,就是编写一个函数将光标定位到相应的坐标位置,然后输入一个空白的上色字符就解决了。然后就可以利用这个函数在真正的背景上相应的位置构建障碍物了。同时可以实现随机创建任意个、任意位置的障碍物了。

 1 //构造障碍物  
2 void createBlock(int x, int y, unsigned short color){
3 HANDLE hOut;
4 COORD OutChar;
5 OutChar.X = x;
6 OutChar.Y = y;
7 hOut = GetStdHandle(STD_OUTPUT_HANDLE);
8 SetConsoleCursorPosition(hOut,OutChar); //定位光标输入
9 cout << col(color, true) << " "; //一个颜色为color的空白字符
10 }
11
12 //生成单个障碍物
13 void createWall(int x,int y){
14 createBlock(x+10,y+10,BG_GREEN);
15 bg[x][y].blocked = true;
16 }
17
18 //判断所指坐标是否被占用
19 bool checkExisted(int x,int y){
20 if(bg[x][y].blocked == true || bg[x][y].food == true){
21 return false;
22 }
23 return true;
24 }
25
26 //随机生成障碍物
27 void rand_createWall(void){
28 srand((unsigned)time(NULL));
29 int n = rand() % 70+10;
30 int pos_x = 0;
31 int pos_y = 0;
32 int i = 0;
33 for(i = 0; i < n; i++){
34 pos_x = rand() % length;
35 pos_y = rand() % (width-1);
36 if(checkExisted(pos_x,pos_y) == true){ //防止障碍物重叠
37 createWall(pos_x,pos_y);
38 }else{
39 n++;
40 }
41 //createWall(pos_x,pos_y);
42 }
43 }

同理,食物的创建也一样。

 1 //创建食物  
2 void createFood(int x,int y){
3 createBlock(x+10,y+10,BG_BLUE);
4 bg[x][y].food = true;
5 }
6
7 //随机创建食物
8 void rand_createFood(void){
9 srand((unsigned)time(NULL));
10 int n = 1;//rand() % 20;
11 int pos_x = 0;
12 int pos_y = 0;
13 int i = 0;
14 for(i = 0; i < n; i++){
15 pos_x = rand() % length;
16 pos_y = rand() % (width-1);
17 if(checkExisted(pos_x,pos_y) == true){ //防止在障碍物上生成食物
18 createFood(pos_x,pos_y);
19 }else{
20 n++;
21 }
22 }
23 }

背景、障碍物和食物都创建好了,接下来就要构建蛇的模型了。由于考虑到蛇在吃完食物后,身体会变长,所以我采用了顺序表的数据结构来记录蛇的信息。那如何来表现蛇的移动呢?我的思路是,将身体靠近头的信息传递到次靠近头的,同时将新的下一步要走的坐标信息传给头部,同时有擦除掉尾部信息即可。 

 1 //物体信息,这是蛇的单元模型  
2 const objLen = 5;
3 struct obj{
4 int x;
5 int y;
6 }snake[objLen];
7
8 //创建蛇
9 LinList<struct obj> newSnake;
10 void createSnake(void){
11 int i = 0;
12 for(i = 0; i < objLen; i++){
13 snake[i].x = i;
14 snake[i].y = 0;
15 newSnake.Insert(snake[i],i);
16 }
17 }
18
19 //绘制蛇
20 void drawSnake(int len){
21 int i = 0;
22 struct obj t;
23 for(i = 0; i < len; i++){
24 t = newSnake.GetData(i);
25 createBlock(t.x,t.y,BG_RED);
26 }
27 }
28
29 //增长蛇的身体
30 void insreaseSnake(int x,int y){
31 struct obj t;
32 t.x = x;
33 t.y = y;
34 newSnake.Insert(t,0);
35 createBlock(x,y,BG_RED);
36 }
37
38 //传递蛇的信息
39 void transSnake(int x,int y,int len){
40 int i = 0;
41 struct obj t1,t2;
42 for(i = 0; i < len-1; i++){
43 t1 = newSnake.GetData(i);
44 t2 = newSnake.GetData(i+1);
45 newSnake.Delete(i);
46 t1.x = t2.x;
47 t1.y = t2.y;
48 newSnake.Insert(t1,i);
49 }
50 newSnake.Delete(newSnake.Size()-1);
51 t1.x = x;
52 t1.y = y;
53 newSnake.Insert(t1,newSnake.Size()-1);
54 }

这里的相对位置是指以背景左上方的点为原点即(0,0),右下方为(length-1,width-1)。 

接着呢,就是要获取用户的方向控制操作,并执行它们。由于采用的二维数组的方式表示蛇的形状以及游戏背景,所以当接收到命令如果是‘上’时,横坐标不变,而纵坐标减1,同时擦除原形状,如此一来,蛇就向上爬了。擦除函数的原理很简单,就是将对应的背景单元格的信息全部还原为初始值,就可以了。 

 1 //清除物体移动轨迹  
2 void removeTrack(int x, int y){
3 HANDLE hOut;
4 COORD OutChar;
5 OutChar.X = x;
6 OutChar.Y = y;
7 hOut = GetStdHandle(STD_OUTPUT_HANDLE);
8 SetConsoleCursorPosition(hOut,OutChar);
9 cout << col(BG_WHITE,true) << " ";
10 }
11
12 //移动物体
13 int x = 10;
14 int y = 10;
15 int tail_x = 0;
16 int tail_y = 0;
17 void moveBlock(char direction){
18 HANDLE hOut2;
19 COORD OutChar2;
20 OutChar2.X = x;
21 OutChar2.Y = y;
22 struct obj t;
23 t = newSnake.GetData(0);
24 tail_x = t.x;
25 tail_y = t.y;
26 hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);
27 removeTrack(t.x,t.y);
28 switch(direction){
29 case 'w':{
30 OutChar2.Y--;
31 y--;
32 SetConsoleCursorPosition(hOut2,OutChar2);
33 break;
34 }
35 case 's':{
36 OutChar2.Y++;
37 y++;
38 SetConsoleCursorPosition(hOut2,OutChar2);
39 break;
40 }
41 case 'a':{
42 OutChar2.X--;
43 x--;
44 SetConsoleCursorPosition(hOut2,OutChar2);
45 break;
46 }
47 case 'd':{
48 OutChar2.X++;
49 x++;
50 SetConsoleCursorPosition(hOut2,OutChar2);
51 break;
52 }
53 }
54
55 transSnake(x,y,newSnake.Size());
56 drawSnake(newSnake.Size());
57 }

做完这个,不要以为就这样结束了,因为我们还没对蛇的运动范围作出限制,而且还要实现蛇的触发事件即碰到食物后身体变长,食物数增加1。 

 1 //判断是否碰到障碍物或边界  
2 bool checkView(char direction){
3 if(direction == 'w' && y >= 10){
4 if(y == 10 || bg[x-10][y-10-1].blocked == true){return false;}
5 }
6 else if(direction == 's' && y < 10+width){
7 if(y == 10+width-2 || bg[x-10][y-10+1].blocked == true){return false;}
8 }
9 else if(direction == 'a' && x >= 10){
10 if(x == 10 || bg[x-10-1][y-10].blocked == true){return false;}
11 }
12 else if(direction == 'd' && x < 10+length){
13 if(x == 10+length-1 || bg[x-10+1][y-10].blocked == true){return false;}
14 }
15 return true;
16 }
17
18 //判断是否吃到食物
19 bool checkFood(int x, int y){
20 if(bg[x-10][y-10].food == true){return true;}
21 return false;
22 }

下面就是游戏原型的主函数:

 1 int main()  
2 {
3 HANDLE hOut;
4 COORD OutChar;
5
6 OutChar.X = 0;
7 OutChar.Y = 0;
8 hOut = GetStdHandle(STD_OUTPUT_HANDLE);
9 SetConsoleCursorPosition(hOut,OutChar);
10
11 /*
12 struct square **bgR = new square*[width];
13 struct square *bgC = new square[length];
14 for(int i = 0; i < width; i++){
15 bgR[i] = bgC;
16 }
17 */
18 //设置背景
19 setBG(length,width);
20 //设置障碍物
21 rand_createWall();
22 //设置食物
23 rand_createFood();
24 //创建蛇
25 createSnake();
26 //移动物体
27 char direction;
28 int score = 0;
29 for(;;){
30 direction = getch();
31 if(checkView(direction) == true){//判断能否移动
32 moveBlock(direction);
33 if(checkFood(x,y) == true){//判断是否吃到食物
34 bg[x-10][y-10].food = false;
35 score++;
36 insreaseSnake(tail_x,tail_y);//增长身体
37 rand_createFood();//吃完后随机在创建一个食物
38 }
39 }
40 OutChar.X = 0;
41 OutChar.Y = 0;
42 hOut = GetStdHandle(STD_OUTPUT_HANDLE);
43 SetConsoleCursorPosition(hOut,OutChar);
44 cout << col(BG_WHITE,true) << "Scores: " << score;
45 }
46 return 0;
47 }

这个贪吃蛇原型是我一时兴起弄的,考虑的也比较简单。


原文链接: https://www.cnblogs.com/bilipan/archive/2012/03/30/2425442.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    贪吃蛇原型实现基本思路

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/45696

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午10:13
下一篇 2023年2月8日 下午10:14

相关推荐