如何在cocos2d-x中使用ECS(实体-组件-系统)架构方法开发一个游戏?

来源:互联网 发布:ipad下载旧版本软件 编辑:程序博客网 时间:2024/04/28 04:16


引言

              在我的博客中,我曾经翻译了几篇关于ECS的文章。这些文章都是来自于Game Development网站。如果你对这个架构方式还不是很了解的话,欢迎阅读理解 组件-实体-系统和实现 组件-实体-系统。

              我发现这个架构方式,是在浏览GameDev上的文章的时候了解到的。很久以前,就知道了有这么个架构方法,只是一直没有机会自己实践下。这一次,我就抽空,根据网上对ECS系统的讨论,采用了一种实现方法,来实现一个。

             我很喜欢做游戏,所以同样的,还是用游戏实例来实践这个架构方法。我将会采用cocos2d-x来作为游戏开发的主要工具,这是一款十分强大的跨平台游戏引擎,感兴趣的读者,可以自行搜索了解。


ShapeWar

             我一直觉得,作为游戏程序员,能够自己独立的绘制游戏中的图片资源是一件非常好玩的事情。所以,没有美术功底的我,就选择了一种复古风格的艺术——像素艺术来学习。经过一段时间的学习,发现做像素画还是很有趣的,所以我就将我以前做的简单的像素图片,来融合成现在的这个游戏实例——ShapeWar 。

            这个游戏很简单,玩家通过键盘上的左右键来移动发射器,通过按下space键,来进行攻击,将所有掉落下来的立方体全都打掉。如果有立方体遗漏掉,那么将会丢掉一颗血,直到玩家死亡为止。这个游戏,开始的时候,可能会非常容易,但是,立方体下落的速度是逐渐增加的,到了后面,如果玩家还能够坚持住的话,那非常了不起!!!

            好了,游戏规则很简单,来看看游戏的截图吧!

                 好了,这个游戏很简单,有兴趣的同学,可以到这里来下载,试玩一下,并且在留言中,告诉我,你最高得了多少分哦!!!


架构设计

                从上面的截图,大家也能够明白,游戏只有两个场景,分别是开始场景,和游戏进行场景。需要完成的功能如下:

  •   能够产生立方体,控制立方体产生
  •   能够控制发射器,发射出球体
  •   能够检测出球体和立方体之间的碰撞
  •   对不同的立方体,需要碰撞不同的次数才能消灭
  •   立方体消灭之后,要播放动画
  •   玩家拥有血量和积分

               这个游戏大致就有这些功能。

               在ECS系统中,我们没有像传统的面向对象方法那样,为游戏中每一个实体定义一个类。比如,对于这里的玩家(Player)定义一个类,然后为绿色的立方体(GreenCube),红色的立方体(RedCube),橙色的立方体(OrangeCube)和紫色的立方体(PurpleCube)都定义一个类。对于这样的小游戏来说,你可以这么做,但是对于大一点的游戏来说,里面的实体有非常的多,如果每一个都定义一个类的话,那么系统将难以维护。所以,在ECS系统中,它将“多使用组合,少使用继承”的概念发挥到极致。


