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


      上篇地址 :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

  • 相关阅读:
    第一个Servlet项目(IDEA)
    Web交互基本流程以及HTTP协议详解
    mybatis中Mapper.xml配置详解
    认识mybatis
    SpringAOP
    Spring AOP
    70. Climbing Stairs
    位运算
    Leetcode分类
    21. Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/chinxi/p/7190225.html
Copyright © 2020-2023  润新知