现在的位置: 首页 > 综合 > 正文

创建模块化游戏(Creating Moddable Games with XML and Scripting Part I)代码阅读,关于整体架构!

2013年09月08日 ⁄ 综合 ⁄ 共 4365字 ⁄ 字号 评论关闭

Kevin Lynx

2006-10-12

工程结构:

 系统类结构:

详细类说明:

jsiVertex 是一个操作顶点的类。基本上它就是重载了一些运算符而已。

jsiAlienColor是一个枚举类型,定义了三种颜色。

jsiUtility 提供了一个静态成员函数,该函数会根据传进来的字符串参数返回相应的颜色值---返回jsiAlienColor

jsiAlien 描述了alien信息,包含一个 jsiVertex 和一个 jsiAlienColor 数据成员,其中      

jsiVertex Z 坐标在这里是被忽略的。

              它还包含一个 pointValue 成员,应该是用来表示给玩家增加点数的属性。

jsiWeaponFire 描述武器信息。其内部定义了一个枚举类型表示武器开火的方向。

                     它还包含一个jsiVertex属性,用来表示其位置。

jsiQuad 是一个结构体,包含了4 jsiVertex 成员,用来描述一个正方形的。它重载了 = 运算符。

jsiPlayer 描述了玩家信息,也包含 jsiVertex 坐标属性。

 

jsiFactory 是一个模板类,它包含一个 vector 容器,用来容纳其创建的所有对象。该类提供管理一组类对象的能力!--创建,删除,删除所有等。

jsiLevel 描述了一个关卡里的怪物alien信息,它使用 jsiFactory 来管理本关卡所有的 alien 对象;并通过传进来的 TiXmlElement 对象来从XML文件中读取一个关卡里所有alien对象的属性,并创建之。

jsiGame 描述了整个游戏信息。它包含一个 jsiFactory < jsiLevel > 对象和一个 jsiGameData数据。看起来,它使用 jsiFactory 来保存所有的关卡。它提供了一个loadXmlLevels

              来分析XML文件。通过分析XML 文件,然后通过jsiFactor创建一个关卡对象,然后把XML的头Element给这个关卡对象进行创建alien的工作。然后再每一帧里,程序都只是简单地渲染,当按下r键时,又重新装入关卡。----

jsiRenderWindowjsiGame保存着同一个jsiGameData信息。jsiGameData 里保存着当前关卡指针,jsiRenderWindow通过该指针来渲染;jsiGame根据指针来控制逻辑。

jsiSingleton看起来是最难的一个类,作者描述它是为了使得它作用的类只存在一个对象。这段代码可能是关键:

            size_t offset = (size_t)(T*)1 - (size_t)(jsiSingleton <T>*)(T*)1;

         m_singleton = (T*)( (size_t)this + offset );

              该类的默认构造函数是保护类型的,并且从类图可以看出,这个类似乎天生就是用来被继承的。

 

系统大致结构图:

jsiGame 对象通过 jsiFactory 管理着一批 jsiLevel 对象(很多关卡),然后每一个jsiLevel又通过jsiFactory管理着一批 jsiAlien 对象(一个关卡里有很多怪物)。

jsiGamejsiRenderWindow共同保存着一个jsiGameData数据结构。该结构描述了当前关卡当前游戏的状态。每次游戏进入新的关卡(reset)时,该数据结构被更新,jsiRenderWindow只需要读取该结构,从而绘制场景。

 

关于jsiSingleton

              我可以通过查看 jsiLog 类来研究 jsiSingleton 类。 jsiLog 类里实际代码却没有操作jsiSingleton类的代码。

              jsiLog对象是在main.cpp WinMain里创建的,并且用来保存该对象地址的指针是栈上分配的,也并没有传给jsiGame对象 ,在以后要使用 jsiLog 对象的地方,都通过jsiLog::getSingleton().writeError类似的形式调用!

 

        注意: jsiSingleton 除了默认构造函数外,没有显示声明其他构造函数,那么它只有一个拷贝构造函数会是公有的,而其默认构造函数被声明为protected的,那么,程序就无论如何创建不了 jsiSingleton 对象!也就是说,这个类,必须被继承才能使用。

 

        其次,虽然 jsiSingleton 的默认构造函数是protected的,但是当它被继承后,创建派生类对象时,依然会自动调用 jsiSingleton 的这个默认构造函数!

 

        这个类非常有研究价值,完整代码如下:

template <class T>

         class jsiSingleton

         {

         public:

 

              ~jsiSingleton()

              {

                   m_singleton = 0;

              }

 

              static T& getSingleton()

              {

                   return (*m_singleton);

              }

 

              static T* getSingletonPtr()

              {

                   return m_singleton;

              }

 

         protected:

              jsiSingleton()

              {

// use a cunning trick to get the singleton pointing to the start of the whole, //rather than

                   // the start of the Singleton part of the object

                   //

                   // Code appears represented from Richard Fine's Enginuity Series

                   //

                   size_t offset = (size_t)(T*)1 - (size_t)(jsiSingleton <T>*)(T*)1;

                   m_singleton = (T*)( (size_t)this + offset );

              }

 

         private:

              static T *m_singleton;

         };

 

         template <typename T> T* jsiSingleton<T>::m_singleton = 0;

 

        我可以感觉,这个类构造函数里那两句话,是为了让m_singleton指向jsiSingleton的派生类对象!

 

2006-10-13

        jsiSingleton乍看上去象是一个单件!但是我觉得它并没有真正实现单件!因为它没有对创建对象进行限制----它本身只能被继承,其派生类对象的创建并没有被限制住!

这分代码中,作者有一个渲染类和一个LOG类,看上去,这两个类的对象确实只能存在一个!但事实上,我们还是可以创建多个这样的对象,代码里没有进行限制!

另外,关于这两句代码的解释:

size_t offset = (size_t)(T*)1 - (size_t)(jsiSingleton <T>*)(T*)1;

         m_singleton = (T*)( (size_t)this + offset );

它主要是用在jsiSingleton被多重继承时得到其派生类对象的地址!

它如何工作的?

例如,现在有这样一个类:

class jsiLog : public jsiSingleton<jsiLog>

现在,当创建 jsiLog 对象时,程序会调用到 jsiSingleton 的构造函数,但是在jsiSingleton的构造函数里,该类的 this 指针的指向地址其实跟 jsLog 里的 this 指针指向的一样。这个时候 , offset 始终为 0。这种情况下,m_singleton 完全可以直接等于 this :

m_singleton = (T*)this;

但是,当出现这样的类关系时:

          class    jsiLog : public OtherClass, public jsiSingleton < jsiLog >

jsiSingleton里的 this 就不与 jsiLog 里的 this 相同了!因为,这样的继承关系,jsiLog类里先是 OtherClass 类的内容---继承就是类的堆叠---然后才是jsiSingleton 的内容。也就是说,在 OtherClass 里的 this 指针,指向的地方才和 jsiLog this 相同。

因此,这里的 offset 计算就显得必要了。

 

其他一些信息:

一个只有一个静态成员变量的类,长度为1----其实我觉得是0.这跟sizeof有关系,因为

    当我为这个类加入一个整形变量后,其长度变为4! 而为这个类加入两个静态变量后,

    sizeof测出来还是1

 

     只创建一个派生类对象的话,实在看不出什么,offset始终为0

 

     创建一个新类,多重继承singleton类,并把它放在第二或更后的位置继承,offset就起作用了。

 

     注意:一个类的this指针,始终指向的是该类所负责的一部分的其始地址。例如:

      

    class CDesi : public CBase, public CBase2 <CDesi>

 

     那么,CBase内的thisCDesi相同,CBase2this ,就要大些--加上CBase里的数据成员。

 

     singleton类的这两句代码:

     size_t offset = (size_t)(T*)1 - (size_t)(CBase <T>*)(T*)1;

    m_data = (T*)( ( size_t)this + offset );//and now it points its desired class object's address

    可以让其静态成员 m_singleton 指向其派生类对象的地址!   

 

     指针转换:

     ( T*)1始终为1,转换后, 1是一个地址,然后经过 (CBase <T>*)转换后,实际值就不再是1了!

       

 

抱歉!评论已关闭.