九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

站在巨人的肩膀上开发游戏(3) -- Orx 文字输出的简化及进一步学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

    在上一节构建了一个用Orx完成的Hello World程序,想起以前有的用N中方式完成Hello World程序的例子,也许这个算是其中最最复杂的了。有个问题问题在于Orx中文字的输出为了与Object一致,所以弄的非常麻烦,毕竟简单的文字完全没有Object那么多属性需要配置。(参考前一节)事实上,经过与iarwain的沟通,最后确认了简化的办法,那就是写自己的函数做为包装。做为前置条件,先学习Orx中config相关API的使用。

配置相关的API

    在Orx的WIKI上有个详尽的页面描
述此API。主要的API以orxConfig_SetXXX及orxConfig_GetXXX组成,比较简单。不举太多例子了。
section的处理也比较简单,push,pop用于使用某个section并且还原,seclect用于选择某个工作的section,但是有个特别的地方,这些API会在section不存在的时候,创建section。
这也是我需要的。

文字输出的简化

    下面就开始文字输出的简化,上一节分析了在Orx中输出文字为什么那么复杂,主要原因在于文字模拟了Object的创建,并享有Object的所有其他功能,所以一个简单的文字属性被分成了3段,这样就变得复杂了,我的思路是将所有的属性都放在一段中,并且还是走原来的路,按照Object的创建方式来创建,事实上,因为orxObject_CreateFromConfig的实现比较复杂,就不重复其原有步骤了,我通过从配置文件中的一段配置,动态构建出此API需要的三段,然后再用原API来创建,这样虽然效率上可能会低一点,但是最大程度的利用了原有函数。

简单的例子,原有的HelloWorld例子中,HelloWorld的配置就有3段,如下:

[HelloWorld]

Graphic              =
 HelloWorldGraphic

[HelloWorldGraphic]

Text                 =
 HelloWorldString
Color                =
 (255.0, 0.0, 0.0)

[HelloWorldString]

String               =
 "HelloWorld"

事实上,我们需要的有效内容就只有2个
Color                =
 (255.0, 0.0, 0.0)


String               =
 "HelloWorld"

也就是说,我希望通过

[HelloWorld]

Color                =
 (255.0, 0.0, 0.0)
String               =
 "HelloWorld"


这样的配置,就能达到原有的效果。想想,7行配置,结果只有3行有用,其他4行都是浪费的无谓link和section,怎么说都无法忍受。为了完全还原原有效果,并且使其名字也能一样,只需要这样使用Orx的配置API即可。


orxOBJECT *CreateText(orxSTRING _zTextSection)
{
    orxConfig_PushSection(_zTextSection);
    orxConfig_SetString("Graphic"
, _zTextSection);
    orxConfig_SetString("Text"
, _zTextSection);

    orxOBJECT *pstReturn = orxObject_CreateFromConfig(_zTextSection);

    orxConfig_PopSection();

    return
 pstReturn;
}

也就是说,通过将需要的Graphic和Text段都连接到自身,这样的config使用方法的想法,完全来自于iarwain.......我只能说,简化了太多太多东西,小小的INI配置,竟然能够玩弄的这样出神入化,可能是MS都无法想象到的。。。。。。。。。。。。。。

进一步学习

    其实在Orx中普通的文字与Object共享了太多的东西,讲的太多,就会出现我前面讲的情况,因为讲解一个API而贯穿了整个Orx,这里仅仅提出几个特别的配置来说明。(虽然说是特别的配置,但是并不是对文字特别,也完全适用于普通的object,仅仅表示比较有用)

位置

    首先,Position属性,表示位置。提到Position,又得将Orx的世界坐标系讲一讲,因为比较特殊。
Orx作为一个2D引擎,没有完全的使用屏幕坐标系,而是将屏幕坐标系移到了屏幕的中心点,(严格来说是创建viewport的中心点,以下都以此方式表述)也就是说,以屏幕中心点为原点,右边为X的正轴,下边为Y的正轴。
并且,因为Orx使用了Z buffer来解决遮挡的问题,还有Z轴坐标,Z轴坐标是从屏幕外指向屏幕内的。也就是屏幕外为负,屏幕内为正。
起码,在默认情况下,Orx的世界坐标就是这样。于是,Position的使用方式来了。指定坐标就可。

比如,原来我没有指定任何Position,那么就默认在原点创建了文字,我现在指定到-100,-100,就表示文字显示在离屏幕中心点,左100像素,上100像素的位置显示文字。如下图:

中心点

    其实,对于文字来讲,有很多排版问题。比如向左对齐,向右对齐啥的,对于object来说就是中心点的问题。这里文字可以利用中心点来完成排版。当然,多行文字的问题就更加复杂了,需要手动排版。首先看属性Pivot
Pivot = center(+truncate|round)|left|right|top|bottom|[Vector]; NB: Truncate and round will adjust pivot values if they are not integers; z is ignored for 2D graphics;

将HelloWorld的配置设为下面这样时:

[HelloWorld]

Color       =
 (255.0, 0.0, 0.0)
String      =
 "HelloWorld"
Position    =
(0.0, 0.0, 0.0)
Pivot       =
 center


显示效果如下图:

与没有设定中心点时比较一下:

可以发现,默认的时候,中心点是在左边的。可以选择配置的选项在上面的说明中都有了,并且允许组合,比如left top, left bottom,天哪,不可思议吧。。。。。。。。

甚至,你可以缩放和对其富裕初速度。。。。。。。。。

[HelloWorld]

Color       =
 (255.0, 0.0, 0.0)
String      =
 "HelloWorld"
Position    =
 (0.0, 0.0, 0.0)
Pivot       =
 left + bottom
Speed       =
 ( 10.0, 0.0, 0.0 )
Scale       =
 2.0


大家自己去尝试吧,要知道,你可以将HelloWorld显示成各种各样的样子,却不用改变一行代码,也不用再次编译程序了,只需要改变配置。。。。。。。现在还没有好用的编辑器,很难想象,做个好用的编辑器后Orx会怎么样。。。。。。。。。。。。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

站在巨人的肩膀上开发游戏(2) -- Orx入门引导及Hello World

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

    关于Orx的使用,iarwain自己写过一系列非常详细的教程 ,推荐每个学习Orx的人阅读之。此外,学习Orx的热心人士Grey也写过一个小教程:Grey's tutorials
    开始写本系列的时候,不打算是上述教程的翻译,(iarwain表示很期待有人将其翻译成中文。。。。。有意向的可以与我联系,我会尽可能的提供帮助,虽然我对Orx的了解并不算透彻),也不准备重复上述教程中提到过的东西,也不是以替代上述教程为目的。想以完成实际的一个游戏为脉络,但是具体是以Orx的使用为主,(那么会偏重于各个配置),还是以Orx的API使用为主,或者探求Orx的内部,探寻实现原理,在这上面笔者比较矛盾。。。。。。。。。唉。。。写点东西纯属个人学习过程中的副产品,个人爱好而已,有的时候想法太多反而混乱,很想没有拘束的乱写一气,想到哪写到哪,但是又怕写的像当年边学边写的《Win32 OpenGL系列 》一样乱七八糟,错误百出。。。。。。。推荐所有人看过原iarwain教程并对Orx有一定了解后再看本系列文章,不然难说看完后我俩谁更混乱。本系列仅仅作为一个完整的Orx游戏制作参考+一些源码导读吧。

版本选择

    在原来的《站在巨人 的肩膀上开发游戏(1) -- orx 库简单介绍 》中已经简单介绍过Orx了。
    因为最近orx更新较快,但是API已经稳定,对于Win32平台,这里我推荐使用的是iarwain(Orx主要维护者)发布的稍微老一点的稳定版,orx1.1 是iarwain编译好的版本,可以拿来就用。 此版本有着较稳定的对Win32平台的支持。 作者还发布过1.2的beta版本 在此版本中,正式提供了对IPhone的支持。这里有SVN上的最新版 ,大家可以到http://orx.svn.sourceforge.net/viewvc/orx/trunk/上去下载,或者,用svn checkout https://orx.svn.sourceforge.net/svnroot/orx /trunk,这些最新的版本虽然添加了新的对IPhone,IPad的支持,但是就我在Win32试用的情况来看,还是有很多不稳定的情况,甚至有一次我编译都失败了,作者也正在将orx从SFML迁移到SDL,这也是个较大的工程,所以,我个人还是推荐大家暂时使用作者发布的1.1版本(PC)或原1.2的beta版本(IPhone),等待作者发布正式的1.2版本时再去使用新版本。iarwain曾经预计在5月末期发布orx1.2,不过因为iarwain突然想要添加一些新的功能 ,延期了,现在iarwain预计在6月下旬发布1.2版本,他说,"very soon now...."。