组件

              在系统中,并没有为每一个实体都定义一个类,而是为构成这些实体的基本单元,也就是前面两篇博文中讲述的Component(组件),一个一个的定义。下面是我游戏中,需要用到的所有的组件类型:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // File: Component.h  
  2. //------------------------------------------------------------------  
  3. // declaration  : Copyright (c), by XJ , 2014 . All right reserved .  
  4. // brief        : This file will define the Component base class of the   
  5. //                Entity-Component-System.  
  6. // author       : XJ  
  7. // date         : 2014 / 6 / 8  
  8. // version      : 1.0  
  9. //-------------------------------------------------------------------  
  10. #pragma once  
  11. #include<cocos2d.h>  
  12. using namespace cocos2d ;  
  13.   
  14. namespace ShapeWar  
  15. {  
  16. #define COMPONENT_NONE 0x0  
  17.   
  18. class Component  
  19. {  
  20. public:  
  21.     Component(){}  
  22.     virtual ~Component(){}  
  23. };  
  24.   
  25. /** 
  26. * Define the Render Component 
  27. */  
  28. #define COMPONENT_RENDER (1 << 1)  
  29. class RenderComponent: public Component  
  30. {  
  31. public:  
  32.     RenderComponent(){}  
  33.     ~RenderComponent()  
  34.     {  
  35.         sprite->removeFromParentAndCleanup(true);  
  36.         delete sprite ;  
  37.     }  
  38.   
  39. public:  
  40.     CCSprite* sprite ;  
  41. };  
  42.   
  43. /** 
  44. * Define the Position Component 
  45. */  
  46. #define COMPONENT_POSITION (1 << 2 )  
  47. class PositionComponent: public Component  
  48. {  
  49. public:  
  50.     PositionComponent(){}  
  51.     ~PositionComponent(){}  
  52.   
  53. public:  
  54.     float x ;  
  55.     float y ;  
  56. };  
  57.   
  58. /** 
  59. * Define the Velocity Component 
  60. */  
  61. #define COMPONENT_VELOCITY (1 << 3)  
  62. class VelocityComponent: public Component  
  63. {  
  64. public:  
  65.     VelocityComponent(){}  
  66.     ~VelocityComponent(){}  
  67.   
  68. public:  
  69.     float vx ;  
  70.     float vy ;  
  71. };  
  72.   
  73. /** 
  74. * Define the Health Component 
  75. */  
  76. #define COMPONENT_HEALTH (1 << 4)  
  77. class HealthComponent: public Component  
  78. {  
  79. public:  
  80.     HealthComponent(){}  
  81.     ~HealthComponent(){}  
  82.   
  83. public:  
  84.     unsigned int health ;  
  85. };  
  86.   
  87. /** 
  88. * Define the Collidable Component 
  89. * brief : Use the AABB's Min-Max representation 
  90. */  
  91. #define COMPONENT_COLLID (1 << 5)  
  92. class CollidableComponent:public Component  
  93. {  
  94. public:  
  95.     CollidableComponent(){}  
  96.     ~CollidableComponent(){}  
  97.   
  98. public:  
  99.     float min_x ;  
  100.     float min_y ;  
  101.     float max_x ;  
  102.     float max_y ;  
  103. };  
  104.   
  105. /** 
  106. * Define the EntityType component 
  107. * brief : This component will indicate which type the entity is. 
  108. */  
  109. #define COMPONENT_ENTITY_TYPE (1 << 6)  
  110. class EntityTypeComponent: public Component  
  111. {  
  112. public:  
  113.     EntityTypeComponent(){}  
  114.     ~EntityTypeComponent(){}  
  115.   
  116. public:  
  117.     static const unsigned int RED_CUBE = (1 << 1) ;  
  118.     static const unsigned int PURPLE_CUBE = (1 << 2) ;  
  119.     static const unsigned int ORANGE_CUBE = (1 << 3) ;  
  120.     static const unsigned int GREEN_CUBE = (1 << 4) ;  
  121.     static const unsigned int SPHERE_BALL = (1 << 5) ;  
  122.     static const unsigned int PLAYER = (1 << 6) ;  
  123.   
  124. public:  
  125.     unsigned int type ;  
  126. };  
  127.   
  128. /** 
  129. * Define the AnimateComponent 
  130. */  
  131. #define COMPONENT_ANIMATE (1 << 7)  
  132. class AnimateComponent:public Component  
  133. {  
  134. public:  
  135.     AnimateComponent(){}  
  136.     ~AnimateComponent(){}  
  137.   
  138. public:  
  139.     cocos2d::CCAnimate* animate ;  
  140.     unsigned frames ;  
  141. };  
  142.   
  143. };  
            从上面的代码中,大家可以看到,我首先定义了一个基类Component,然后让所有的组件都继承于这个基类。这里,我并没有用到继承,读者可以发现Component中什么内容也没有。 我将其他组件继承于Component的组要原因是能够将他们统一的进行处理,仅此而已。

            在定义完了基类之后,分别定义了如下的组件类型:

  • RenderComponent, 用于支持渲染
  • PositionComponent, 用于定义位置属性
  • VelocityComponent,用于定义速度属性
  • HealthComponent,用于定义健康属性
  • CollidableComponent,用于定义AABB碰撞检测盒
  • EntityTypeComponent,用于定义实体类型
  • AnimateComponent, 用于定义动画渲染属性

         读者可能发现,在每一个组件上方,我都为它定义了一个标示符,如#define COMPONENT_RENDER  (1 << 1)。这是因为,我们需要知道一个实体中到底有哪些组件,所以,我们为每一个组件定义一个标示符,然后就可以通过判断这个标示符,来知道,一个实体是否拥有指定的组件了。我们将在后面看到它的用处。


