上篇地址 :http://www.cnblogs.com/chinxi/p/7185309.html 有了一條會移動的“蛇”,就可以開始寫改變它方向的方法了。 由於這是運行在linux下的,沒有像windows下的getch()方法,想要輸入一個鍵,不輸入回車,就讓程式有響應,還是件麻煩事。 不過, ...
上篇地址 :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([email protected]) 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('\b'); 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([email protected]) 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([email protected]) 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