工程建立

    我不希望所有的教程都像原来的Orx教程一样,仅仅是通过动态plugin的方式或者仅仅包含Orx Lib的方式,我希望教程与Orx源代码在一起,方便debug 进Orx的源代码和做游戏时查看Orx的源代码。这里我自己选择的是orx1.1的源代码版本 。然后,我的所有教程源代码 都会在Google Code的上托管。大家按需checkout,我还是使用了mercurial
    工程创建方面的知识本来是不与Orx特别相关的,这里仅仅提到几点特别的地方,工程中SDL的部分在1.1中并没有使用,这是iarwain将来的1.2版本使用的。Orx本身的源代码有好几个选项,这与Orx支持的一些特殊功能有关,使用时需要特别注意自己需要的到底是什么,比如我上面的工程使用的就是Embedded Dynamic Debug/Release。另外,因为SDL和SFML库的版权问题,(LGPL)推荐使用Embedded Dynamic Debug的方式,这样你才可以闭源发布自己的软件。而在IPhone平台,iarwain没有使用SDL,所以你可以完全包含Orx并闭源发布。
    在https://jtianling-orx--template-1-1.googlecode.com/hg/上,就是一个附带orx,box2d源代码的工程,(也包含了所有必要的lib和dll)可以很方便的进行调试。修改好工作路径后,就可以直接编译运行了,用的是iarwain的standalone(教程10stand alone & localization )的例子。

Orx的设计(纯个人看法)

    说在Hello World前面的话:仅仅想学Orx使用的人完全可以忽略,只要做到Orx 的 Hello World会比较复杂的心理准备即可。以下仅仅解释为什么会这样。
    在Orx中Hello World的例子已经算是很复杂了,同时也说明了作为一个有一切完备野心的游戏引擎Orx的复杂之处。即,为了让Orx尽量的做更多的工作,为了让更多的东西可配置,事实上,Orx很难简单的作为一个库来使用,比如,简单的用一条Print语句让Orx在屏幕上显示一个Hello World。更进一步说,你要么用Orx,要么不用,很难说,我就想要Orx的某一部分,比如,就用Orx的图形显示部分,其他的东西都不要,你很难做到。
    从这点上来讲,事实上,Orx本身的设计是比较缺乏正交性的,也就是说内部各模块之间关联是比较紧密的,以各种方式耦合在一起。也许你仅仅调用了一条orxObject_CreateFromConfig API语句,然后Orx使用了配置模块,读取配置,然后使用io模块,读取材质文件,使用display,render模块,显示物体,使用物理模块,模拟物理,使用FX模块来完成你配置的特效,甚至同时还使用了animation模块来播放你配置的动画,并用声音模块播放声音。简单的说,我要想将整个orxObject_CreateFromConfig的调用流程全部走一次,可以看到几乎整个Orx...........你没有看错,我说的就是一个API调用。初学者往往会在这里懵掉。
    从根本上来讲,问题的关键在于配置,因为从最开始,iarwain就打算将Orx做成数据驱动的(实际是config驱动的)游戏引擎,所以,必然的,一切可配置的东西就和配置耦合在一起了。然后,为了能够在配置中能够简单的描述,iarwain提供了一个非常高级的抽象,object,object能够表示上述几乎所有模块的配置,于是乎,object也陷进了强耦合性的深渊了。。。。。最后,也就形成了上面的情况,一个orxObject_CreateFromConfig调用,你实际可能是在调用整个Orx的各个模块。
    不知道iarwain是否考虑了到了这一点,所以在Orx中提供了plugin的方案,使得你可以替换掉内部使用的一些东西,比如图形plugin,物理引擎plugin等等,但是,这还是不能改变其本质,你无法不使用配置和object。
    但是,随着更进一步的了解Orx,和查看Orx的源代码,会发现Orx本身的设计会比你想象的要精妙。配置,问题的关键在于配置,iarwain想要配置主宰一切,就无法避开配置与一切关联的事实,然后,其使用了object来接管整个配置,也就接管了整个引擎。再然后,iarwain尽量的保持了下面的模块的正交性,虽然也许很多模块都会有一个XXX_CreateFromConfig接口,但是除了这个以外,大部分模块完全独立,由object来使用一切,然后由配置来控制object。其实也有一些例外,比如viewport,camera等,但是,总体效果上,Orx的引擎可以看成是大的层次结构 [ 配置 -> Object -> 其他模块 ],从这个角度上来看,Orx的分层次设计又是很优美的。
    最后,从总体上来讲,作为一个游戏引擎,而不是一个游戏库来开发的Orx,自动的做了太多太多的工作,(Orx说他不喜欢重复的工作),于是乎,对于初学者来说,比如我,常常会碰到一个问题,也许是配置的问题,然后很难很难去发现和理解为什么出问题,再然后就非常痛苦的去看源代码,而这个过程,由于经过了太多层的转换,由于Orx做了太多的工作,会非常漫长。然而,当你熟悉这些配置后,你会发现,你能非常快的完成你的工作,非常的惬意。很多时候,一行代码+数行的配置,就能完成非常非常多的工作。也许,将来实现一个配置文件生成工具后,会更加惬意。
    这就像你通过一个自动配置,然后看着华丽的自动化操作流水线自动的生产出了一台又一台异常华丽的汽车,最后你却发现汽车无法发动。你只能从流水线的设计上一个环节一个环节开始研究为什么。
    而平时我们的工作常常是在完全手工的操作一个个流水线环节,当你焊接并安装上汽车的油箱的时候,你总会知道,新生产出来的汽车还是没有加油的。
    Orx就像这个华丽的自动化操作流水线。。。。。。。。。。。。

INI配置语法

    INI不是Json,不是XML,语法那个简单啊,用过Windows配置的,用过Linux众多软件配置的总会了解一些。但是iarwain在INI的基础上对INI进行了很多扩展。在Orx的WIKI上有关于Ini的详细的介绍 。基本上看过一遍就应该知道了。

应用程序的运行方式

    在Orx中,可以通过将自己的应用程序编译成动态库,然后通过Orx加载的方式,来实现快速的开发,(这种方式现在似乎比较流行),因为这个过程省下了编写的应用与Orx库的链接过程,所以可以用于加快开发速度。(其实与Orx的链接时间实在也算不了多久,毕竟Orx其实很小,像OGRE那样的大工程才的确需要这样的机制)几乎所有的iarwain的教学例子都是如此。
    与此对应的,将Orx的代码直接链接进自己的应用,那就叫standalone程序了。我个人比较习惯这样的开发方式,并且习惯将Orx的源码建立在同一个解决方案中,以方便查看和修改。前面提到的我建立好的工程即是如此。并且,在IPhone开发中,没得选择的,必须使用此方式。
    配置,是Orx的很大一部分,这里不得不提及,Orx在启动时会自动的载入与应用程序名相同的ini,比如,运行orx.exe会自动载入orx.ini,按照我们的习惯,debug版本时,在应用程序后加d,编译成orxd.exe时,自动载入的是orxd.ini。此时,Orx config的include就能发挥作用了,一般的做法是只做一个实际的orx.ini配置,然后orxd.ini中include orx.ini。
    另外,特别注意的是,这种载入是默认的操作,并且是必须的。虽然Orx中有API orxConfig_Load可以手动载入配置,但是因为Orx 的辅助函数orx_Execute,(一般的使用方法)在启动初始化各模块时就使用了配置的Display段的信息来创建窗体,而手动载入配置总是会晚于这个过程,所以在Orx中必须有默认的ini,并且,必须有合适的display字段和physics字段的配置,不然在debug模式下box2d会出现断言。(主要问题出在physics的初始化上),除非你自己写一个新的类似orx_Exccute的函数,自己手动处理主循环,各模块的初始化,显示的设置。个人感觉这是个bug。。。。。。。。已经在Orx的论坛上发帖询问了,看看再说。(BTW:由于iarwain如此的热心,你有任何疑问都可以在Orx的论坛上发帖问,几乎总是会得到回应,iarwain那是知无不言言无不尽啊。。。。。)

Hello World

    在了解了这么多之后,总算可以开始学习怎么用Orx来显示Hello world了。要显示Hello World,首先需要创建出合适的窗体,并了解Orx中的一些主要配置。可以参考此WIKI

Display

    display是其中最主要需要了解的。

[ Display]
Decoration = <bool>  ;是否显示窗体的外观,比如标题,边框等。
FullScreen = <bool>
ScreenWidth = <int>
ScreenHeight = <int>
ScreenDepth = <nt>
Smoothing = <bool>  ;是否扛锯齿
Title = <string>
VSync = <bool>

其他的都很好理解了。

其他辅助配置

[Render]
ShowFPS = true; NB: Displays current FPS in the top left corner of the screen;

简单的用于显示FPS。太方便了。。。。。。。。。。。我不知道在不同场合用不同的方法实现此功能多少次了,iarwain能将此功能整合进引擎,实在是善莫大焉。

[Clock]
MainClockFrequency = 20
只用于没有垂直同步的情况。不然一旦设置,会消耗大量的CPU,一般情况不要有此配置段即可。

