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 ,在主线程中开了一个子线程运行音乐。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值