实体

         如果读者,你仔细的阅读了我前面介绍的几篇文章,那么你就会知道,实体实际上就是一个ID值而已,所以,我并没有专门为这个概念定义什么,它在我开发的游戏中,仅仅是一个下标值而已。但是,我们需要知道,游戏中那么多的实体,需要进行统一的管理。所以为此,我创建了如下的一个类,用来对游戏中所有的实体进行管理。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //File:EntityManager  
  2. //------------------------------------------------------------------  
  3. // declaration  : Copyright (c), by XJ , 2014 . All right reserved .  
  4. // brief        : This file will define the Entity of the Entity-Componet-  
  5. //                System and the entity manager.  
  6. // author       : XJ  
  7. // date         : 2014 / 6 / 8  
  8. // version      : 1.0  
  9. //-------------------------------------------------------------------  
  10. #pragma once  
  11.   
  12. #include<vector>  
  13. #include"Component.h"  
  14. using namespace std ;  
  15.   
  16. namespace ShapeWar  
  17. {  
  18. /** 
  19. * Define the EntityManager 
  20. */  
  21. class EntityManager  
  22. {  
  23. private:  
  24.     EntityManager();  
  25.     ~EntityManager();  
  26.   
  27.     /** Singleton getter*/  
  28. public:  
  29.     static EntityManager* getEntityManager();  
  30.   
  31.     /** Core method */  
  32. public:  
  33.     /** 
  34.     * Create an empty entity 
  35.     */  
  36.     _int64 createEntity() ;  
  37.   
  38.     /** 
  39.     * Remove an entity 
  40.     */  
  41.     void removeEntity(_int64 entity);  
  42.   
  43.     /** 
  44.     * Register component 
  45.     * brief : This method will make the entity manager to alloc the memory to store 
  46.     *         the registed componet.If you want to use one componet in the ECS , you 
  47.     *         must registed it at the start time. 
  48.     */  
  49.     void registComponents(_int64 component_size);  
  50.   
  51.     /** 
  52.     * Add an component to the entity 
  53.     */  
  54.     void addComponent(Component* component, _int64 component_type, _int64 entity);  
  55.   
  56.     /** 
  57.     * Remove an component of the entity 
  58.     */  
  59.     void removeComponent(_int64 component_type, _int64 entity);  
  60.   
  61.     /** 
  62.     * Get component list 
  63.     */  
  64.     std::vector<Component*>* getComponentList(_int64 component_type) const ;  
  65.   
  66.     /** 
  67.     * Get the specificed component of the entity 
  68.     */  
  69.     Component* getComponent(_int64 component_type, _int64 entity);  
  70.   
  71.     /** 
  72.     * Get entity flag 
  73.     */  
  74.     _int64 getEntityFlag(_int64 entity) const ;  
  75.   
  76.     /** 
  77.     * Set entity flag 
  78.     */  
  79.     void setEntityFlag(_int64 entity, _int64 entity_type);  
  80.   
  81.     /** 
  82.     * Get the entity size 
  83.     */  
  84.     unsigned int getEntitySize() const ;  
  85.   
  86.     /** 
  87.     * Define the Component_List 
  88.     */  
  89.     typedef std::vector<Component*> Component_List;  
  90.   
  91. private:  
  92.     /** 
  93.     * Destroy all the component 
  94.     */  
  95.     void _destroy();  
  96.   
  97. private:  
  98.     std::vector<_int64> m_EntityFlagArray ;                           //Contain the Entity flag   
  99.     <pre name="code" class="cpp">        std::vector<Component_List>  m_ComponentContainer ;                //Contain all the entity  

};};

          正如读者看到的那样,这个类是一个单例类,里面提供了很多的方法。要理解这个类,我们先来看看组件是如何在这个类里面进行保存的。

          在这个类中,我定义了一个这样的成员:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. std::vector<Component_List>  m_ComponentContainer ;               //Contain all the entity  

            而Component_List定义为如下:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /** 
  2. * Define the Component_List 
  3. */  
  4. typedef std::vector<Component*> Component_List;  
            也就是说,这个ComponentContainer,包含了所有的在游戏中使用的组件实例。同一种组件实例,放在同一个Component_List中,然后不同的Component_List,放在Component_Container中。如果读者对这个不是很清楚的话,可以看下面的图片:

          

             从上图中可以看出,这是一个二维的空间盒子,纵向表示了一个组件类型中所有的组件实例,横向表示了一个实体拥有哪些组件。所以,这里的实体,也就是这里的容器中的下标了。

            好了,在明白了组件是如何保存了的之后,我们还需要了解在EntityManager中定义的这个数组是什么意思:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. std::vector<_int64> m_EntityFlagArray ;                           //Contain the Entity flag   
            这个数组,保存了相对应的实体中组件的标示符。还记得我们在组件那一节讲述的组件表示符吗?通过这个数组,我们保存了每个实体对应的组件中有哪些组件。比如说,在这个数组下标为1的单元中,也就是实体1中,有如下的组件标示符保存:

           COMPONENT_RENDER | COMPONENT_POSITION | COMPONENT_VELOCITY

            那么就意味着,这个实体是由RenderComponent,和PositionComponent,VelocityComponent组合而成的。

            好了,在明白了这个数组的功能之后,我们来看看上面管理器中各个函数的作用吧。


_int64 CreateEntity

           这个函数用来创建一个空的实体,并且返回实体的下标。用户可以通过这个方法来创建一个实体


void removeEntity(_int64 entity)

           这个函数,根据传递进来的实体下标,将实体从容器中移除,并且释放相关的资源


void registComponent(int num)

           这个函数,用来根据参数,开辟相应的组件类型空间。在开始的时候,我们并不知道有多少个组件需要使用,所以让用户自行决定需要使用多少个组件。


void addComponent(Component* component, _int64 component_type, _int64 entity)

          这个函数,根据传进来的组件,还有组件类型,以及实体下标,将组件加入到相对应的位置去。

void removeComponent(_int64 component_type, _int64 entity);

         这个函数,用来将制定类型的组件,从实体中移除。

        

         由于篇幅限制,这里不再一一的讲述。上面的代码能够很好的自我解释出每一个函数的功能。

          这里有个问题需要注意,读者可能想知道,我是如何通过组建标示符,来找到那个组建的容器的???并且实体只是定义了横向的坐标,而纵向的坐标是如何获取的了?

          这个还要讲解下我定义的容器的组织方式。

          对于不同的组件,我分别定义了标示符,而标示符中都有不同的位置标示,如COMPONENT_RENDER为 10,这个标示符中1在第1位(从0计算),那么我们将这个组件的纵向位置定义为1 - 1 = 0 ,也就是0号下标的组件容器中。所以,这就是为什么我要定义不同的组件标示符。为了能够从64位的标示符中获取‘1’在哪一位上,我在前面的博客中算法设计:如何从64位数中获取哪一位数为1采用分治算法,设计了这个方法来获取位数。

           好了,通过上面的描述,读者应该明白我是以怎么样的方式来维护游戏中所有的实体的了!!!