有了上面这些,我们已经可以创建一个窗体了。。。。。。。
调用一条语句:
orx_Execute(argc, argv, Init, Run, Exit);
给出3个空的回调即可。。。。。。

我这里给其包装了一下,全部整合进一个单件的GameApp中。

全部源代码可以整合进一个文件中,也不大:

#include "orx.h"

#include <iostream>

class GameApp
{
public :
  static  orxSTATUS orxFASTCALL  EventHandler(const  orxEVENT *_pstEvent);
  static  orxSTATUS orxFASTCALL  Init();
  static  void  orxFASTCALL       Exit();
  static  orxSTATUS orxFASTCALL  Run();

  GameApp() {};
  ~GameApp() {};

  static  GameApp* Instance() {
      static  GameApp instance;
      return  &instance;
  }

private :
  orxSTATUS                     InitGame();
};

// Init game function
orxSTATUS GameApp::InitGame()
{
  orxSTATUS eResult = orxSTATUS_SUCCESS;
  
  // Creates viewport
  if  ( orxViewport_CreateFromConfig("Viewport" ) == NULL  ) {
      eResult = orxSTATUS_FAILURE;
  }

  // Done!
  return  eResult;
}

// Event handler
orxSTATUS orxFASTCALL GameApp::EventHandler(const  orxEVENT *_pstEvent)
{

  // Done!
  return  orxSTATUS_SUCCESS;
}

// Init function
orxSTATUS GameApp::Init()
{
  orxSTATUS     eResult;
  orxINPUT_TYPE eType;
  orxENUM       eID;

  /*  Gets input binding names */
  orxInput_GetBinding("Quit" , 0 , &eType, &eID);
  const  orxSTRING zInputQuit = orxInput_GetBindingName(eType, eID);

  // Logs
  orxLOG(" /n - ' %s ' will exit from this tutorial"
         " /n * The legend under the logo is always displayed in the current language" , zInputQuit );

  orxLOG("Init() called!" );

  // Inits our stand alone game
  eResult = GameApp::Instance()->InitGame();

  // Done!
  return  eResult;
}

// Exit function
void  GameApp::Exit()
{

  // Logs
  orxLOG("Exit() called!" );
}

// Run function
orxSTATUS GameApp::Run()
{
  orxSTATUS eResult = orxSTATUS_SUCCESS;

  // Done!
  return  eResult;
}

// Main program function
int  main(int  argc, char  **argv)
{
  // Inits and runs orx using our self-defined functions
  orx_Execute(argc, argv, GameApp::Init, GameApp::Run, GameApp::Exit);

  // Done!
  return  EXIT_SUCCESS ;
}

#ifdef __orxMSVC__

// Here's an example for a console-less program under windows with visual studio
int  WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int  nCmdShow)
{
  // Inits and executes orx
  orx_WinExecute(GameApp::Init, GameApp::Run, GameApp::Exit);

  // Done!
  return  EXIT_SUCCESS ;
}

#endif  // __orxMSVC__

并且,封装后,使用起来就像传统的C++方式了,另外,还可以通过clock来添加Update函数,当然,这是后话。
并且,上面演示了不从命令行运行的方式,即开启宏__orxMSVC__ 的方式,此方式下,会失去命令行,作为发布。但是也失去了日志输出,所以推荐调试时还是有命令行的为好。
创建后的窗体能在左上角看到FPS的显示。

viewport

    Orx中的viewport如同3D中的viewport概念,可以参考《Win32 OpenGL编程(10) 视口变换 》,Orx WIKI中的配置详细讲解


[ViewportTemplate]
BackgroundClear =  <bool>
BackgroundColor =  <vector>
Camera =  CameraTemplate;
RelativePosition =  left | right | top | bottom
Position =  <vector>
RelativeSize =  <vector>
Size =  <vector>
ShaderList =  ShaderTemplate1#ShaderTemplate2
Texture =  path/to/TextureFile

    同样的,也可以通过viewport来实现一屏的多显。达到《Win32 OpenGL编程(10) 视口变换 》中的效果,而且iarwain的官方教程中就有这样的例子。(例5
要想在屏幕上显示个什么东西,确定viewport是必须的,不然Orx不知道该在哪个位置显示,但是,实际的使用会更加简单,因为Orx中的配置都是有默认值的,比如viewport一般而言。可以使用默认值,也就是说,当前的全窗口作为viewport。后面会讲到一些更复杂的应用,这里暂时一笔带过吧,毕竟还是Hello World阶段。。。。。。。。。。简单的说,viewport就是描述了在什么地方显示图形的问题。

camera


[CameraTemplate]
FrustumHeight =  <float>
FrustumWidth =  <float>
FrustumNear =  <float>
FrustumFar =  <float>
Position =  <vector>
Rotation =  <float>
Zoom =  <float>

    Orx WIKI中的配置详细讲解
    viewport配置中会绑定一个camera,此camera决定了前后剪裁面等信息(如上配置所示),因为Orx是2D引擎,事实上是没有使用透视投影的,Orx使用的是正投影。前后剪裁面的设定决定了视景体剪裁的范围,这些与一般的概念类似。不明白的推荐看看OpenGL相关的知识。简单的说就是在此范围外的东西根本就不会显示。这里Orx将一些3D概念引入了2D引擎,其实增加了一般人的理解负担,个人认为绝大部分情况,其实一般的2D游戏开发者可以暂时忽略这部分,然后稍微将视景体的前后剪裁平面调大点,平时设置Object的时候注意Z轴的坐标位置不要超过此区域即可。

其实简单的说,Camera描述了你想要从哪个位置观察viewport显示的图形,并且描述了你观察的范围。

有个图对于正交视景体的描述很有帮助,来自于红宝书的在线网页。可以看到,Camera的配置其实就是对应描述了下面图中的各个值。

特别需要提及的是,前后裁剪面是半开区间,也就是说,图形显示的范围事实满足此公式: near < pos <= far。特别特别注意,虽然远平面上的物体会被显示出来,但是近平面的是不会的!

 

一般时候,如下的配置就已经很合适了。
[Viewport]
Camera          =  Camera
BackgroundColor =  (0, 0, 0)

[Camera]
; We use the same size for the camera than our display on screen so as to obtain a 1:1 ratio
FrustumWidth  =  @Display.ScreenWidth
FrustumHeight =  @Display.ScreenHeight
FrustumNear   =  0
FrustumFar    =  2.0
Position      =  (0.0, 0.0, -1.0)
Zoom          =  1.0

在上面的例子中,Camera的显示范围大小就是在Display中设置的屏幕大小,前后剪裁面分别是距离摄像头0, 2.0,这是个相对值,相对于摄像头的位置而言的Z轴, 也就是说,上面的配置中,因为Camera的位置是-1.0,所以,其实前后剪裁面的Z轴绝对位置是-1.0和1.0。 需要特别注意。 只要在设置object位置的时候,保证在此范围内,就没有问题。

Text显示

    天哪,当我需要来讲解Orx的时候,我才发现其复杂性。。。。。。因为大量的东西可配置,所以大量的东西都需要配置,这是很郁闷的,事实上,也许说明了Orx还不够成熟,默认配置还不是足够的好。总算可以开始显示文字了,到这个部分也还不简单啊。一个Hello World需要下面3段配置。

[HelloWorld]
Graphic              =  HelloWorldGraphic

[HelloWorldGraphic]
Text                 =  HelloWorldString
Color                =  (255.0, 0.0, 0.0)

[HelloWorldString]
String               =  "HelloWorld"

配置讲了那么多,其实整个的游戏代码可以非常少。。。。。。
真正需要的是在Init的时候用下面几行代码即可。

// Init game function
orxSTATUS GameApp::InitGame()
{
  orxSTATUS eResult = orxSTATUS_SUCCESS;
  
  // Creates viewport
  if  ( orxViewport_CreateFromConfig("Viewport" ) == NULL  ) {
      eResult = orxSTATUS_FAILURE;
  }

  if  ( orxObject_CreateFromConfig("HelloWorld" ) == NULL ) {
      eResult = orxSTATUS_FAILURE;
  }

  // Done!
  return  eResult;
}

orxViewport_CreateFromConfig 用于从配置中创建Viewport,orxObject_CreateFromConfig 用于从配置中创建Text。
至此,Hello World算是完成了.

全部源代码可以在http://code.google.com/p/jtianling/source/list?repo=orx-sample&r=hello_world浏览或clone后,update到tag hello_world

 

 


在Text的创建上面,我与iarwain沟通过,我认为仅仅是创建一个文字,这样的操作繁复了,三段配置其实大部分内容都是无用的,但是iarwain的意思是他是为了尽量维护text与Object的统一,如上所示,创建text的函数都是orxObject_CreateFromConfig ,这样Text就能使用额外的很多对Object使用的函数,比如Fx啥的。
不过,我还是觉得太麻烦了,当我用这样的方法创建了一个主菜单以后,更加如此觉得,我决定添加一个便利函数,来完成这样的工作,不过放到下一篇中吧。

小结

    全文到此也算结束了,一个Hello World牵涉到的东西之多,也许让很多人望而却步,的确,Orx在最开始的配置上,有点过于繁复了,一方面是因为Orx需要将大量的配置放出来以方便配置,另一方面,又没有提供足够好的默认配置,使得一开始的配置较多,但是,反过来说,这些都是配置上的问题,你可以从一些教程的配置的基础上开始工作,代码量其实是非常小的,上述例子中还有几行代码,那都是因为我对Orx的封装,实际的代码也就两个API调用。至于用代码更加容易理解,还是用配置更加容易理解,那就是见仁见智的问题了,我个人其实是更倾向于代码更加容易理解的,但是配置总的来还是方便。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

Orx 1.2版本前瞻 附带iarwain对SFML以及SDL的评价

    Orx不是一个非常成熟的引擎,我一直这样觉得,相对于很多成熟的引擎,Orx还有很多功能上的缺失,还缺少很多有用的游戏概念的添加,但是,我们能够看到作者iarwain的不懈努力。他曾经提到,他在Ubisoft工作之余,一边要带孩子,一边将其他所有的业余时间全部都投入到了Orx的开发中。。。。。而且,iarwain开发Orx,仅仅是for fun.....

    在1.2版本中,iarwain添加了新的功能,在官网上如此描述“

custom bitmaps support and UTF-8 support.

”,呵呵,UTF-8

是个作者没有定在1.2版本Road map中的功能,仅仅是因为来自东亚(比如中国)人对Orx的增多的兴趣,而决定在新版本中添加的。。。。。^^

    另外,虽然作者在最新的news中没有提及,但是我知道1.2版本的新的其他改动,对IPhone,IPad的支持,以及其他系统的版本从SFML库改到了SDL这个更为成熟,大家也更加熟悉的库。

    这里顺便提及SFML与SDL的比较,不浪费任何有用的经验:)作为iarwain这样一个经验丰富的程序员,他对SFML与SDL的比较时,这样描述SFML,buggy,但是实现的特性更加多,可惜有很多是游戏引擎本身就会实现的,所以事实上多出的特性很多有重复了,由于使用C++及面向对象的方式提供接口,对于使用C++的人来说,会更加易于使用。。。。当然,buggy一词,完全的打消了我对SFML的兴趣。而SDL,提供的接口更加底层,效率更高,(因此,1.2版本的Orx效率也会更高),更加成熟,稳定。

