C++編寫的linux shell上可運行的貪喫蛇遊戲

無意中瀏覽到網頁 blog.csdn.net/jjzhoujun2010/article/details/6709827 ,一看是置頂的文章,是用C語言寫的貪喫蛇遊戲,覺得很好完,就看了看,粗略看了一下,代碼量不是很大,而且關鍵的算法也給了詳細的說明,由於自己更偏重C++,所以就有了用C++來改寫此遊戲的想法,於是就行動起來。前後大約一天半的時間把整個程序調試通過。

整個代碼可以從 https://github.com/lmdyyh/Snake 下載。下面就說說各個文件的作用:

Timer.h和Timer.cpp定義了遊戲中的定時器,時間到時會向內核發送SIGALRM信號,測試程序testSnake.cpp中定義了SIGALRM的信號處理程序,整個遊戲中只需要一個定時器,所以Timer採用了單例模式編寫。

Timer.h

#ifndef TIMER_H
#define TIMER_H
class Timer{
public:
   static void wrapUp();
   static int set_ticker(int n_msecs);
   static Timer* getInstance();
   Timer();
private:
   static Timer* _instance;
};
#endif

Food.h和Food.cpp定義遊戲中蛇的食物,當food被蛇吃了後,會隨機生成另一個位置,模擬生成另一個food,而實際遊戲中就只有一個food,於是還是採用單例模式。Food是個靜態類,裏面只能包含靜態成員,而我們又需要設置food的位置,於是就需要兩個set函數,set函數作爲static類成員函數時,會出現問題,因爲靜態類成員參數中會默認插入const  Food作爲this的實參,此時就丟棄了const限定符,編譯器會報錯,於是就需要在set函數後加上const修飾符,可是加上const修飾符後就不能設置數據成員了,此時就需要將位置數據成員用mutable修飾。

Food.h

#ifndef FOOD_H
#define FOOD_H
class Food{
public:
   static Food* getInstance();
   void dispFood()const;
   int getXPos()const;
   int getYPos()const;
   void setXPos(int)const;
   void setYPos(int)const;
protected:
   Food(int,int);
private:
   static Food* _instance;
   mutable int fx_pos;
   mutable int fy_pos;
};
#endif

核心程序是Snake.h和Snake.cpp。把一些與所有遊戲都相關的函數抽象到Game.h中形成一個抽象類,使Snake類繼承至Game類,Snake採用STL中的list雙向鏈表實現,構造函數中首先生成一個空鏈表,然後push_back一個std::pair<int,int>數據成員作爲蛇的初始位置,並且生成一個Food和一個Timer,Snake和Food、Timer採用組合的方式耦合。

Game.h

#ifndef GAME_H
#define GAME_H
class Game{
public:
   virtual void gameStart()=0;
   virtual void gameOver(int)=0;
   virtual void initGame()=0;
   virtual void keyControl()=0;
};
#endif

Snake.h

#ifndef SNAKE_H
#define SNAKE_H
#include <boost/noncopyable.hpp>
#include <list>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "Game.h"
#include "Timer.h"
#include "Food.h"
#include <signal.h>
using namespace std;
class Snake :public Game ,boost::noncopyable{
public:
   Snake(int ,int );
   ~Snake();
   void insertSnake(int,int);
   void shrinkSnake();
   void moveSnake();
   void gameOver(int);
   void gameStart();
   void initGame();
   void keyControl();
private:
   void initSnake();
   int x_pos; //position
   int y_pos;
   int length;//pairList's size
   int x_dir;
   int y_dir;
   int ttm;
   int ttg;
   bool moved;
   bool ate;
   typedef list<pair<int,int> > pairList;
   pairList* snakeNode;
   Timer *timer;
   Food *food;
};
#endif

具體的算法參考鏈接原文。這裏timer和food應該使用boost::shared_ptr來管理,防止資源泄漏。

最後說一下這個C++代碼中最困難的一點,就是在每當定時器時間到,向系統發射SIGALRM信號,這時就會調用我們用signal設置的信號處理函數,此處信號處理函數是使蛇移動,也就是調用Snake的moveSnake()函數,signal需要的是void (*)(int)的回調函數,而我們傳給signal的是void (Snake::*)(int)的回調函數,編譯器會報錯。這裏耽誤了很長時間,反覆實驗了幾種方法,最後通過將信號處理函數包裝才得以解決,如:

testSnake.cpp

#include "Snake.h"
#include <iostream>
using namespace std;
void Snake_memberFn_Wrapper(int n);
Snake snake(5,10);
int main(){
   signal(SIGALRM,Snake_memberFn_Wrapper);
   snake.gameStart();
}
void Snake_memberFn_Wrapper(int n){
   snake.moveSnake();   
}
聲明一個全局的snake,將真正的信號處理函數包裝進Snake_memberFn_Wrapper中,並將其作爲signal的處理函數即可,這樣內核收到SIGALRM後就會調用moveSnake了。

又用了一個下午加一個晚上給遊戲加了一個背景音樂,用的irrklang庫,詳見www.ambiera.com/irrklang/docu/index.html#tipsandtricks ,在主線程中開了一個子線程運行音樂。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章