系统

           在实现了上面的组件,实体之后,接下来就应该实现系统了。我这里实现系统的方式,是根据这篇博客中提出的方法来实现的。

          首先,抽象一个系统的类,如下所示:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1.        /** 
  2. * Define the base system class. All system will inherit from this base class. 
  3. */  
  4. class System  
  5. {  
  6. public:  
  7.     System(int _priority);  
  8.     virtual ~System();  
  9.   
  10. public:  
  11.     virtual void enter() = 0 ;  
  12.     virtual void excute(float dt) = 0;  
  13.     virtual void exit() = 0 ;  
  14.   
  15. public:  
  16.     int priority ;  
  17. };  

        在这个抽象的系统中,我定义了一个优先级,这样,我们就可以定义哪一些系统需要在另外一些系统之前进行运行。有了系统之后,我们就需要一个管理的方式,所以,在定义了一个系统管理器,如下所示:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /** 
  2. * Define the system manager 
  3. */  
  4. class SystemManager  
  5. {  
  6. private:  
  7.     SystemManager();  
  8.     ~SystemManager();  
  9.   
  10.     /** Singleton getter*/  
  11. public:  
  12.     static SystemManager* getSystemManager() ;  
  13.   
  14.     /** Core method*/  
  15. public:  
  16.     /** 
  17.     * Add one system to the system list 
  18.     */  
  19.     void addSystem(System * system);  
  20.   
  21.     /** 
  22.     * Update all the system 
  23.     */  
  24.     void update(float dt);  
  25.   
  26.     /** 
  27.     * Pause all the system 
  28.     */  
  29.     void pause();  
  30.   
  31.     /** 
  32.     * Resume all the system 
  33.     */  
  34.     void resume();  
  35.   
  36. private:  
  37.     /** 
  38.     * Destroy all the systems 
  39.     */  
  40.     void _destroy();  
  41.   
  42. private:  
  43.     std::vector<System*> system_list ;  
  44.     bool bPaused ;  
  45. };  

             这个类同样也是单例的,用户可以通过调用addSystem来添加系统到系统管理器中。系统管理器,会在每一帧,调用update方法,update方法如下所示:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void SystemManager::update(float dt)  
  2. {  
  3.     if(bPaused == true)  
  4.         return ;  
  5.   
  6.     //Excute all the system  
  7.     for(int i = 0  ; i < system_list.size() ; i ++)  
  8.     {  
  9.         system_list[i]->excute(dt);  
  10.     }// end for  
  11. }// end for update  

             很简单,它调用已经根据优先级排好序的系统中的excute方法,来执行每一个系统的任务。

             在我的这个简单的游戏中,我定义了如下的几个系统,根据优先级从低到进行排序:

  • RenderSystem,负责进行渲染
  • MovementSystem, 负责进行实体的移动
  • HealthSystem,负责判断哪些实体已死亡
  • CreatorSystem,负责游戏中立方体的创建规则
  • InputSystem, 负责处理键盘输入
  • CollidDetectionSystem,负责进行碰撞检测
  • BoundaryCheckSystem,负责进行边界检查,当立方体和球体出了边界之后,进行相应的操作  

              下面我们来分别看看这些系统的实现过程:

RenderSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"RenderSystem.h"  
  2. #include"EntityMananger.h"  
  3. using namespace ShapeWar ;  
  4.   
  5. RenderSystem::RenderSystem(int _priority, CCNode* _scene)  
  6.     :System(_priority),  
  7.     scene(_scene)  
  8. {  
  9.   
  10. }  
  11.   
  12. RenderSystem::~RenderSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void RenderSystem::enter()  
  18. {  
  19.   
  20. }// ed for enter  
  21.   
  22. void RenderSystem::excute(float dt)  
  23. {  
  24.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  25.     for(unsigned int i = 0 ; i < size ; i ++)  
  26.     {  
  27.         _int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);  
  28.         if((flag & (COMPONENT_RENDER | COMPONENT_POSITION)) == (COMPONENT_RENDER | COMPONENT_POSITION))  
  29.         {  
  30.             RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);  
  31.             PositionComponent* pPos =  (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION,i);  
  32.   
  33.             if(pRender->sprite->getParent() == NULL)  
  34.             {  
  35.                 EntityTypeComponent* pType = (EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE,i);  
  36.                 if(pType->type != EntityTypeComponent::PLAYER)  
  37.                 {  
  38.                     pRender->sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.0/60, 5)));  
  39.                     scene->addChild(pRender->sprite);  
  40.                 }// end for PLAYER  
  41.                 else  
  42.                     scene->addChild(pRender->sprite, 10);  
  43.             }  
  44.   
  45.             pRender->sprite->setPosition(ccp(pPos->x, pPos->y));  
  46.         }  
  47.     }// end for sprite  
  48.   
  49. }// end for excute  
  50.   
  51. void RenderSystem::exit()  
  52. {  
  53.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  54.     for(unsigned int i = 0 ; i < size ; i ++)  
  55.     {  
  56.         RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);  
  57.         pRender->sprite->stopAllActions();  
  58.         pRender->sprite->removeFromParentAndCleanup(true);  
  59.     }// end for  
  60. }// end for exit  

MovementSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"MovementSystem.h"  
  2. #include"EntityMananger.h"  
  3. using namespace ShapeWar ;  
  4.   
  5. MovementSystem::MovementSystem(int _priority)  
  6.     :System(_priority)  
  7. {  
  8.   
  9. }  
  10.   
  11. MovementSystem::~MovementSystem()  
  12. {  
  13.   
  14. }  
  15.   
  16. void MovementSystem::enter()  
  17. {  
  18.   
  19. }// end for enter  
  20.   
  21. void MovementSystem::excute(float dt)  
  22. {  
  23.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  24.     for(unsigned int i =  0 ; i < size ; i ++)  
  25.     {  
  26.         _int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);  
  27.           
  28.         if((flag & (COMPONENT_POSITION | COMPONENT_VELOCITY)) == (COMPONENT_POSITION | COMPONENT_VELOCITY))  
  29.         {  
  30.             PositionComponent* pPos = (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION, i);  
  31.             VelocityComponent* pVelocity = (VelocityComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_VELOCITY, i);  
  32.   
  33.             pPos->x += (1.0 / 60) * pVelocity->vx ;  
  34.             pPos->y += (1.0 / 60) * pVelocity->vy ;  
  35.         }  
  36.     }// end for  
  37. }// end for excute  
  38.   
  39. void MovementSystem::exit()  
  40. {  
  41.   
  42. }// end for exit  

HealthSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"HealthSystem.h"  
  2. #include"EntityMananger.h"  
  3. #include"GameInfo.h"  
  4. using namespace ShapeWar ;  
  5.   
  6. HealthSystem::HealthSystem(int priority)  
  7.     :System(priority)  
  8. {  
  9.   
  10. }  
  11.   
  12. HealthSystem::~HealthSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void HealthSystem::enter()  
  18. {  
  19.   
  20. }// end for enter  
  21.   
  22. void HealthSystem::excute(float dt)  
  23. {  
  24.     //Get all the HealthComponent list  
  25.     EntityManager::Component_List* pHealth = EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH);  
  26.   
  27.     for(unsigned int entity = 0 ; entity < EntityManager::getEntityManager()->getEntitySize() ;)  
  28.     {  
  29.         HealthComponent* health = (HealthComponent*)(*pHealth)[entity] ;  
  30.   
  31.         if(health != NULL)  
  32.         {  
  33.             EntityTypeComponent* pType = (EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE, entity);  
  34.             if(pType->type == EntityTypeComponent::PLAYER)  
  35.             {  
  36.                 GameInfo::getGameInfo()->CUR_HEALTH_PLAYER = health->health ;  
  37.             }  
  38.   
  39.             if(health->health == 0)  
  40.             {  
  41.                 if((EntityManager::getEntityManager()->getEntityFlag(entity) & COMPONENT_ANIMATE) == 0)  
  42.                 {  
  43.                     switch(pType->type)  
  44.                     {  
  45.                     case EntityTypeComponent::GREEN_CUBE:  
  46.                     case EntityTypeComponent::RED_CUBE:  
  47.                         GameInfo::getGameInfo()->CUR_SCORE += 1 ;  
  48.                         break ;  
  49.                       
  50.                     case EntityTypeComponent::ORANGE_CUBE:  
  51.                         GameInfo::getGameInfo()->CUR_SCORE += 2 ;  
  52.                         break ;  
  53.   
  54.                     case EntityTypeComponent::PURPLE_CUBE:  
  55.                         GameInfo::getGameInfo()->CUR_SCORE += 3 ;  
  56.                         break ;  
  57.                     }// end switch  
  58.   
  59.                     EntityManager::getEntityManager()->removeEntity(entity);  
  60.                 }  
  61.                 else  
  62.                     entity ++ ;  
  63.             }// end if  
  64.             else  
  65.                 entity ++ ;  
  66.         }// end if  
  67.         else  
  68.             entity ++ ;  
  69.     }// end for  
  70. }// end for excute  
  71.   
  72. void HealthSystem::exit()  
  73. {  
  74.   
  75. }// end for exit  

CreatorSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"CreatorSystem.h"  
  2. #include"EntityCreator.h"  
  3. using namespace ShapeWar ;  
  4.   
  5. CreatorSystem::CreatorSystem(int _priority)  
  6.     :System(_priority),  
  7.     frames(0)  
  8. {  
  9.   
  10. }  
  11.   
  12. CreatorSystem::~CreatorSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void CreatorSystem::enter()  
  18. {  
  19.   
  20. }// end for enter  
  21.   
  22. void CreatorSystem::excute(float dt)  
  23. {  
  24.     frames ++ ;  
  25.   
  26.     static int delta = 0 ;  
  27.     delta = frames / 1800 ;  
  28.   
  29.     if(delta >= 30)  
  30.         delta = 30 ;  
  31.   
  32.     if(frames % (60 - delta ) == 0)  
  33.     {  
  34.         int value = rand()%100 ;  
  35.         float vy = -60 - (frames / 300.0) * 10 ;  
  36.   
  37.         if(0 <= value&& value < 40)  
  38.         {  
  39.             EntityCreator::createGreenCube(0, vy);  
  40.         }  
  41.         else if(40 <= value&& value < 80)  
  42.         {  
  43.             EntityCreator::createRedCube(0, vy);  
  44.         }  
  45.         else if(80 <= value && value < 90)  
  46.         {  
  47.             EntityCreator::createOrangeCube(0, 0.6*vy);  
  48.         }  
  49.         else if(90 <= value && value<100)  
  50.         {  
  51.             EntityCreator::createPurpleCube(0,0.4*vy);  
  52.         }  
  53.     }//end if  
  54.   
  55. }// end for excute  
  56.   
  57. void CreatorSystem::exit()  
  58. {  
  59.   
  60. }// end for exit  

InputSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include "InputSystem.h"  
  2. #include "EntityMananger.h"  
  3. #include "EntityCreator.h"  
  4. #include "AudioSystem.h"  
  5. using namespace ShapeWar ;  
  6.   
  7. InputSystem::InputSystem(int _priority)  
  8.     :System(_priority)  
  9. {  
  10.   
  11. }  
  12.   
  13. InputSystem::~InputSystem()  
  14. {  
  15.   
  16. }  
  17.   
  18. void InputSystem::enter()  
  19. {  
  20.   
  21. }// end for enter  
  22.   
  23. void InputSystem::excute(float dt)  
  24. {  
  25.     //Get the Component list  
  26.     EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);  
  27.     EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);  
  28.   
  29.     //Find the player and the un-shooted ball  
  30.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  31.   
  32.     int player = -1 , ball = -1 ;  
  33.     for(unsigned int i = 0 ; i < size ; i ++)  
  34.     {  
  35.         unsigned int type = ((EntityTypeComponent*)(*pType)[i])->type ;  
  36.         if(type == EntityTypeComponent::PLAYER)  
  37.         {  
  38.             player = i ;  
  39.         }// end if  
  40.   
  41.         if(type == EntityTypeComponent::SPHERE_BALL)  
  42.         {  
  43.             _int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);  
  44.             if((flag & COMPONENT_VELOCITY) == 0)  
  45.             {  
  46.                 ball = i ;  
  47.             } // end if  
  48.         }// end if  
  49.   
  50.         if(player != -1 && ball != -1)  
  51.             break ;  
  52.     }// end for  
  53.   
  54.     PositionComponent* pPlayer_Pos = NULL ;  
  55.     PositionComponent* pBall_Pos = NULL ;  
  56.   
  57.     if(player != -1)  
  58.         pPlayer_Pos = (PositionComponent*)(*pPos)[player] ;  
  59.     if(ball != -1)  
  60.         pBall_Pos = (PositionComponent*)(*pPos)[ball] ;  
  61.   
  62.     if(GetKeyState(VK_RIGHT) & 0x8000)  
  63.     {  
  64.         if(pPlayer_Pos != NULL)  
  65.         {  
  66.             pPlayer_Pos->x += 5 ;  
  67.             if(pPlayer_Pos->x >= 320 - 22)  
  68.                 pPlayer_Pos->x = 320 - 22 ;  
  69.   
  70.             if(pBall_Pos != NULL)  
  71.                 pBall_Pos->x = pPlayer_Pos->x ;  
  72.         }  
  73.     }else if(GetKeyState(VK_LEFT)&0x8000)  
  74.     {  
  75.         if(pPlayer_Pos != NULL)  
  76.         {  
  77.             pPlayer_Pos->x -= 5 ;  
  78.             if(pPlayer_Pos->x <= 22)  
  79.                 pPlayer_Pos->x = 22 ;  
  80.   
  81.             if(pBall_Pos != NULL)  
  82.                 pBall_Pos->x = pPlayer_Pos->x ;  
  83.         }  
  84.     }  
  85.   
  86.     static int nFrame = 0 ;  
  87.     if((GetKeyState(VK_SPACE)& 0x8000) && (nFrame >= 15))  
  88.     {  
  89.         VelocityComponent* pVelocity = new VelocityComponent();  
  90.         pVelocity->vx = 0 ;  
  91.         pVelocity->vy = 600 ;  
  92.         EntityManager::getEntityManager()->addComponent(pVelocity, COMPONENT_VELOCITY, ball);  
  93.   
  94.         //Create another ball  
  95.         EntityCreator::createSphereBall(pPlayer_Pos->x, pPlayer_Pos->y);  
  96.   
  97.         //Player Effect  
  98.         AudioSystem::sharedAudioSystem()->playSound("Shoot.wav");  
  99.         nFrame = 0 ;  
  100.     }  
  101.   
  102.     nFrame ++ ;  
  103.   
  104. }// end for excute  
  105.   
  106. void InputSystem::exit()  
  107. {  
  108.   
  109. }// end for exit  

CollidDetectionSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"CollidDetectionSystem.h"  
  2. #include"EntityMananger.h"  
  3. #include"AudioSystem.h"  
  4. using namespace ShapeWar ;  
  5.   
  6. CollidDetectionSystem::CollidDetectionSystem(int _priority)  
  7.     :System(_priority)  
  8. {  
  9.   
  10. }  
  11.   
  12. CollidDetectionSystem::~CollidDetectionSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void CollidDetectionSystem::enter()  
  18. {  
  19.   
  20. }// end for enter  
  21.   
  22. void CollidDetectionSystem::excute(float dt)  
  23. {  
  24.     //Get all PositionComponent list  
  25.     EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);  
  26.   
  27.     //Get all the CollidableComponent list  
  28.     EntityManager::Component_List* pCollid = EntityManager::getEntityManager()->getComponentList(COMPONENT_COLLID);  
  29.   
  30.     //Get all the EntityTypeComponent list  
  31.     EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);  
  32.   
  33.     //Get all the HealthComponent list  
  34.     EntityManager::Component_List* pHealth = EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH);  
  35.   
  36.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  37.   
  38.     //Find all sphere ball  
  39.     std::vector<unsigned int> index_array ;  
  40.     for(unsigned int i = 0 ; i < size ; i ++)  
  41.     {  
  42.         if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)  
  43.         {  
  44.             if((EntityManager::getEntityManager()->getEntityFlag(i) & COMPONENT_VELOCITY) == COMPONENT_VELOCITY)  
  45.             {  
  46.                 index_array.push_back(i);  
  47.             }// end if  
  48.         }// end if  
  49.     }// end for  
  50.   
  51.     for(unsigned int i = 0 ; i < index_array.size() ; i ++)  
  52.     {  
  53.         CollidableComponent* collidAreaA = ((CollidableComponent*)((*pCollid)[index_array[i]])) ;  
  54.         PositionComponent* posA = ((PositionComponent*)((*pPos)[index_array[i]])) ;  
  55.         collidAreaA->min_x = posA->x - 16 ;  
  56.         collidAreaA->min_y = posA->y - 16 ;  
  57.         collidAreaA->max_x = posA->x + 16 ;  
  58.         collidAreaA->max_y = posA->y + 16 ;  
  59.   
  60.         size = EntityManager::getEntityManager()->getEntitySize();  
  61.         for(unsigned int j = 0 ; j < size ; j ++)  
  62.         {  
  63.             if((EntityManager::getEntityManager()->getEntityFlag(j) & COMPONENT_COLLID) == COMPONENT_COLLID &&  
  64.                 ((EntityTypeComponent*)(*pType)[j])->type != EntityTypeComponent::SPHERE_BALL)  
  65.             {  
  66.                 CollidableComponent* collidAreaB = ((CollidableComponent*)((*pCollid)[j])) ;  
  67.                 PositionComponent* posB = ((PositionComponent*)((*pPos)[j])) ;  
  68.                 collidAreaB->min_x = posB->x - 16 ;  
  69.                 collidAreaB->min_y = posB->y - 16 ;  
  70.                 collidAreaB->max_x = posB->x + 16 ;  
  71.                 collidAreaB->max_y = posB->y + 16 ;  
  72.   
  73.                 if(collidAreaA->min_x > collidAreaB->max_x  
  74.                     ||collidAreaA->max_x < collidAreaB->min_x) continue ;  
  75.                 if(collidAreaA->min_y > collidAreaB->max_y ||  
  76.                     collidAreaA->max_y < collidAreaB->min_y) continue ;  
  77.   
  78.                 HealthComponent* cube = (HealthComponent*)(*pHealth)[j] ;  
  79.                 cube->health -- ;  
  80.   
  81.                 if(cube->health == 0)  
  82.                 {  
  83.                     AnimateComponent* pAnimate = new AnimateComponent();  
  84.                     pAnimate->animate = new CCAnimate();  
  85.                       
  86.                     CCAnimation* pAnimation =  CCAnimation::create();  
  87.                       
  88.                     for(int i = 0 ; i < 10 ; i ++)  
  89.                     {  
  90.                         char buffer[32] ;  
  91.                         sprintf(buffer,"Explosion000%d.png",i);  
  92.                         pAnimation->addSpriteFrameWithFileName(buffer);  
  93.                     }// end for  
  94.   
  95.                     pAnimation->setDelayPerUnit(1.0/10);  
  96.                     pAnimate->animate->initWithAnimation(pAnimation);  
  97.                     pAnimate->frames = 60 ;  
  98.   
  99.                     //Add the Animate Component to the entity  
  100.                     EntityManager::getEntityManager()->addComponent(pAnimate, COMPONENT_ANIMATE, j);  
  101.   
  102.                     //Remove the CollidDetection Component  
  103.                     EntityManager::getEntityManager()->removeComponent(COMPONENT_COLLID, j);  
  104.   
  105.                     //Remove the Velocity Component  
  106.                     EntityManager::getEntityManager()->removeComponent(COMPONENT_VELOCITY, j);  
  107.   
  108.                 }// end if  
  109.   
  110.                 HealthComponent* ball = (HealthComponent*)(*pHealth)[index_array[i]] ;  
  111.                 ball->health -- ;  
  112.   
  113.                 //Play hurt effect  
  114.                 AudioSystem::sharedAudioSystem()->playSound("Hurt.wav");  
  115.   
  116.                 break ;  
  117.             }// end if  
  118.         }// end for cube  
  119.     }// end for sphere ball  
  120. }// end for excute  
  121.   
  122. void CollidDetectionSystem::exit()  
  123. {  
  124.   
  125. }// end for exit  