阅读全文....

纯八卦一下。。。。。Orx的名字来源于一个沼泽。。。。

本来想问iarwain(Orx维护者)Orx
是啥意思,然后可以给他取个中文名,以利于在宣传。结果他说,means nothing............

Orx的名字来源于他老家的一个沼泽。。。。。。。还给我发了一张图片。至于为啥要用一个沼泽的名字给一个游戏引擎命名,他说,just like it.......看来还是很难适应外国人的思维。。。。。。。

就像我看firends里面成天oh my GOD,oh my GOD,oh.......my.......GOD的时候,真的与一个外国人沟通的时候,说了句oh my GOD,他还要说她是基督徒,希望我能说oh my 或者oh my goodness............无语了。

 

 

 

 

风景倒是不错。。。。。。。。不过我说,用沼泽给一款游戏引擎命名感觉比较怪,容易给人不好的联想,iarwain倒是没有反对。。。。。。废话嘛,用这个引擎就像进入了一个沼泽地一样,直接就陷进去了。。。。。要么就往好的方向想想?无法自拔?-_-!死路一条。

阅读全文....

站在巨人的肩膀上开发游戏(1) -- orx 库简单介绍

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

为什么我们要从现成的游戏引擎学习开始

    很多人问过我类似的问题,学习程序该从什么入手?怎么样开始编写一个游戏?实际上的游戏开发都是从OpenGL开始的吗?学了OpenGL后怎么开始做游戏?

    这些问题可能很难有什么标准答案,不过个人认为,在学习一门编程语言后,开始真正的做一些东西是很重要的,并且,这些东西要上一定的规模,那种玩具式的开发虽然也能学到一些东西,但是因为其过于简单,会掩盖很多随着规模变大而碰到的问题,这样也就会让你无法获得真实开发中获得的经验及教训。经验,那是作为一个程序员最为宝贵的财富。

    游戏的开发,很少有公司/个人会真的从零开始开发,得益于开源运动的努力,现在已经有很多开源的游戏引擎可以使用,从3D中最为著名的OGRE,irrlicht,2D的HGE。都是其中的佼佼者,从这些引擎的基础上开始自己的工作,会让你事半功倍,就像站在巨人的肩膀上,能看的更高,更远,更加专注于自己关心的游戏逻辑模块。诚然,假如需要真的对一款游戏引擎了解透彻,并掌握自己开发游戏引擎的技术,从零开发的经验是很重要的,但是对于大多数人来讲,即使是学习,先采取从上之下的学习方式,(即先学会使用一款引擎,然后再深入了解这个引擎的原理)也会轻松愉快很多。有了一定的对引擎的经验后,再去尝试开发自己的引擎,那会使你受益匪浅。假如一开始就做太大规模的尝试,很可能使得你有太强的挫败感,毕竟,这是游戏编写最难的部分,人都是需要先学会走路然后才学会跑的。

    另外,我选择从2D引擎Orx开始,也是出于这样的考虑,虽然事实上,也许我对Irrlicht的使用经验比对orx的使用经验还要多。

