自制贪吃蛇——方向控制,初始长度增加

  上篇地址 :http://www.cnblogs.com/chinxi/p/7185309.html

  有了一条会移动的“蛇”,就可以开始写改变它方向的方法了。

  由于这是运行在linux下的,没有像windows下的getch()方法,想要输入一个键,不输入回车,就让程序有响应,还是件麻烦事。

  不过,既然我能想到这种方式,那么之前一定有些前辈也遇到过相同问题。果然,找到了个解决办法:

  http://blog.csdn.net/shawnkong/article/details/36658177

  虽然我目前不知道这是什么道理,不过,直接把代码拷过来测试后发现可行,就先直接用了。

  在初始长度仍为1的情况之下 ,先试控制它的移动方向。

  在game.h中增加新方法

 1  ///
 2  /// @file    game.h
 3  /// @author  marrs(chenchengxi993@gmail.com)
 4  /// @date    2017-07-15 11:42:03
 5  ///
 6 
 7 #ifndef __GAME_H__
 8 #define __GAME_H__
 9 
10 #include "map.h"
11 #include "snake.h"
12 
13 namespace marrs{
14 
15 class Game
16 {
17     public:
18         int init();    
19         int output_map();    
20         int refresh_map();
21         int gen_random_point();
22         int reset_random_point();
23         
24         int forward();
25         int check_collision();     //todo
26 
27         int input();               //add
28         int change_direction();    //add
29         int loop();                //add
30 
31     private:
32         Map _map;
33         Snake _snake;    
34         Coordinate _random_point;
35         bool _bool_is_need_exit;   //add
36 };
37  
38 }
39 
40 #endif

  把main.cc中的循环也迁移到此,方便后期处理。

 1 int Game::input()
 2 {
 3     struct termios stored_settings;  
 4     struct termios new_settings;  
 5     tcgetattr (0, &stored_settings);  
 6     new_settings = stored_settings;  
 7     new_settings.c_lflag &= (~ICANON);  
 8     new_settings.c_cc[VTIME] = 0;  
 9     new_settings.c_cc[VMIN] = 1;  
10     tcsetattr (0, TCSANOW, &new_settings);  
11     char char_input = getchar();
12     putchar('');
13     tcsetattr (0, TCSANOW, &stored_settings);
14     switch(char_input)
15     {
16         case 'w':return EN_DIR_UP;
17         case 's':return EN_DIR_DOWN;
18         case 'a':return EN_DIR_LEFT;
19         case 'd':return EN_DIR_RIGHT;
20         default:break;
21 
22     }
23 
24     return -1;
25 }
26 
27 int Game::change_direction()
28 {
29     int int_new_direction = input();
30     if (int_new_direction == -1)
31     {
32         _bool_is_need_exit = true;
33     }else
34     {
35         _snake.change_direction(int_new_direction);
36     }
37     return 0;
38 }
39 
40 int Game::loop()
41 {
42     while(!_bool_is_need_exit)
43     {
44         change_direction();
45         forward();
46         refresh_map();
47 
48     }
49     return 0;    
50 
51 }

  目前做得很简单,输入个方向,蛇才前进一步,这样做是为了测试,而且想着先实现能够控制方向,再实现其它的东西。

  测试结果如下:

  运行程序

 1 [ccx@ubuntu ~/Retro_Snaker]$>./bin/game.exe 
 2 ++====================================++
 3 ||                                    ||
 4 ||                                    ||
 5 ||                                    ||
 6 ||                                    ||
 7 ||                                    ||
 8 ||                                    ||
 9 ||                                    ||
10 ||                                    ||
11 ||                                    ||
12 ||                  OO                ||
13 ||                                    ||
14 ||                                    ||
15 ||                                    ||
16 ||                                    ||
17 ||                                    ||
18 ||                                    ||
19 ||                                    ||
20 ||                                    ||
21 ++====================================++

  此时它不会主动前进。按下"w"后

 1 [ccx@ubuntu ~/Retro_Snaker]$>./bin/game.exe 
 2 ++====================================++
 3 ||                                    ||
 4 ||                     、               ||
 5 ||                                    ||
 6 ||                                    ||
 7 ||                                    ||
 8 ||                                    ||
 9 ||                                    ||
10 ||                                    ||
11 ||                  OO                ||
12 ||                                    ||
13 ||                                    ||
14 ||                                    ||
15 ||                                    ||
16 ||                                    ||
17 ||                                    ||
18 ||                                    ||
19 ||                                    ||
20 ||                                    ||
21 ++====================================++

  向上移了一行,分别测试"w"、"s"、"a"、"d",均符合预期。

  那么,如果初始长度不为1,会怎么样?

  修改game.cc的init方法:

 1 int Game::init()
 2 {
 3     Coordinate coordinate = _map.get_map_center_pos();
 4     _snake.init('O', EN_DIR_DOWN, 5, coordinate);    
 5     
 6     MapBase map;
 7     map.char_icon = 'O';
 8     map.int_type = EN_MAP_SNAKE;
 9     _map.set_map_val(coordinate.int_x, coordinate.int_y, map);
10 
11     _bool_is_need_exit = false;
12 
13     reset_random_point();
14     output_map();
15 
16     return 0;
17 
18 }

  由于我希望它从一个点出来,不是一开始就展示长度为5的蛇,故场景没有处理。

  此时,snake.h和snake.cc没有修改,它的init方法中,add_new_node还是todo状态,所以,这个参数改了也是白改。

  于是我加上了这个方法,并发现了之前的一个会导致程序在蛇长大于1的情况下会core的bug(囧)

  于是,对snake进行了修改

 1  ///
 2  /// @file    snake.h
 3  /// @author  marrs(chenchengxi993@gmail.com)
 4  /// @date    2017-07-15 09:32:47
 5  ///
 6 
 7 #ifndef __SNAKE_H__
 8 #define __SNAKE_H__
 9 