BoundaryCheckSystem

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include"BoundaryCheckSystem.h"  
  2. #include"EntityMananger.h"  
  3. using namespace ShapeWar ;  
  4.   
  5. BoundaryCheckSystem::BoundaryCheckSystem(int priority)  
  6.     :System(priority)  
  7. {  
  8.   
  9. }  
  10.   
  11. BoundaryCheckSystem::~BoundaryCheckSystem()  
  12. {  
  13.   
  14. }  
  15.   
  16. void BoundaryCheckSystem::enter()  
  17. {  
  18.   
  19. }// end for enter  
  20.   
  21. void BoundaryCheckSystem::excute(float dt)  
  22. {  
  23.     //Get all PositionComponent list  
  24.     EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);  
  25.   
  26.     //Get all the EntityTypeComponent list  
  27.     EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);  
  28.       
  29.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  30.   
  31.     //Find the Player's health Component  
  32.     unsigned int player_entity = -1 ;  
  33.     for(int i = 0 ; i < size ; i ++)  
  34.     {  
  35.         if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::PLAYER)  
  36.         {  
  37.             player_entity = i ;  
  38.             break ;  
  39.         }  
  40.     }// end for  
  41.   
  42.     HealthComponent * health = (HealthComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_HEALTH, player_entity);  
  43.   
  44.     //Check if the entity is out of the screen  
  45.     for(unsigned int i = 0 ; i < size ; )  
  46.     {  
  47.         if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)  
  48.         {  
  49.             if(((PositionComponent*)(*pPos)[i])->y > 480)  
  50.             {  
  51.                 EntityManager::getEntityManager()->removeEntity(i);  
  52.                 size -= 1 ;  
  53.                 continue ;  
  54.             }  
  55.         }// end if for sphere ball  
  56.         else  
  57.         {  
  58.             if(((PositionComponent*)(*pPos)[i])->y < 0)  
  59.             {  
  60.                 EntityManager::getEntityManager()->removeEntity(i);  
  61.                 size -= 1 ;  
  62.                 health->health-- ;  
  63.                 continue ;  
  64.             }  
  65.         }  
  66.   
  67.         i ++ ;  
  68.     }// end for  
  69. }// end for excute  
  70.   
  71. void BoundaryCheckSystem::exit()  
  72. {  
  73.       
  74. }// end for exit  

            系统内部是如何工作的,不是本文章讨论的范畴。这篇文章旨在告诉读者,我们可以通过ECS系统,实现更加弹性的设计。通过使用组合的方法,大大降低系统的耦合性。同时,这里将数据和处理过程,通过组建和系统的方法实现了分离。通过这样的系统,我们很容易的能够实现网络游戏,因为只需要对组件数据进行单独的传输即可,并且很容易的实现诸如关卡保存,这样的内容。

            但是,任何事情都是双面的,在带来这些好处的同时,在另外的方面也会带来限制。


系统缺点

           通过上面的描述,我们大概可以明确这样的系统有如下的缺点:

  •  内存利用较低。我们在容器中为每一个实体都开辟了同样大的空间,如果某个实体并不具有那样的组件的时候,那个空间依然为它保留着,这浪费了大量的空间
  •  同一个实体,没有办法拥有同一个组件的两份实例。也就说,对于像动画这样的组件,一个实体,可能不只有一个动画属性。它可能需要在死亡时,同时播放两种动画,那么这个系统就没有办法完成这样的工作。
  • 最重要的一个缺点就是性能问题。读者可能发现,系统和实体的交互方式,完全是系统主动的轮询,来进行系统的处理。我们知道,高效的设计方法,应该是让实体在有需要的时候,调用系统来进行工作。如果系统持续的运行,在很多情况下,系统并没有做什么有效的工作。所以,应该将这种主动轮询的方式改成由事件驱动的可能更好一点。但是,博主暂时没有想到如何设计这样的系统,可能在后面的实践中,掌握这样的设计方法的时候,再来向大家讲述。



        好了,ECS架构实践的第一篇博客就到此结束了。

        如果您有什么不明白的地方,或者发现了文中设计上的缺陷,欢迎大家在评论中指出。毕竟,旁观者清,当局者迷。希望能够和大家互相的学习!互相进步!

        这个游戏的源代码和程序以及上传至CSDN,感兴趣的同学可以自行下载来阅读和试玩,不要忘了在评论中给出你获得的最高分哦,大家比比看谁的反应是最好的哦哦!!!

       ShapeWar_SourceCode.zip

       ShapeWar_exe.zip(部分资源来至于网络,请大家不要用于商业用途哦!!!)

0 0
原创粉丝点击