Orx介绍(http://orx-project.org/)

Orx的LOGO,有点像我们常提及的orz。。。。。




    Orx不是世界上最优秀的2D游戏引擎,也不是最流行的一个,事实上,Orx还有太多不成熟的地方,我也常常对这些地方感觉非常郁闷,由于Orx的小知名度,所以周边的模块开发,文档等也非常少,这样极大的限制了其传播,并且极大的限制了其发展。

    但是,我还是很喜欢Orx,因为它很有特点。

Orx以配置为基础

    以配置为基础,所以使得Orx非常的灵活,可以进行快速的开发,快速的实验,快速的调整,并且因为配置文件模块写的比较强大,你也很容易添加进自己的配置,这点我非常喜欢,我一直很喜欢一句话,lua之父说:只有当配置的使用足够的简单,人们才会乐于使用它。虽然此话是针对lua的,但是对于任何配置的使用都适用,Orx就有这样的特质。这也是Orx最大的特点。

    虽然在初期,对于太多配置的理解会比对于一些代码的理解更加困难,但是掌握后的好处是无穷无尽的。

Orx是跨平台的

    目前Orx直接支持的平台包含了所有流行的平台,包括了Windows,MacOS,Linux,还有IPhone,IPad。事实上,我当年就是在寻找一个合适的跨平台IPhone引擎时发现Orx的。(我前段时间的工作就是在IPhone平台上,所以比较关注)当然,即使对于IPhone平台来说,最优秀的2D引擎毋庸置疑的是Cocos2D for Iphone,Cocos2D for Iphone是我见过的支持特性最多的2D引擎,不仅仅针对IPhone平台,在所有的开源2D引擎中,它支持的特性都是堪称最多的,得益于IPhone开发的热门,其周边的工具也是非常的丰富,实际建立在Cocos2D for Iphone引擎上的游戏也是数以十计。但是,Cocos2D for IPhone是仅限Iphone平台的,并且使用的是Objective C语言开发。这是它的强项,也是其弱项。我希望有个跨平台的引擎,这样才能看到更多,并且易于协作,(在公司也是这样)在Windows下开发,然后在其他平台运行,这是很重要的,别的不说,就说Macos独霸的XCode,根本没有办法与在Windows下经历过多次残酷竞争并且胜出的Visual studio相比,再加上Visual assist和ViEmu两个插件,VS绝对是梦幻级的平台!

    并且,虽然我也可以使用Objective C来开发,但是我更加熟悉的还是C++,所以我希望使用C++来开发,这样对于我来说,效率会更加高。

    跨平台,对于很多只关注Windows平台的人来说是完全不考虑的,但是,其实,优势有太多太多。

Orx的协议非常自由

    在外企的工作经验使我对协议非常敏感,不再像在国内企业时那样,只要是世界上最强大的,拿来就用,Orx的新版本(1.2)会使用Zlib协议,这是一个非常非常自由的协议,支持进行商业闭源的开发,并且也完全可以对Orx进行任何的闭源的修改。

Orx支持的特性比较多

    Orx不是最强大的,但是支持的特性已经足够多了,可以很方便的做一些简单的游戏,其内嵌物理引擎Box2D,内嵌声音引擎,有很多有用的图形特效,比如缩放,翻转,移动,alpha值变化等,事实也提供了对addcolor和普通透明混合效果的支持。

Orx使用较为简单

    Orx的使用很简单,不仅仅其以配置为基础(事实上我感觉这点在初期还比较麻烦),Orx的作者对Orx的定位是一款完整的游戏引擎,而不仅仅是一个图形引擎,在Orx中所有的东西都抽象成了Object,拥有统一的接口,并且可以方便的通过配置/代码来更改属性及效果。并且最最重要的是,Orx对于物理引擎的支持不是简单的外挂(如Cocos2D for Iphone),而是内嵌,直接将Box2D与其Object绑定在一起,可以直接通过配置的设置,不用知道任何Box2D的东西,就能直接使用物理引擎。当然,事实上,知道其相关的物理概念还是很重要的,不然怎么知道配置什么啊?但是起码可以不使用任何Box2D的API。(目前仅提供一些基础的支持,不支持Joint这样稍微复杂一点的特性)目前个人使用感觉是,用Orx做物理相关的东西,那是非常的简单。但是,由于Orx对于图形动画的支持较弱,而且也没有一款动画编辑器支持,所以用来做复杂的动画(其实即使是简单的动画)会比较麻烦,需要非常多的手动配置。我正考虑为Orx做一款以Json为基础的动画编辑器以简化此过程。

Orx的开发者有丰富的经验,并且极为热心

    我在Orx的论坛上,以及私下与Orx的开发者(目前Orx核心主要由iarwain开发)有很多的交流,他有着15年以上的程序编写经验,10年以上的游戏开发经验,并且一直是从事底层开发,现在任职于Ubisoft的加拿大蒙特利尔工作室,他的工作经验,使得Orx有着坚实的基础,良好的架构,特别值得一提的是其编码风格,注释详尽到几乎每行都有,我曾经询问过这个问题,因为通常来讲,推荐的注释的作用为解释代码的运行原理和作用(即Why?How?),而不是具体干了什么(what),但是他认为,整个代码他就是分为两部分,一部分为逻辑,一部分为实现,逻辑由注释描述,实现由代码描述,方便他在不看代码的情况下就能方便的了解逻辑,进行全面的了解或者深入的调试。并且其提出,在他那里,有很多编程经验丰富的人尝试用这种风格来编码,从来没有人说这种风格不好的,最后都坚持使用了这种风格。当然,这仅是一家之言,他的个人看法,但是对比现在我工作中的几乎没有注释的代码,我还是感慨良多。

    他经验丰富,最难得的是他非常热心,在论坛中,他知无不答,答无不尽,纠正了很多我对游戏开发的一些不对看法,也解释了很多Orx的设计,运行原理和思想,我受益良多。在私下的用steam的交流中,他也是给了我很多提示和解答,对我的帮助非常多。

最后

    推荐有兴趣的人都去其网站看看,并了解了解,希望你也能像我一样喜欢上Orx,网址是http://orx-project.org/,在WIKI上有两个教程
,讲的还算比较详细,但是也有很多我认为遗漏的地方,API Doc
得益于iarwain的详尽注释风格,非常详细。事实上,我准备按照我的学习经验,自己组织一系列关于Orx的教程,并且,以开发一款完整的游戏为脉络,而不是以特性介绍为原则。会以Windows为平时的开发平台。

 

 

 

-----------------------------------------------------------------------------------------------------------------------------

关于极端详细的注释风格,我问过iarwain,这样的风格岂不是很影响开发效率?毕竟写代码时写那么详尽的注释总是个负担,但是他说他和他的朋友们都说从来没有这样的感觉,注释总是让他
们在调试时,在过了很久再回头来看时,可以忽略很多代码,而直达问题的关键。后来,我想了想,也许和他的工作内容有关吧,他常常进行的是底层的开发工作(如他所
言,low-level),所以可能相对来说代码量比较小,改动也比较小的,更多的时间是用于思考,而不是敲代码,而且,因为稳定,有可能需要回头看用过了很久的代码,所以这样的注释风格才适合他吧。呵呵,对于我这样的coder,常常敲无厘头,没难度,不用思考的逻辑代码,最多一周几千行C++代码的工作量,要是按那个规模注释,估计很难达到老板要求的速度了。

 

 

-----------------------------------------------------------------------------------------------------------------------------

这里贴一段iarwain自己的话来解释一下LuckilyYu
的问题,以及iarwain自己对

Orx的目标,也同时作为一个额外的参考材料。其实并不是所有的引擎都只关心图形显示的。

其中Irrlicht作者将其归入Game Engines一类,个人对其有所了解,感觉应该在High level game libraries。其他还是比较赞同。

 usually see 4 different kinds of game creation tools:

    * Low level game libraries: Allegro, SDL, ClanLib, ...

    * High level game libraries: IndieLib, Cocos2D, STK, ...

    * Game engines: they're usually 3D ones: Panda3D, Irrlicht, Delta3D, ...

    * Fully integrated game engines: Construct, Unreal, Unity, GameMaker, ...

I think orx is (or trying to be) part of the third list. You don't manage sprites, sound resources etc, in orx, you just have objects with properties and rules in a 3D world.

What it means in the end is that, as you said, there's a higher level of abstraction which usually leads to less low level control.
As everything is public in orx, and due to the plugin architecture, you could take control of the low level parts, even rendering if you needed 3D support.
Of course, it's not the philosophy of the engine and I'm trying hard to make things so that you wouldn't feel the need too much to do so.

Orx is the way it is because I don't like having to initialize in code a whole bunch of things and write 10 lines everytime I want to add a new sprite with a visual effect.
I'd rather just change some parameters in config and restart the program without changing a line of code, or even just reload the config file on-the-fly depending on the cases.
Of course, nothing's perfect and this aim isn't totally reached, but that's my current goal: having the least amount of code to write as possible.

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

Irrlicht On IPhone

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

主要参考来自于:http://www.ccloveaa.com/blog/article.asp?id=476
http://www.irrlicht3d.org/wiki/index.php?n=Main.IrrlichtEngineWiki

http://www.irrlicht3d.org/

http://irrlicht.sourceforge.net/

1: Download irrlicht engine from: http://irrlicht.svn.sourceforge.net/viewvc/irrlicht/branches/ogl-es/

2: Create a new window base iphone app, add include and source folder into the project. add include in the include search path in xcode.
3: Add Foundation CoreGraphic OpenGLES QuartzCore UIKit framework in to the project.
3.1: delete all the error file from project when you build the project
4: Pass the UIWindow or UIView pointer to windowID, then Create a irrlicht device.
5: Use irrlicht engine render scene.

全文很简练,但是完全有效且使用,没有任何废话,按照步骤做就得了。
只是没有想到会这么容易,毕竟,Irrlicht是没有官方支持IPhone的,当然,上面得地址是官方的IPhone branch,从某种角度来说,比起官方支持的OGRE来说,要编译一个可用的irrlicht还是复杂了很多,并且,很多基础组件都没有,这点,所有尝试的人都需要注意。
假如你对Irrlicht很感兴趣,并且也对IPhone很感兴趣,那么就放心的尝试吧,比起你尝试装黑苹果的经历肯定要愉快的多。

首先,下载代码,解压,这些都不多说了,注意的是,需要自己新建工程,原工程太老太老,还是PPC的玩意儿,根本没有用,新建一个OpenGL ES的工程你会轻松很多,然后编译,肯定一堆错误,如上面所言,将所有报错的文件全部删了,另外,推荐通过irrcompile的config文件,关闭所有的d3d,并且完全删除libjpeg,我们不会用到,(事实上,主要是编译不过),然后去掉一堆irrlicht包含的第三方库的垃圾文件,比如readme,configure啦。。。。。。。

然后,链接,这个也不是太难,最主要的是,你需要注意,很多冲突的产生,原因都是因为很多第三方库(以bzip2最多)包含自己的Test程序,这些Test程序都包含有自己的main函数,这会与你自己的main函数冲突,全部删之,然后,恭喜你,你已经成功了!哈哈,没有想到这么简单是吗?

Irrlicht论坛中也有很多有用的资源:
最有用的thread
:Compiling irrlicht ogl-es branch for iphone (progress)
  (事实上,按照thread中的说明一步一步来肯定能编过,并且成功,亲身实验)
IPhone上的触摸响应:http://www.rockfishnw.com/media/files/TouchEvents.zip

有用的Irrlicht在IPhone下的模版:http://www.mediafire.com/file/i1juyzy3mzz/iPhoneTemplateApp.zip


有图有真相,下面贴个上面templateApp运行时的截图:

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

【转帖】暴强游戏资源帖

From:http://www.gamedev.net/community/forums/topic.asp?topic_id=324643

 

Graphics Engines

Ogre 3D

Nebula Device

Jad
(formerly Haddd)
Irrlicht

Haaf's Game Engine
(hardware
accelerated 2D games engine)
Axiom
3D

(OGRE for .NET)
TrueVision

Revolution3D

Genesis3D

Crystal
Space 3D


Panda3D

Quake 3 Source Code

Quake 2 Source Code

Quake
2

(complete sources)
Tenebrae
(Quake with per
pixel lights and shaders)
Wild Magic
(David
Eberly's engine)
Apocalyx

ClanLib Game SDK

DevLib

kjAPI

Portable Game Library (PLIB)

RealmForge GDK

Delta3D

G3D

NeoEngine

QuakeForge

XEngine

Yake

Kyra Sprite Engine

Libraries

Free Game
Development Libraries

(external list)
Graphics:

Crazy Eddie's GUI system

Cal3D
(character animation
library)
GLFW
(OpenGL Framework)
GLee
(OpenGL easy extension
library)
DevIL

Game
Texture Loader


SDL

Cpw
(another OGL
framework)
Allegro

Open Scene Graph

Corona

Titan

SFont

CxImage

CImg

FreeImage

BMF_Font

BFont

FreeType
(font rendering library)
PaintLib

GLEW
(OpenGL extension
wrangler library)
gluX

GLUT

FreeGLUT

Open Inventor

FTGL
(OpenGL Font Library
nVidia Scene
Graph SDK


OpenSG

ParaGUI

LibUFO
(OpenGL
GUI Toolkit)
GPUmesh

GNU Triangulated Surface Library

libASE

lib3DS

GLOD
(Geometric
Level of Detail for OpenGL)
Video:

FFmpeg

Dirac

Theora

XviD

Networking:

RakNet

OpenTNL
(Torque network library)
HawkNL

OpenPlay

ReplicaNet

Game Networking Engine

Sound:

FMOD

OpenAL

Audiere

BASS

libsndfile

Ogg Vorbis

Physics and Collision:

Open Dynamics Engine (ODE)

Tokamak

Newton Game Dynamics

NovodeX SDK

Physical

True Axis

Open Dynamics Framework

ColDet
(Free 3D Collision
Detection Library)
Opcode

RAPID

FreeSOLID

V-Collide

Scripting and XML:

Python

IronPython

Lua

AngelScript

CInt

EOS

GameMonkey Script

Guile

IO

javascript

Squirrel

Small

Ruby

Tcl

Perl

LibXML

TinyXML

Expat

Apache XML libraries

ezXML

Compression and File Systems:

zlib

bzip2

LZMA SDK

LZO

PhysicsFS

#ZipLib
(native C# archive library)
SharpFS
(native C# VFS)
unrarlib

ZipArchive

Note: Much of the above lists (but not all) were taken from this page
,
maintained by Kylotan here
.

Applications and Utilities:

Development Environments:

Visual Studio 2005
Express


Code::Blocks IDE

Dev-C++

SharpDevelop

ActivePython

Graphics Development:

gDEBugger

FX
Composer


RenderMonkey

NVIDIA SDK

ATI Radeon SDK

DirectX Developer Center

NVIDIA Cg

Sh
(shader language)
Modeling:

SoftImage|XSI
Mod Tool


Milkshape 3D
(not
free, $25)
Wings 3D

Blender

Anim8or

DeleD

World Building:

Quake Army Knife (QuArK)

GtkRadiant

Getic3D

Audio:

ModPlug

Audacity

SynthEdit

Buzz Machines

KVR Audio
(not actually software,
but has tons of links)
Images:

Paint.NET (excellent free
image editor)

The GIMP

BMFont
(font
texture generator)

Media

Textures:

Transmogrifying
Textures Vol. I (NVIDIA)


Mayang's Free Textures

3D Cafe's Free Textures

Absolute Background Textures
Archive


Image * After

Media Link's
Free Backgrounds and Textures


Free Game Textures

Seamless
Textures -- Absolute Cross


3D
Millenium Free Textures


Pixel Poke

The Texture Studio

Textures

Marlin
Studios


Free Foto

Free
Textures


Free Textures

MorgueFile

Psionic3D

Models:

3D Millenium
Free Models


Polycount

Marlin
Studios


Free 3D Model
Collections


Free 3D Models

Digital
Animators Models


Psionic3D

Free 3D Models for 3D
Studio Max


Sprites

This
thread has lots...and lots...and lots...


Sounds:

SFX
and Music Resources

(another long GDNet post)
Jamendo
(Creative Commons
licensed music)
A1 Free Sound
Effects


Sound Effects

Tintagel's Free
Sound File Archive


Free Sound
Effects


Sound Hunter

Wav
Sound Complete Archive


deusX Sound Design

FindSounds
(sound search
engine)
Absolute Sound Effects Archive

That's all I've got for now. Still need plenty of resources in the art
and music department...free sounds, free textures, free models, free
music, and of course, many more utilities.

阅读全文....

Boost::Signals2 学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

信号和插槽系统是我想用来在游戏引擎中让各模块解耦的关键部分,虽然自己以前也写过简单的实现,但是今天竟然找不到原来的代码了,而且我现在越来越懒了。。。。。于是乎,还是学习Boost中的signal2库吧。。。。。。。。。。汗颜中。在我发现公司一个比较成功的项目中竟然用了loki库后,使用外部库是越来越大胆了。。。。。更何况,这仅仅是我自己业余作品,按需配置使用使用boost并不过分吧。。。。呵呵,一点都不过分。

为什么我们需要信号和插槽?想想Qt几乎建立在这个基础之上,并且如此好用就能够理解了。降低耦合性,这应该是其核心目的吧。
信号和插槽可以让各对象只管处理自己的事情,比如按钮只管通知自己已经被按下而不是直接处理按下后应该做什么.
需要响应按键消息的对象应该只是注册自己需要在按钮按下后干什么,而不是每次去查询按钮是否被按下.毕竟轮询式的查询远不及通知来的有效。
UI用信号-槽设计的好处是巨大的,这让给对象的职责更加明确,减少了代码耦合,某一天,你想换套响应代码,OK,没有问题,直接换吧,UI层完全还是原来一样,不用关心外部的更改,反过来亦然,这在你将操作直接放在按钮按下的地方处理,肯定是没有办法做到了。

不过,个人感觉在游戏中比较特殊,因为各对象有固定的Update活动周期,各对象的处理应该都在自己的Update周期中处理,不然可能会破坏各自的Update顺序,导致问题.而且,在普通应用程序中存在的循环查询问题在游戏中也几乎可以忽略,因为游戏本身就是一个循环,无论什么时候,游戏都是每帧在render,每帧在Update,除非,你愿意给玩家看到一个往往全全的静态游戏。 另外,特别需要注意的是,无论如何,这样的灵活性不是没有代价的,就像动态语言比静态语言往往要好用的代价一样,牺牲的是性能,信号和插槽在最好的情况下也多了一个间接层。

下面的例子(来自boost文档)是我个人认为最有价值,最能说明问题的,解决的也就是我提到的问题。

//[ passing_slots_defs_code_snippet
// a pretend GUI button
class Button
{
    typedef boost::signals2::signal<void (int x, int y)> OnClick;
public:
    typedef OnClick::slot_type OnClickSlotType;
    // forward slots through Button interface to its private signal
    boost::signals2::connection doOnClick(const OnClickSlotType & slot);

    // simulate user clicking on GUI button at coordinates 52, 38
    void simulateClick();
private:
    OnClick onClick;
};

boost::signals2::connection Button::doOnClick(const OnClickSlotType & slot)
{
    return onClick.connect(slot);
}

void Button::simulateClick()
{
    onClick(52, 38);
}

void printCoordinates(long x, long y)
{
    std::cout << "(" << x << ", " << y << ")n";
}
//]

int main()
{
    //[ passing_slots_usage_code_snippet
    Button button;
    button.doOnClick(&printCoordinates;);
    button.simulateClick();
    //]
    return 0;
}

参考:
boost 手册,signal2》,特别感谢金庆 将其翻译成中文了,并且翻译的非常好。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

boost::bind 学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    最近学习了太多与MacOS与Iphone相关的东西,因为不会有太多人有兴趣,学习的平台又是MacOS,不太喜欢MacOS下的输入法,所以写下来的东西少了很多。

    等我学习的东西慢慢的与平台无关的时候,可能可以写下来的东西又会慢慢多起来吧。。。。。不过我想早一批订阅我博客的人应该已经不会再看了,因为已经很少会有程序语言或者C++之类的信息,相关的东西应该都是关于计算机图形,游戏的。或者,早一批订阅我博客的人现在也已经毕业,工作了呢?

    对了,这次的主题是boost:bind。在历经了
boost::asio(1)

boost::asio(2)

boost::asio(3)

boost::foreach

boost::function

boost::lambda

boost::serialization(1)

boost::serialization(2)

boost::string_algo

boost::thread

之后,boost常用的很多都学会了,现在自己写点小东西,那是大胆的用大。。。。。。。呵呵,反正自己的东西,就当用来锻炼技术了。在学习boost::signal2的过程中发现自己其实对boost::bind这个将来要进C++标准的库了解还不够多,(在boost::functon中有所提及,也同时学了一点),所以抽了点时间,好好的学习了一下。


Purpose(目的)

原来文档中的purpose完全就是教程了,事实上,bind主要用于改造函数,比如,一个地方需要一个无参的函数,你只有一个以int为参数的函数,并且你知道此时int一直为1就可以了,你怎么办?传统方法,直接实现一个函数,然后以1调用以前的那个int为参数的函数。如下:

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

    FunWeNeed();

    

    return
 0
;

};

当然,这个例子太扯了,我们只需要直接用Fun1(1)调用就可以了,但是bind的目的就是如此,只不过现实中因为各种各样的原因,你的确需要改造函数。再比如下面的情况,你有一个原来写好的函数,接受一个以无参函数为参数,那么,你的Fun1就没有办法派上用场了,那么,传统上,怎么办?如下:

typedef
  void
 FunWeNeed_t(void
);

void
 CallFun( FunWeNeed_t f) {

    f();

}

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

//  CallFun(Fun1);          // this line can't compile

    CallFun(FunWeNeed);

    return
 0
;

};

Ok,你不得不写新的函数以满足需求,因为你没有简单的办法改变一个函数的参数。事实上,假如你是STL流派的话,那么随着你使用标准库的算法的次数的增加,你会遇到越来越多上面描述的情况,到底很简单,C++是如此的类型安全的语言,它不会加上诸如参数过多就忽略后面参数的胡扯特性。那么,一个算法需要你传进来的是什么形式的函数(或者函数对象),那么你就的怎么做。

去看看,标准库中提供了一大堆多么扯的函数对象吧,less, more,,greater_equal,not1,not2,。。。。。。然后还给了你一堆compose1,compose2........最后附带最恶心的bind1st,bind2nd,事实上,这些东西如此之多,以至于我甚至懒的列举出来,但是事实上我们在项目中用到了多少?简而言之,None!一次也没有,甚至因为很多算法与此相关,我们连那些算法也不用!

为啥C++当年出现了那么多奇怪臃肿无用的设计?可能是,C++标准出现的那个年代,编程技术的发展也就到那个地步吧。。。。。。。。。在C/C++语言中,关于函数的抽象特别的少,原因很简单,因为函数指针太不好用了!(函数抽象也用,但是好像用的最多的是在C语言中无物可用时,不得已的而为之)

记得在哪看过一句话,技术再花哨也没有用,最重要的是足够简单,因为不够简单的技术就很难给人讲解,别人很难理解那么就很难应用。这些C++特性应该算是其中的一例了。

boost中的bind,function
,lambda
就是为此而生的。注意,在tr1中,你就已经可以使用bind和function特性了,这也很可能是将来的C++标准之一。现在boost中的lambda还不够成熟,语法很怪,限制很多,因为,毕竟,boost再强大,仅仅是库啊。。。。。。。。。匿名函数的功能完全值得用语言特性来实现!

上面那个很扯的例子,总的给个bind的解决方案吧。

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

void
 CallFun( boost::function<void
(void
)> f) {

    f();

}

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

    CallFun(boost::bind(Fun1, 1
));         // this line can't compile

    CallFun(FunWeNeed);

    return
 0
;

};

需要注意的是,此时不能再使用函数指针了,bind的天生合作伙伴是function,而function是支持函数指针的(如上例所示)

。目的将的很多了,下面看看用法吧(不是什么问题)。


普通函数

最有意思的是,你甚至能用bind来扩充原有函数的参数,见下例:


#include
<iostream>

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

using
 namespace
 std;
using
 namespace
 boost;

void
 f(int
 a, int
 b)

{

    cout <<"Argument 1 is "
 <<a <<endl;

}

void
 g(int
 a, int
 b, int
 c)

{

    cout <<"sum is "
 <<a+b+c <<endl;

    cout <<"arg 1: "
 <<a <<endl;

    cout <<"arg 2: "
 <<b <<endl;

    cout <<"arg 3: "
 <<c <<endl;

    cout <<"---------------------------"
 <<endl;

}

int
 main()

{

    function<void
(int
,int
)>  f1= bind(f, _2, _1);                 // 调整参数1,2的位置

    f1(1
, 2
);

    function<void
(int
)> sum1 = bind(g, _1, _1, _1);        // 3个参数变1个

    sum1(10
);

    function<void
(int
, int
)> sum2 = bind(g, _2, _2, _2);       // 3个参数变2个,仅用一个

    sum2(10
, 20
);

    function<void
(int
, int
, int
)> sum3 = bind(g, _3, _3, _3);      // 3个参数还是3个,但是仅用1个

    sum3(10
, 20
, 30
);

    function<void
(int
, int
, int
, int
)> sum4 = bind(g, _4, _4, _4);     // 3个参数变4个,但是仅用1个

    sum4(10
, 20
, 30
, 40
);

    return
 0
;

};


输出结果:


Argument 1 is 2

sum is 30

arg 1: 10

arg 2: 10

arg 3: 10

---------------------------

sum is 60

arg 1: 20

arg 2: 20

arg 3: 20

---------------------------

sum is 90

arg 1: 30

arg 2: 30

arg 3: 30

---------------------------

sum is 120

arg 1: 40

arg 2: 40

arg 3: 40

---------------------------

函数对象

注意用法中很重要的一条:通常情况下,生成的函数对象的
operator()
的返回类型必须显式指定(没有
typeof
操作符,返回类型无法推导)。(来自Boost文档)

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

using
 namespace
 std;
using
 namespace
 boost;

struct
 F

{

    int
 operator
()(int
 a, int
 b) { return
 a - b; }

    bool
 operator
()(long
 a, long
 b) { return
 a == b; }

};

int
 main()

{

    F f;

    int
 x = 104
;

    function< int
(int
) > fun1 = bind<int
>(f, _1, _1);      // f(x, x), i.e. zero

    cout <<fun1(1
);

    function< bool
(long
) > fun2 = bind<bool
>(f, _1, _1);       // f(x, x), i.e. zero

    cout <<fun2(1
);

    return
 0
;

};

其他的也就很简单了。


成员指针

例子来源于boost文档。
#include
<iostream>

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

#include
<boost/smart_ptr.hpp>

using
 namespace
 std;
using
 namespace
 boost;

struct
 X

{

    void
 f(int
 a) {

        cout <<a <<endl;

    }

};

int
 main()

{

    X x;

    shared_ptr<X> p(new
 X);

    int
 i = 1
;

    bind(&X::f, ref(x), _1)(i);     // x.f(i)

    bind(&X::f, &x, _1)(i);         //(&x)->f(i)

    bind(&X::f, x, _1)(i);          // (internal copy of x).f(i)

    bind(&X::f, p, _1)(i);          // (internal copy of p)->f(i)

    return
 0
;

}

可见bind的强大,支持自己拷贝需要的对象,支持引用,甚至,支持智能指针。

最后一个例子,结合标准库容器及算法的例子,这才是展示bind的强大的地方。

还是来自于boost文档。
class
 image;
class
 animation

{
public
:

    void
 advance(int
 ms);

    bool
 inactive() const
;

    void
 render(image & target) const
;

};

std::vector<animation> anims;

template
<class
 C, class
 P> void
 erase_if(C & c, P pred)

{

    c.erase(std::remove_if(c.begin(), c.end(), pred), c.end());

}

void
 update(int
 ms)

{

    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::advance, _1, ms));

    erase_if(anims, boost::mem_fn(&animation::inactive));

}