10 #include "define.h" 
11 
12 namespace marrs{
13 
14 class Snake
15 {
16     public:
17         Snake();
18         ~Snake();
19 
20     public:
21         int init(char char_icon, int int_direction, int int_size, Coordinate coordinate);
22         int change_direction(int int_direction);    
23         int add_new_node(Coordinate coordinate_new, char char_icon_new);
24         int add_new_node(char char_icon_new);
25         Snake_Base* del_tail_node();
26         Snake_Base* forward();
27         Snake_Base* get_snake_base();
28         SnakeHead* get_snake_head();
29         int get_snake_size();
30 
31     private:
32         SnakeHead _snake;
33 
34 };
35 
36 }
37 
38 #endif
  1  /// /// @file    snake.cc
  2  /// @author  marrs(chenchengxi993@gmail.com)
  3  /// @date    2017-07-15 11:02:32
  4  ///
  5 
  6 #include <string.h>
  7 #include <iostream>
  8 #include "snake.h"
  9 
 10 namespace marrs{
 11  
 12 Snake::Snake()
 13 {
 14 }
 15 
 16 Snake::~Snake()
 17 {
 18 }
 19 
 20 int Snake::init(char char_icon, int int_direction, int int_size, Coordinate coordinate)
 21 {
 22     _snake.char_icon = char_icon;
 23     _snake.int_direction = int_direction;
 24     _snake.int_size = 1;
 25     _snake.coordinate_cur = coordinate;
 26     _snake.pNext = NULL;
 27 
 28     if (int_size > 1)
 29     {
 30         for (int int_idx = 0; int_idx < int_size; ++int_idx)
 31         {
 32             add_new_node(char_icon);
 33         }
 34     }
 35     return 0;
 36 
 37 }
 38 
 39 int Snake::change_direction(int int_direction)
 40 {
 41     _snake.int_direction = int_direction;
 42     return int_direction;
 43 }
 44 
 45 int Snake::add_new_node(char char_icon_new)
 46 {
 47     Snake_Base* pNode = new Snake_Base;
 48     pNode->char_icon = char_icon_new;
 49     pNode->pNext = _snake.pNext;
 50     _snake.pNext = pNode;    
 51     
 52     ++_snake.int_size;
 53 
 54     return 0;
 55 
 56 }
 57 
 58 int Snake::add_new_node(Coordinate coordinate_new, char char_icon_new)
 59 {
 60     Snake_Base* pNode = new Snake_Base;
 61     memset(pNode, 0, sizeof(Snake_Base));
 62     pNode->coordinate_cur = _snake.coordinate_cur;
 63     pNode->char_icon = _snake.char_icon;
 64 
 65     if (_snake.pNext == NULL)
 66     {
 67         _snake.pNext = pNode;        
 68     }else{
 69         pNode->pNext = _snake.pNext;
 70         _snake.pNext = pNode;
 71     }
 72     
 73     _snake.coordinate_cur = coordinate_new;
 74     _snake.char_icon = char_icon_new;
 75     ++_snake.int_size;
 76 
 77     return 0;
 78 }
 79 
 80 Snake_Base* Snake::del_tail_node()
 81 {
 82     Snake_Base* pPre;
 83     Snake_Base* pCur;
 84     if(_snake.int_size == 1)
 85     {
 86         return NULL;
 87     }
 88     pCur = _snake.pNext;
 89     if(!pCur->pNext)
 90     {
 91         _snake.pNext = NULL;
 92     }
 93     else{
 94         while(pCur->pNext)
 95         {
 96             pPre = pCur;
 97             pCur = pCur->pNext;
 98         }
 99         pPre->pNext = NULL;
100     }
101     --_snake.int_size;
102     return pCur;
103 
104 }
105 
106 Snake_Base* Snake::forward()
107 {
108     Snake_Base* pReturn = NULL;
109     if(_snake.int_size > 1)
110     {
111         add_new_node(_snake.coordinate_cur, _snake.char_icon);
112         pReturn = del_tail_node();
113     }
114     else
115     {
116         pReturn = new Snake_Base;
117         pReturn->coordinate_cur = _snake.coordinate_cur;
118     }
119 
120     switch(_snake.int_direction)
121     {
122         case EN_DIR_UP:
123             _snake.coordinate_cur.int_x -= 1;
124             break;
125         case EN_DIR_DOWN:    
126             _snake.coordinate_cur.int_x += 1;
127             break;
128         case EN_DIR_LEFT:    
129             _snake.coordinate_cur.int_y -= 1;
130             break;
131         case EN_DIR_RIGHT:    
132             _snake.coordinate_cur.int_y += 1;
133             break;
134         default:break;
135 
136     }
137     return pReturn;
138 
139 }
140 
141 Snake_Base* Snake::get_snake_base()
142 {
143     return _snake.pNext;
144 
145 }
146 
147 SnakeHead* Snake::get_snake_head()
148 {
149     return &_snake;
150 
151 }
152 
153 int Snake::get_snake_size()
154 {
155     return _snake.int_size;
156 }
157 
158 }

  在   Snake::add_new_node(char char_icon_new)  方法中,只是简单地头插了节点,并没有设置从标什么的,是因为在前进的时候,是采用先增加节点,再删除最后一个节点的方式。这样,只有在一开始的时候,蛇会展示成只有一节,后面不会有问题。(此处可能表达不是很清楚)

  好了,来测试一下。

  运行程序

 1 [ccx@ubuntu ~/Retro_Snaker]$>./bin/game.exe 
 2 ++====================================++
 3 ||                                    ||
 4 ||                                    ||
 5 ||                                    ||
 6 ||                                    ||
 7 ||                                    ||
 8 ||                                    ||
 9 ||                                    ||
