您的位置:首页 > 运维架构 > Shell

C++编写的linux shell上可运行的贪吃蛇游戏

2014-02-23 20:59 639 查看
无意中浏览到网页
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 ,在主线程中开了一个子线程运行音乐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  贪吃蛇 C++ Linux