void
 render(image & target)

{

    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::render, _1, boost::ref(target)));

}

例子展示了erase_if,for_each算法中使用bind的方法,当然,实际中,假如是你的游戏引擎中的update,render函数,碰到上述需求或者类似代码实现是很正常的,但是,你会放心的仅仅为了简化一些代码,然后将如此性能相关的位置,直接交给bind吗?

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

寻找最佳的数据存储方式

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

游戏中总是有很多相关的数据需要存储,比如编辑器产生的关卡数据,人物,物品属性的配置等等,并且现在已经不是每人都设计一种自己数据格式的时代了.程序员们是越来越懒,大家都想着一套文件格式,一套解析,处理代码能够通吃所有的程序.以下讨论,包括优缺点,都仅仅是对于游戏数据(还仅指编辑的数据)存储而言,以下都省略此前提背景,其他领域的使用本文仅供参考,游戏领域的使用本文也仅是一家之言,当然也仅供参考.
首先,看看传统的数据存储解决方案.

传统数据存储解决方案

EXCEL方案

最大的优点是几乎人人都会更改并且可以更改而不用进一步的学习.干这行的,别说策划,即使是文秘应该起码也会EXCEL吧?
缺点,需要自己写一大堆的Excel解析代码,这点是无奈的.
并且,假如不想每个Excel文件都写一套解析代码的话,你就要想好一种通用,并且扩展性良好的Excel编写格式,这也不是太容易的.
即使设计的Excel格式扩展性足够好,当数据慢慢复杂的时候,你会发现,事情越来越复杂.....几乎就开始将Excel做关联数据库使用了.有的时候,通过改动一些关联性很强的Excel数据来完成某个功能,甚至比你通过代码改变来实现还要复杂.....到那个时候,你才会发现,Excel已经不敷使用.
最最重要的是,Excel(起码03版的是这样)是二进制数据,SVN,mercurial等版本控制工具(偏偏这就是大家项目中所用的吧)不支持merge,这样,每次多人更改,必然导致冲突,然后,手工再去痛苦的重写吧.
但是,假如你碰到像我们公司说的这种,翻译组只懂使用Excel,那么你再痛苦,也就无奈去吧.这也是Excel的优点吧........当然,从程序的角度来看,那是最大的缺点.

