C++跨平台游戏开发之ClanLib SDK

2016-02-19 21:31 24 1 收藏

最近很多朋友喜欢上设计,但是大家却不知道如何去做,别担心有图老师给你解答,史上最全最棒的详细解说让你一看就懂。

【 tulaoshi.com - 编程语言 】

一、 简介 ClanLib是一个主要针对游戏开发者的跨平台C++框架。尽管API主要为游戏开发设计,你照样可以轻易地使用ClanLib来开发一个科学的3D可视化工具或多媒体应用程序(例如Gecko多媒体系统)。 ClanLib拥有各种API-2D和3D图形,声音,网络,I/O,输入,GUI以及资源治理。它还提供透明的OpenGL支持,因此你可以使用本机OpenGL命令而让ClanLib处理依靠于操作系统的窗口治理和其它一切事情。ClanLib通过DirectX或简单的Direct Media Layer(一平台独立的多媒体库)生成2D图形。ClanLib游戏主页上列举了约50多个开发非常成功的游戏,包括以2D和3D形式完成的难题、策略以及射手类游戏。例如,Asteroid Arena(见图1)使用了ClanLib和OpenGL技术,实现了胜人一筹的经典街机游戏。
  
  
  图1.Asteroid Arena屏幕快照
  ClanLib可以工作在Windows,Linux和MacOS操作系统之上,并且提供源码级的zip或tar文件支持。Windows开发者可以使用微软Visual Studio,Borland C++或者MinGW(小型GNU for Windows)编译器和环境。第三方的对于Ruby和Perl语言的绑定支持也是可用的。可选的特效程序包括一个Lua插件(流行的小脚本编程语言)和FreeType(一个免费的TrueType字体库)。
  
  二、 ClanLib特征集
  
  在具体使用API之前,让我们看一下ClanLib的主要特征:
  
  ·基本跨平台运行时刻库(GUI,多线程,文件I/O,等等)
  
  ·基于模板的C++信号/槽库(类型安全的回调/代理)
  
  ·综合的资源治理
  
  ·声音混合器支持。WAV文件,Ogg Vorbis,以及由MikMod库(MOD,S3M,XM,等等)支持的任何类型文件
  
  ·文档对象模型(DOM)XML分析器支持
  
  ·高级2D图形API,支持OpenGL,DirectX和SDL作为着色目标
  
  ·高性能的批量着色引擎,当用OpenGL着色2D时
  
  ·2D碰撞检测
  
  ·2D精灵动画支持
  
  ·高度可定制的GUI框架
  
  ·从低级到高级的网络库接口
  
  三、 ClanLib基本的游戏模型
  
  现在,让我们仔细分析一下ClanLib API模型。我发现最好的教程是一个完全自解释的示例程序。具体地,让我们分析一下Luke Worth的盒子游戏,这是一个有两个玩家的纸和铅笔游戏(见图2)。这个盒子游戏包含一些格子点,在任意两点间玩家都可以画线。谁用最后一条线画成一个封装的矩形,谁就得一分,并进入到下一轮中。
  
  
  图2.一个进行中的盒子游戏,得分情况是蓝8/红3
  我特意使程序的main函数尽可能简短,这样我们可能集中注重力于高亮处的"游戏循环":
  
  1 #include iostream
  2 #include ClanLib/application.h
  3 #include ClanLib/core.h
  4 #include ClanLib/display.h
  5 #include ClanLib/gl.h
  6 #include ClanLib/sound.h
  7 #include ClanLib/vorbis.h
  8
  9 const int boardsize = 6, spacing = 50, border = 20;
  
   10 const int numsquares = int(pow(float(boardsize - 1), 2));
  11
  12 enum coloursquare { off, blue, red };
  13 strUCt cursor {
  14  int x, y;
  15  bool vert;
  16 };
  17
  18 class Boxes: public CL_ClanApplication {
  19  bool ver[boardsize][boardsize - 1];
  20  bool hor[boardsize - 1][boardsize];
  21  coloursquare squares[boardsize - 1][boardsize - 1];
  22  bool redturn;
  23  bool fullup;
  24  cursor curs;
  25
  26  void inputHandler(const CL_InputEvent &i);
  27  bool findsquares(void);
  28  inline int numaroundsquare(int x, int y);
  29  void init();
  30  void drawBoard();
  31  void endOfGame();
  32
  33 public:
  34  virtual int Boxes::main(int, char **);
  35 } app;
  36
  37 using namespace std;
  40
  41 int Boxes::main(int, char **)
  42 {
  43  int winsize = spacing * (boardsize - 1) + border * 2;
  44  try {
  45   Boxes::init();
  46   while (!CL_Keyboard::get_keycode(CL_KEY_ESCAPE)) {
  47    Boxes::drawBoard();
  48    if (fullup) break;
  49   CL_System::keep_alive(20);
  50   }
  51   Boxes::endOfGame();
  52
  53   CL_SetupVorbis::deinit();
  54   CL_SetupSound::deinit();
  55   CL_SetupGL::deinit();
  56   CL_SetupDisplay::deinit();
  57   CL_SetupCore::deinit();
  58  }
  59  catch (CL_Error err) {
  60   std::cout "Exception caught: " err.message.c_str() std::endl;
  61  }
  62
  63  return 0;
  64 }
  关于这个应用程序,应注重的第一事情是main()函数(见行41)并不是一个最顶层的函数,而是嵌入到一个从CL_ClanApplication派生的对象中。该对象封装了不少难以避免的平台依靠性-这可能包含一个传统的::main()实现(例如在Win32应用程序中必须使用WinMain())。
  
  而且还应注重,事实上所有的可执行的代码(行43-58)被封装在一个try{}/catch{}异常处理器块中。假如需要的话,ClanLib将引发异常,你可以重启一游戏,等等。基本上,所有的游戏逻辑包含在init(),drawBoard(),endOfGame()和inputHandler()这几个方法中。假如board不再移动(fullup==true),则退出游戏循环(行48)。CL_System::keep_alive()更新所有的输入和系统事件(象关闭窗口或者移动它)。这在老式的Win16 API ::Yield()或者Linux上的sleep()中将会释放CPU周期。
  
  66 void Boxes::init()
  67 {
  68  CL_SetupCore::init();
  69  CL_SetupDisplay::init();
  70  CL_SetupGL::init();
  71  CL_SetupSound::init();
  72  CL_SetupVorbis::init();
  73
  74  CL_DisplayWindow window("Boxes", winsize, winsize);
  75  CL_SoundOutput output(44100); //选择44Khz采样
  76
  77  CL_Surface *cursimg = new CL_Surface("cursor.tga");
  78  cursimg-set_alignment(origin_center);
  79  CL_Surface *redpict = new CL_Surface("handtransp.tga");
  80  redpict-set_alignment(origin_center);
  81  redpict-set_scale(float(spacing)/float(redpict-get_width()),
  82  float(spacing)/float(redpict-get_height()));
  83  CL_Surface *bluepict = new CL_Surface("circlehandtransp.tga");
  84  bluepict-set_alignment(origin_center);
  85  bluepict-set_scale(float(spacing) / float(bluepict-get_width()),
  86  float(spacing) / float(bluepict-get_height()));
  87
  这里的init()方法完成大部分的游戏初始化工作。当然,在此需要ClanLib子系统以用于处理图形和声音(行68-72),然后构建一个窗口用于显示所有的图形(行75)。
  
  
  CL_Surface(行77-87)是一个2D位图类,用于绘制光标,用蓝色填充的方格和用红色填充的方格。
  
  TGA文件是一种位图文件格式。ClanLib有一个集成的PNG库,因此它可以读写最流行的位图文件格式化。
  
  下一步,你必须把板子初始化成一个空状态(行87-103)并执行类似的其它的清理工作以实现新的游戏计数器。
  
  89
  90 redturn = true;
  91 curs.vert = false;
  92 fullup = false;
  93 curs.x = curs.y = 1;
  94
  95 srand(CL_System::get_time()); //启动随机数字生成器
  96
  97 for (int x = 0; x boardsize - 1; x++) {
  98  for (int y = 0; y boardsize; y++)
  99  hor[x][y] = ver[y][x] = false;
  100
  101  for (int y = 0; y boardsize - 1; y++)
  102   squares[x][y] = off;
  103
  104   ClanLib的一个非凡突出的方面是它避开传统型应用于许多框架中的回调模型,而引入了"信号和槽"模型。这种模型广泛应用于Boost C++库中,并在QT中得到实现。信号代表具有多个目标的回调函数,又在一些类似的系统中称作"出版者"或者"事件"。信号被连接到一些槽上,它们是回调函数接收器(也称作事件目标或者订户),当信号被"发出"时即被调用。信号具有类型安全的优点,它们避开了在传统型的框架中的不可避免的cast操作。
  
  信号和槽被统一治理。在信号和槽中(或者更准确些说是,作为槽的一部分出现的对象)跟踪所有的连接,并当任何其一被破坏时能够自动地断开信号/槽连接。这能够使用户建立信号/槽连接而不需要花费多大的代价来治理那些连接以及所有包含于其中的对象的生命周期。在行105中,你只要捕捉所有的键击("down")事件并确保使用了你自己的inputHandler()(见行168-216)。
  
  105 CL_Slot keypress =
  CL_Keyboard::sig_key_down().connect(this,
  &Boxes::inputHandler);
  现在,你将开始初始化程序的音乐部分。首先,你用一个.wav格式的("binary")音乐文件装载一个CL_SoundBuffer,然后预备一个会话句柄以为玩游戏之用。下一步,你应用一个淡入淡出过滤器来异步地调整音量-在五秒(行 108-112)内把音量从零变化到最大音量的百分之六十。
  
  106 CL_SoundBuffer *music = new CL_SoundBuffer("linemusic.ogg");
  107 CL_SoundBuffer_Session session = music-prepare();
  108 CL_FadeFilter *fade = new CL_FadeFilter(0.0f);
  109 session.add_filter(fade);
  110 session.set_looping(true);
  111 session.play();
  112 fade-fade_to_volume(0.6f, 5000);
  113 }
  drawBoard()方法绘制线段所在的点画格子图案,如,每个玩家赢得的红色的西红柿和蓝色的矢车菊框出的方格,还有模拟的光标。而最重要的代码行是第165行。CL_Display::flip()交换前后台缓冲区。后台缓冲区是在该帧中你绘制所有图形的地方,而前台缓冲区是显示在屏幕上的内容。
  
  115 void Boxes::drawBoard()
  116 {
  117  CL_Display::clear(redturn ? CL_Color::red : CL_Color::blue);
  118  CL_Display::fill_rect(CL_Rect(border/2, border/2,
  119    winsize - border/2, winsize - border/2),CL_Color::black);
  120
  121  //画方框
  122  for (int x = 0; x boardsize - 1; x++)
  123   for (int y = 0; y boardsize - 1; y++) {
  124    if (squares[x][y] == red) {
  125   CL_Display::fill_rect(CL_Rect(x * spacing + border,y * spacing + border, x * spacing + border +
    spacing,
  127   y * spacing + border + spacing),CL_Gradient(CL_Color::red,
  128   CL_Color::red, CL_Color::tomato, CL_Color::tomato));
  129   redpict-draw(x * spacing + border + spacing / 2,
  130    y * spacing + border + spacing / 2);
  131    }
  132    else if (squares[x][y] == blue) {
  133    CL_Display::fill_rect(CL_Rect(x * spacing + border,
  134    y * spacing + border,x * spacing + border +spacing,
  135    y * spacing + border +spacing),CL_Gradient(CL_Color::blue,
  136    CL_Color::blue, CL_Color::cornflowerblue,CL_Color::cornflowerblue));
  137    bluepict-draw(x * spacing + border + spacing / 2,y * spacing + border + spacing / 2);
  139    }
  140   }
  141
  142   //画线
  
   143   for (int x = 0; x boardsize; x++) {
  144    for (int y = 0; y boardsize - 1; y++) {
  145   if (ver[x][y]) CL_Display::draw_line(x * spacing + border,
  146   y * spacing + border,x * spacing + border,
  147   y * spacing + border+ spacing,CL_Color::yellow);
  148   if (hor[y][x]) CL_Display::draw_line(y * spacing + border,
  149   x * spacing + border,y * spacing + border+ spacing,x * spacing + border,CL_Color::yellow);
  151   }
  152   }
  153
  154   //画格子
  155   for (int x = 0; x boardsize; x++)
  156    for (int y = 0; y boardsize; y++)
  157   CL_Display::draw_rect(CL_Rect(x * spacing + border,
  158   y * spacing + border,x * spacing + border + 2,159 y * spacing + border + 2),CL_Color::white);
  160
  161   //画光标
  162   if (curs.vert) cursimg-draw((curs.x - 1) * spacing + border,int((curs.y - 0.5) * spacing + border));
  163   else cursimg-draw(int((curs.x - 0.5) * spacing + border),(curs.y - 1) * spacing + border);
  164
  165    CL_Display::flip();
  166    }
  你安装的inputHandler()函数用于观察在行105的按键信号。这个函数负责处理细节问题-把键击变成游戏运动,还有最重要的空格或者回车键-用于指示当前玩家的一个选择(行200-210)。然后,你要检查一下是否已完成了一个"方形"并把控制返回到原来的玩家。
  
  168 void Boxes::inputHandler(const CL_InputEvent &i)
  169 {
  170  if (redturn) {
  171   switch(i.id) {
  172    case CL_KEY_LEFT:
  173    case CL_KEY_G:
  174   if (curs.x 1) curs.x--;
  175   break;
  176    case CL_KEY_RIGHT:
  177    case CL_KEY_J:
  178   if (curs.x boardsize) curs.x++;
  179   break;
  180    case CL_KEY_UP:
  181    case CL_KEY_Y:
  182   if (!curs.vert && curs.y 1) {
  183    curs.y--;
  184    curs.vert = !curs.vert;
  185   }
  186   else if (curs.vert) curs.vert = false;
  187   break;
  188    case CL_KEY_DOWN:
  189    case CL_KEY_H:
  190   if (curs.vert && curs.y boardsize) {
  191    curs.y++;
  192    curs.vert = !curs.vert;
  193   }
  194   else if (!curs.vert) curs.vert = true;
  195   break;
  196    }
  197    if (curs.x == boardsize && !curs.vert) curs.x--;
  198    if (curs.y == boardsize && curs.vert)
  curs.vert = false;
  199
  200    if (i.id == CL_KEY_SPACE i.id == CL_KEY_ENTER) {
  201   if (curs.vert) {
  202    if (!ver[curs.x-1][curs.y-1]) {
  203   ver[curs.x-1][curs.y-1] = true;
  204   if (!findsquares()) redturn = !redturn;
  205    }
  206    }
  207    else {
  208   if (!hor[curs.x-1][curs.y-1]) {
  209    hor[curs.x-1][curs.y-1] = true;
  210    if (!findsquares()) redturn = !redturn;
  211   }
  212    }
  213   }
  214  }
  215 }
  最后,由endOfGame()方法计算最后的得分。记住游戏还没有结束,直到板子满了为止(见行48)或者某人通过按下ESC键(见行46)退出。最后,你用大约1秒的时间把音量淡出到0。
  
  217 void Boxes::endOfGame()
  218 {
  219  // 计数得分
  220  int redscore, bluescore;
  221  redscore = bluescore = 0;
  222  for (int x = 0; x boardsize - 1; x++)
  223   for (int y = 0; y boardsize - 1; y++) {
  
   224    if (squares[x][y] == red) redscore++;
  225    else if (squares[x][y] == blue) bluescore++;
  226   }
  227
  228  cout "Red: " redscore "Blue: " bluescore endl;
  229  if (bluescore != redscore)
  230   cout (bluescore redscore ? "Blue" : "Red") " player wins";
  231  else cout "It was a tie";
  232
  233  if (fullup) {
  234   fade-fade_to_volume(0.0f, 1000);
  235   CL_System::sleep(1000);
  236  }
  237 }

来源:https://www.tulaoshi.com/n/20160219/1626419.html

延伸阅读
Stephan Lavavej提出了一个非常有趣也很尖锐的问题:“C++的未来在哪里?” 这个问题是有解的。没有哪个语言会成为永恒,不是吗?(尽管C语言现在依旧生机勃勃)我不希望C++在2017年,或者甚至在2057年也依然那么有活力。在计算机行业,50年已经是一个几乎不可思议的时间了;虽然到今年为止,晶体管已有60年的历史。所以,在我问“C++的...
一、功能 保证一个类仅有一个实例。 三、优缺点 Singleton模式是做为"全局变量"的替代品出现的。所以它具有全局变量的特点:全局可见、贯穿应用程序的整个生命期,它也具有全局变量不具备的性质:同类型的对象实例只可能有一个。 四、实现 教科书上的Singleton定义如下: class Singleton { public: static Si...
数据库开发:delphi一枝独秀 数据库支持是delphi的强项。这主要体现在delphi与bde的无缝集成,以及delphi提供的那一大堆现成的数据库操作控件。这是vc望尘莫及的。目前delphi支持bde、ado、interbase三种数据库访问方式。所有的方式都能拖拉到应用程序中实现可视化操作。正是因为delphi对数据库类的包装,使得用户操作数据库不像在vis...
标签: Java JAVA基础
使用Java语言编写应用程序最大的优点在于“一次编译,处处运行”,然而这并不是说所有的Java程序都具有跨平台的特性,事实上,相当一部分的Java程序是不能在别的操作系统上正确运行的,那么如何才能编写一个真正的跨平台的Java程序呢?下面是在编写跨平台的Java程序是需要注意的一些事情: 1.编写Java跨平台应用程序时,你...
应用框架:mfc?有kfc流行吗? 应用程序框架(application frame),有时也称为对象框架。visual c++采用的框架是mfc。mfc不仅仅是人们通常理解的一个类库。(同样,delphi的vcl也不仅仅是一个控件库,尽管它的名字叫“可视控件库”。)你如果选择了mfc,也就选择了一种程序结构,一种编程风格。mfc早在windows 3.x的时代就出现了,那时的...

经验教程

887

收藏

59
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部