上篇地址 :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 }
测试结果这里就不发了。
未完待续....