数据库方案

数据库方案最大的优点是扩展性异常强大,数据库方案的扩展性几乎是无限的,无论多么庞大的数据量,放在数据库中总是不会显得很多,关联表格也总是能将冗余数据降到最低,并且,如此的清晰.由于现代数据库前端做的越来越好,修改数据库中的数据也不总是需要SQL语句了.最最重要的是,当你需要的时候,你还是能用SQL语句来实现你需要的最最复杂的查询,或者批量修改某些相关的数据.
当然了,没有方案是完美的,用数据库方案,的确强大,但是也有强大的代价,首先,项目中得有数据库专家,能够设计足够好的表格,及写出复杂的多重嵌套SELECT语句,项目中得有人懂得使用各自语言自己的数据库接口API,并且比较郁闷的是,各种数据库的API设计还不太一样......通用的API设计往往是以低效率为代价的....当然,这些都扯远了,扯上了原来做服务器的经验了........假如仅仅将数据库作为普通游戏数据存储的方式,缺点还没有那么夸张,并且,我们总是可以使用mySQL,SQLLite这样的开源解决方案.在各类语言中也总是有可用的相应的API可用.
除了上面那些,还有个问题,即使仅仅将数据库作为普通游戏数据存储的方式,为每个需求设计一套表,写一堆的从数据库中获取数据的代码,并且,最要要是以二进制数据发布的话,你还得写一套从数据库中读数据,然后序列化成二进制数据的代码,然后,额外的,解析二进制的代码也不可少.强大的数据库解决是以庞大的人工代码量堆积起来的.

