自製貪吃蛇——方向控制,初始長度增加

来源:http://www.cnblogs.com/chinxi/archive/2017/07/16/7190225.html
-Advertisement-
Play Games

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近有接觸到關於visual studio 2017 擴展的開發,特此記錄,也是為了督促自己去深入瞭解其原理。 開始開發Visual Studio 擴展,在這裡我安裝了visual studio 2017, 在安裝的時候記得勾選上visual studio 擴展開發。 創建一個項目 我們打開編譯器, ...
  • 由於數據類型多,要按照逐個類型寫一個類型轉換的方法的話一是代碼量多,顯得累贅。 ...
  • 電商平臺 都會有搶購的情況,比如 1元搶購。 而搶購 最重要的 就是庫存,很多情況下 庫存處理不好,就會出現超賣現象。 本文將用redis為緩存,StackExchange 框架,消息隊列方式 實現分散式鎖的情況 一,效果 先看效果, 窗體下單 構建高併發情況 開多個控制台應用程式 處理訂單 二,配 ...
  • 在asp.net中常見的<%%>方式有如下幾種: <%%>、<%=%>、<%:%>、<%#%>、<%$%>、<%@%> <%%>:書寫後臺代碼,如C#、vb等代碼 <%=%>:計算=後面的表達式,並將結果輸出 <%:%>:將:號後面的內容編碼輸出,實際<%=Server.HtmlEncode()%> ...
  • 一、C#語言 C#是微軟在2000開始推出的一種面向對象的編程語言。 它簡單易學、類型安全,可以用非常簡潔易懂的代碼寫出理想的功能,大大提高了開發人員的工作效率,並降低了程式的維護成本。 作為一種面向對象的語言,C#支持封裝、繼承和多態等概念。 按照微軟一貫的語法糖作風,在C#後續的版本里,陸續推出 ...
  • 編碼 ASCII--0~127 65-A 97-a 西歐碼表 ISO-8859-1 0-255 1個位元組 gb2312 0-65535 gbk 2個位元組 Unicode編碼體系 utf-8 3個位元組 中 f bit位 Byte位元組 1Byte = 8bit 1KB=1024B MB GB TB PB ...
  • Java中的二進位及基本的位運算 二進位是計算技術中廣泛採用的一種數制。二進位數據是用0和1兩個數位來表示的數。它的基數為2,進位規則是“逢二進一”,借位規則是“借一當二”,由18世紀德國數理哲學大師萊布尼茲發現。當前的電腦系統使用的基本上是二進位系統,數據在電腦中主要是以補碼的形式存儲的。計算 ...
  • 走向開源! 這博客五月份做完,直到今天才發佈的原因是之前伺服器到期了,資料庫丟失沒有備份又重搞的才搞完也,每次想開放出來,讓更多人知道和提出寶貴建議,都會覺得自己的東西並不是那麼好,直到今天,我才覺得程式這東西,只有更多人體驗和反饋才會更好。 我始終不相信這世界上有這麼一句話:已經沒有比這更好的東西 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...