10 ||                                    ||
11 ||                                    ||
12 ||                  OO                ||
13 ||                                    ||
14 ||                                    ||
15 ||                                    ||
16 ||                                    ||
17 ||                                    ||
18 ||                                    ||
19 ||                                    ||
20 ||                                    ||
21 ++====================================++

  只有一个节点,然后随便按,比如"wwaass" 

 1 [ccx@ubuntu ~/Retro_Snaker]$>./bin/game.exe 
 2   ====================================++
 3 ||                                    ||
 4 ||                                    ||
 5 ||                                    ||
 6 ||                                    ||
 7 ||                                    ||
 8 ||                                    ||
 9 ||                                    ||
10 ||              OOOOOO                ||
11 ||              OO  OO                ||
12 ||              OO                    ||
13 ||                                    ||
14 ||                                    ||
15 ||                                    ||
16 ||                                    ||
17 ||                                    ||
18 ||                                    ||
19 ||                                    ||
20 ||                                    ||
21 ++====================================++

  继续,比如"ddddssaw"

 1 [ccx@ubuntu ~/Retro_Snaker]$>./bin/game.exe 
 2   ====================================++
 3 ||                                    ||
 4 ||                                    ||
 5 ||                                    ||
 6 ||                                    ||
 7 ||                                    ||
 8 ||                                    ||
 9 ||                                    ||
10 ||                                    ||
11 ||                                    ||
12 ||                    OOOO            ||
13 ||                    OOOO            ||
14 ||                    OOOO            ||
15 ||                                    ||
16 ||                                    ||
17 ||                                    ||
18 ||                                    ||
19 ||                                    ||
20 ||                                    ||
21 ++====================================++

  由于目前没有做碰撞检测,所以会出现一些有趣的现象,比如一直向右或者下移动,会把“墙”吃了,并且跑到场景外面去,如果一直左或者上,则程序会core。如果反复左右或者上下,则会展示成只有两节。

  现在,要把前进和方向控制分开了。想了许多办法,比如用epoll,写个定时器,或者用多线程,或多进程。比较简单的方法,是用一个子线程来等待输入,主线程只要死循环,定时前进就行了。

  game.h中增加方法

  

static void* pthread_func(void* p_args);

  game.cc中的改动也很小

 1 int Game::init()
 2 {
 3     Coordinate coordinate = _map.get_map_center_pos();
 4     _snake.init('O', EN_DIR_DOWN, 5, coordinate);    
 5     
 6     MapBase map;
 7     map.char_icon = 'O';
 8     map.int_type = EN_MAP_SNAKE;
 9     _map.set_map_val(coordinate.int_x, coordinate.int_y, map);
10 
11     _bool_is_need_exit = false;
12 
13     reset_random_point();
14     output_map();
15 
16     ::pthread_t pthid;
17     ::pthread_create(&pthid, NULL, Game::pthread_func, this);
18 
19     return 0;
20 
21 }
22 
23 int Game::loop()
24 {
25     while(!_bool_is_need_exit)
26     {
27         forward();
28         refresh_map();
29         //sleep(1);
30         usleep(100000);
31     }
32     return 0;    
33 
34 }
35 
36 void* Game::pthread_func(void* p_args)
37 {
38     Game* game_tmp = static_cast<Game*> (p_args);
39     while(!game_tmp->_bool_is_need_exit)
40     {
41         game_tmp->change_direction();
42     }
43 }

  测试结果这里就不发了。

  未完待续....

  github:https://github.com/ccx19930930/Retro_Snaker

原文地址:https://www.cnblogs.com/chinxi/p/7190225.html