现代数据存储解决方案

因为传统解决方案有着其天生的缺点,Excel对于越来越大的游戏,越来越多的游戏数据有些吃力,而是用数据库方案对于光是保存游戏数据(仅指编辑数据,不包括网游的运行时数据)又显得太过臃肿.那我们有没有更好的方法呢?先看看我们想要什么样的方案:
1.有着文本存储的方式,便于开发期调试和修改,也能直观的查看到改变和开发期的版本控制merge.
2.有二进制存储方式,在发布后可以使用这样的方式,减少parse时间,并且,从文本方式切换到二进制方式是有很简单解决方案的.
3.parse,序列化的代码编写要足够的简单,起码不会随着数据量的扩大而扩大到难以掌握的地步.
4.多语言支持,你总能在你喜欢的语言中使用
5.在数据之间建立关联,嵌套关系要足够的简单,这样才能简单的表示丰富的内容,而不是通过另外再建一套数据来表示关联(就像在Excel中需要做的那样)

有方案能满足上面所有的需求吗?可以寻找一下.
因为我能力有限,这里仅举较为常用的XML,JSON,Google Protobuf为例,作为比较,也作为自己选择最佳方式的参考.(在查找资料的过程过还发现了一个名叫Thrift的解决方案,有文章
的比较指出其比Google Protobuf要强大较多,因为使用较少,我不准备尝试,在此提供此信息,仅供参考)

XML方案

现在游戏数据中XML方案应用的应该算是非常广泛了,几乎算是事实上的工业标准?起码我的感受是这样,甚至于,Office 2007以后的通用格式都是XML化的了.是因为网页开发的太过流行,导致大家都喜欢上了尖括号横生的标记语言了吗?我不知道.但是,XML的确有其优点.在WIKI百科上,有这么一段描述:
As of 2009[update]

, hundreds of XML-based languages have been developed,[5]

including RSS
, Atom
, SOAP
, and XHTML
. XML-based formats have become the default for most office-productivity tools, including Microsoft Office
(Office Open XML
), OpenOffice.org
(OpenDocument
), and Apple
's iWork
.[6]


看看吧,应用的还真不少.
首先,上述5点需求的需求1,3,4,5是较为完美的满足的.XML是写给人看的数据存储格式,虽然喜欢尖括号的人(比如我)会反感,并且因为其流行,解析XML的库也是非常丰富,各类语言对XML的支持肯定少不了,XML对于嵌套,关联的支持也是非常好的,其自描述性,能让你看到XML就能知道,它在描述什么.XML的处理都是字符串,支持unicode, wikipedia上还提供了一个有意思的例子:
version
="1.0"
encoding
="UTF-8"
?>


<烏語>

Китайська мова>


中国人写的?-_-!当然,实际中,除了字符串可能会用上中文外,一般的tag我们还是习惯用英文.
另外,有一点也很重要,XML是可扩展的,添加新的XML属性完全不影响原有的解析及代码的运行,直到你真的需要这个属性,然后再添加处理代码即可.
但是,XML有个致命的缺点,一堆类似HTML的标记,根本就不是设计给人来写的,(当然,好用的工具另外),在阅读的时候,很显然,废数据也过多,那么,存储,传输的时候,(假如传输的真是文本的话)同样的,信噪比太低,这点我不是太喜欢.当然,最主要的是直接写XML比较麻烦.也许有强大的工具能解决此问题,但是鉴于我们公司的制度,那可能是可望而不可及的事情.

JSON
方案

上述5点需求的需求1,3,4,5可以得到比XML更完美的满足.得益于简单的格式,所以解析及创建都可以做的很简单的,特别是JSONCPP这个库,在使用的时候简直就像获取到JS中的Object一样,直接通过[]操作完成索引及建立操作,使用起来感觉非常爽,并且,也有XML有的可扩展性,相对来说,比起以前使用LUA做配置而言,JSON会更加简单,当然,功能也稍微弱一些,不用逻辑处理(到脚本层次)的时候,我个人认为JSON就是最合适的数据存储格式.当然,从数据处理的速度来说,可能还需要一个从JSON到二进制的转换工具,(XML也有类似问题),然后在发布期完全使用二进制.
目前有点问题的就是,JSON的格式没有XML那么规范,所以常常会容易写错,然后自己还没有感觉,JSONCPP使用了MAP的特性,也就是说,使用方便,但是当[]查找不到的时候,是默认建立了空的索引,这会带来一些混乱.....
在我们的项目中,使用JSON来配置界面,为UI的灵活性提供了很大的方便,并因此节省了无数代码及编译时间,此时我想起一句LUA之父说的话,只有当配置足够灵活,人们才会使用它,的确如此,当JSON的使用如此方便的时候,使用配置,而不是代码,那是享受,而不是痛苦.

另外,XML与JSON方案还有个好处,因为他们的通用性,各个项目之间的沟通也会更加简单,与人解释XML,JSON在干什么,比与人解释自己的二进制文 件在干什么要简单的多.也因为沟通更加简单,使得在通用格式上面的那一层代码也能得到更多的复用机会.甚至,我们考虑过使用其他项目中创造的用JSON实现的关键帧动画.........

Google Protobuf
方案

用的不多,试用了一下,谈下感想,Google在说明文档中说Google内部使用时,所有的进程间他通行,一律要求使用Google Protobuf,也可见其自有其强大之处.使用方式上与上述两个方案有比较大的不同,XML与JSON仅仅是描述数据,单纯的数据,解析的代码还是需要自己去写(尽管JSON的解析已经非常简单了)但是Google Protobuf不同,其要我们编写的是结构描述文档,并且通过其附带的工具,直接生成C++,JAVA,Python的代码(也就是Google内部使用的3语言,与我们公司完全一致),至此,不仅解析简单了,甚至结构都有了,我们需要做的是直接调用结构的接口去完成输入输出,所有的数据的getter都通过Google规定的方式进行了命名.甚至,直接将结构打包成2进制数据然后发送了.(也就是Google在文档中所描述的进程间通信).比起XML,JSon而言,此方案最大的优势在于速度,它有很好的文本格式以及二进制格式,可以很方便的切换(原生就支持),这也是3个方案中唯一满足了需求2的方案.(事实上,完全满足上述所有需求)但是,很明显的,有个缺点,(实在是没有实际使用,仅仅是通过试用感觉的),假如结构变换的话,可能得重新使用Google提供的工具,重新生成结构,这就需要重新编译代码,这是个较大的问题.................其他的,个人感觉,Google Protobuf是非常不错的解决方案,特别是用在需要进程间通信时(比如需要进行网络通信,连打包的代码都省了).

最后的总结是,未来的趋势应该是JSON
方案吧,Google Protobuf
方案更适合需要网络通信的情况,毕竟还需要通过工具生成代码,使用的方便性来说不如JSON.

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....