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



引言

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

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

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


ShapeWar

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

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

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

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


架构设计

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

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

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

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


组件

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

  1. //File:Component.h
  2. //------------------------------------------------------------------
  3. //declaration:Copyright(c),byXJ,2014.Allrightreserved.
  4. //brief:ThisfilewilldefinetheComponentbaseclassofthe
  5. //Entity-Component-System.
  6. //author:XJ
  7. //date:2014/6/8
  8. //version:1.0
  9. //-------------------------------------------------------------------
  10. #pragmaonce
  11. #include<cocos2d.h>
  12. usingnamespacecocos2d;
  13. namespaceShapeWar
  14. {
  15. #defineCOMPONENT_NONE0x0
  16. classComponent
  17. {
  18. public:
  19. Component(){}
  20. virtual~Component(){}
  21. };
  22. /**
  23. *DefinetheRenderComponent
  24. */
  25. #defineCOMPONENT_RENDER(1<<1)
  26. classRenderComponent:publicComponent
  27. {
  28. public:
  29. RenderComponent(){}
  30. ~RenderComponent()
  31. {
  32. sprite->removeFromParentAndCleanup(true);
  33. deletesprite;
  34. }
  35. public:
  36. CCSprite*sprite;
  37. };
  38. /**
  39. *DefinethePositionComponent
  40. */
  41. #defineCOMPONENT_POSITION(1<<2)
  42. classPositionComponent:publicComponent
  43. {
  44. public:
  45. PositionComponent(){}
  46. ~PositionComponent(){}
  47. public:
  48. floatx;
  49. floaty;
  50. };
  51. /**
  52. *DefinetheVelocityComponent
  53. */
  54. #defineCOMPONENT_VELOCITY(1<<3)
  55. classVelocityComponent:publicComponent
  56. {
  57. public:
  58. VelocityComponent(){}
  59. ~VelocityComponent(){}
  60. public:
  61. floatvx;
  62. floatvy;
  63. };
  64. /**
  65. *DefinetheHealthComponent
  66. */
  67. #defineCOMPONENT_HEALTH(1<<4)
  68. classHealthComponent:publicComponent
  69. {
  70. public:
  71. HealthComponent(){}
  72. ~HealthComponent(){}
  73. public:
  74. unsignedinthealth;
  75. };
  76. /**
  77. *DefinetheCollidableComponent
  78. *brief:UsetheAABB'sMin-Maxrepresentation
  79. */
  80. #defineCOMPONENT_COLLID(1<<5)
  81. classCollidableComponent:publicComponent
  82. {
  83. public:
  84. CollidableComponent(){}
  85. ~CollidableComponent(){}
  86. public:
  87. floatmin_x;
  88. floatmin_y;
  89. floatmax_x;
  90. floatmax_y;
  91. };
  92. /**
  93. *DefinetheEntityTypecomponent
  94. *brief:Thiscomponentwillindicatewhichtypetheentityis.
  95. */
  96. #defineCOMPONENT_ENTITY_TYPE(1<<6)
  97. classEntityTypeComponent:publicComponent
  98. {
  99. public:
  100. EntityTypeComponent(){}
  101. ~EntityTypeComponent(){}
  102. public:
  103. staticconstunsignedintRED_CUBE=(1<<1);
  104. staticconstunsignedintPURPLE_CUBE=(1<<2);
  105. staticconstunsignedintORANGE_CUBE=(1<<3);
  106. staticconstunsignedintGREEN_CUBE=(1<<4);
  107. staticconstunsignedintSPHERE_BALL=(1<<5);
  108. staticconstunsignedintPLAYER=(1<<6);
  109. public:
  110. unsignedinttype;
  111. };
  112. /**
  113. *DefinetheAnimateComponent
  114. */
  115. #defineCOMPONENT_ANIMATE(1<<7)
  116. classAnimateComponent:publicComponent
  117. {
  118. public:
  119. AnimateComponent(){}
  120. ~AnimateComponent(){}
  121. public:
  122. cocos2d::CCAnimate*animate;
  123. unsignedframes;
  124. };
  125. };
从上面的代码中,大家可以看到,我首先定义了一个基类Component,然后让所有的组件都继承于这个基类。这里,我并没有用到继承,读者可以发现Component中什么内容也没有。 我将其他组件继承于Component的组要原因是能够将他们统一的进行处理,仅此而已。

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

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

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


实体

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

  1. //File:EntityManager
  2. //------------------------------------------------------------------
  3. //declaration:Copyright(c),2014.Allrightreserved.
  4. //brief:ThisfilewilldefinetheEntityoftheEntity-Componet-
  5. //Systemandtheentitymanager.
  6. //author:XJ
  7. //date:2014/6/8
  8. //version:1.0
  9. //-------------------------------------------------------------------
  10. #pragmaonce
  11. #include<vector>
  12. #include"Component.h"
  13. usingnamespacestd;
  14. namespaceShapeWar
  15. {
  16. /**
  17. *DefinetheEntityManager
  18. */
  19. classEntityManager
  20. {
  21. private:
  22. EntityManager();
  23. ~EntityManager();
  24. /**Singletongetter*/
  25. public:
  26. staticEntityManager*getEntityManager();
  27. /**Coremethod*/
  28. public:
  29. /**
  30. *Createanemptyentity
  31. */
  32. _int64createEntity();
  33. /**
  34. *Removeanentity
  35. */
  36. voidremoveEntity(_int64entity);
  37. /**
  38. *Registercomponent
  39. *brief:Thismethodwillmaketheentitymanagertoallocthememorytostore
  40. *theregistedcomponet.IfyouwanttouseonecomponetintheECS,you
  41. *mustregisteditatthestarttime.
  42. */
  43. voidregistComponents(_int64component_size);
  44. /**
  45. *Addancomponenttotheentity
  46. */
  47. voidaddComponent(Component*component,_int64component_type,_int64entity);
  48. /**
  49. *Removeancomponentoftheentity
  50. */
  51. voidremoveComponent(_int64component_type,_int64entity);
  52. /**
  53. *Getcomponentlist
  54. */
  55. std::vector<Component*>*getComponentList(_int64component_type)const;
  56. /**
  57. *Getthespecificedcomponentoftheentity
  58. */
  59. Component*getComponent(_int64component_type,_int64entity);
  60. /**
  61. *Getentityflag
  62. */
  63. _int64getEntityFlag(_int64entity)const;
  64. /**
  65. *Setentityflag
  66. */
  67. voidsetEntityFlag(_int64entity,_int64entity_type);
  68. /**
  69. *Gettheentitysize
  70. */
  71. unsignedintgetEntitySize()const;
  72. /**
  73. *DefinetheComponent_List
  74. */
  75. typedefstd::vector<Component*>Component_List;
  76. private:
  77. /**
  78. *Destroyallthecomponent
  79. */
  80. void_destroy();
  81. private:
  82. std::vector<_int64>m_EntityFlagArray;//ContaintheEntityflag
  83. <prename="code"class="cpp">std::vector<Component_List>m_ComponentContainer;//Containalltheentity

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

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

  1. std::vector<Component_List>m_ComponentContainer;//Containalltheentity

而Component_List定义为如下:
  1. /**
  2. *DefinetheComponent_List
  3. */
  4. typedefstd::vector<Component*>Component_List;
也就是说,这个ComponentContainer,包含了所有的在游戏中使用的组件实例。同一种组件实例,放在同一个Component_List中,然后不同的Component_List,放在Component_Container中。如果读者对这个不是很清楚的话,可以看下面的图片:

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

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

  1. std::vector<_int64>m_EntityFlagArray;//ContaintheEntityflag
这个数组,保存了相对应的实体中组件的标示符。还记得我们在组件那一节讲述的组件表示符吗?通过这个数组,我们保存了每个实体对应的组件中有哪些组件。比如说,在这个数组下标为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 entity)

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

void removeComponent(_int64 component_type,_int64 entity);

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


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

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

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

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

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


系统

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

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

  1. /**
  2. *Definethebasesystemclass.Allsystemwillinheritfromthisbaseclass.
  3. */
  4. classSystem
  5. {
  6. public:
  7. System(int_priority);
  8. virtual~System();
  9. public:
  10. virtualvoidenter()=0;
  11. virtualvoidexcute(floatdt)=0;
  12. virtualvoidexit()=0;
  13. public:
  14. intpriority;
  15. };

在这个抽象的系统中,我定义了一个优先级,这样,我们就可以定义哪一些系统需要在另外一些系统之前进行运行。有了系统之后,我们就需要一个管理的方式,所以,在定义了一个系统管理器,如下所示:
  1. /**
  2. *Definethesystemmanager
  3. */
  4. classSystemManager
  5. {
  6. private:
  7. SystemManager();
  8. ~SystemManager();
  9. /**Singletongetter*/
  10. public:
  11. staticSystemManager*getSystemManager();
  12. /**Coremethod*/
  13. public:
  14. /**
  15. *Addonesystemtothesystemlist
  16. */
  17. voidaddSystem(System*system);
  18. /**
  19. *Updateallthesystem
  20. */
  21. voidupdate(floatdt);
  22. /**
  23. *Pauseallthesystem
  24. */
  25. voidpause();
  26. /**
  27. *Resumeallthesystem
  28. */
  29. voidresume();
  30. private:
  31. /**
  32. *Destroyallthesystems
  33. */
  34. void_destroy();
  35. private:
  36. std::vector<System*>system_list;
  37. boolbPaused;
  38. };

这个类同样也是单例的,用户可以通过调用addSystem来添加系统到系统管理器中。系统管理器,会在每一帧,调用update方法,update方法如下所示:
  1. voidSystemManager::update(floatdt)
  2. {
  3. if(bPaused==true)
  4. return;
  5. //Excuteallthesystem
  6. for(inti=0;i<system_list.size();i++)
  7. {
  8. system_list[i]->excute(dt);
  9. }//endfor
  10. }//endforupdate

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

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

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

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

RenderSystem

  1. #include"RenderSystem.h"
  2. #include"EntityMananger.h"
  3. usingnamespaceShapeWar;
  4. RenderSystem::RenderSystem(int_priority,CCNode*_scene)
  5. :System(_priority),
  6. scene(_scene)
  7. {
  8. }
  9. RenderSystem::~RenderSystem()
  10. {
  11. }
  12. voidRenderSystem::enter()
  13. {
  14. }//edforenter
  15. voidRenderSystem::excute(floatdt)
  16. {
  17. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  18. for(unsignedinti=0;i<size;i++)
  19. {
  20. _int64flag=EntityManager::getEntityManager()->getEntityFlag(i);
  21. if((flag&(COMPONENT_RENDER|COMPONENT_POSITION))==(COMPONENT_RENDER|COMPONENT_POSITION))
  22. {
  23. RenderComponent*pRender=(RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);
  24. PositionComponent*pPos=(PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION,i);
  25. if(pRender->sprite->getParent()==NULL)
  26. {
  27. EntityTypeComponent*pType=(EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE,i);
  28. if(pType->type!=EntityTypeComponent::PLAYER)
  29. {
  30. pRender->sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.0/60,5)));
  31. scene->addChild(pRender->sprite);
  32. }//endforPLAYER
  33. else
  34. scene->addChild(pRender->sprite,10);
  35. }
  36. pRender->sprite->setPosition(ccp(pPos->x,pPos->y));
  37. }
  38. }//endforsprite
  39. }//endforexcute
  40. voidRenderSystem::exit()
  41. {
  42. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  43. for(unsignedinti=0;i<size;i++)
  44. {
  45. RenderComponent*pRender=(RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);
  46. pRender->sprite->stopAllActions();
  47. pRender->sprite->removeFromParentAndCleanup(true);
  48. }//endfor
  49. }//endforexit

MovementSystem

  1. #include"MovementSystem.h"
  2. #include"EntityMananger.h"
  3. usingnamespaceShapeWar;
  4. MovementSystem::MovementSystem(int_priority)
  5. :System(_priority)
  6. {
  7. }
  8. MovementSystem::~MovementSystem()
  9. {
  10. }
  11. voidMovementSystem::enter()
  12. {
  13. }//endforenter
  14. voidMovementSystem::excute(floatdt)
  15. {
  16. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  17. for(unsignedinti=0;i<size;i++)
  18. {
  19. _int64flag=EntityManager::getEntityManager()->getEntityFlag(i);
  20. if((flag&(COMPONENT_POSITION|COMPONENT_VELOCITY))==(COMPONENT_POSITION|COMPONENT_VELOCITY))
  21. {
  22. PositionComponent*pPos=(PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION,i);
  23. VelocityComponent*pVelocity=(VelocityComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_VELOCITY,i);
  24. pPos->x+=(1.0/60)*pVelocity->vx;
  25. pPos->y+=(1.0/60)*pVelocity->vy;
  26. }
  27. }//endfor
  28. }//endforexcute
  29. voidMovementSystem::exit()
  30. {
  31. }//endforexit

HealthSystem

  1. #include"HealthSystem.h"
  2. #include"EntityMananger.h"
  3. #include"GameInfo.h"
  4. usingnamespaceShapeWar;
  5. HealthSystem::HealthSystem(intpriority)
  6. :System(priority)
  7. {
  8. }
  9. HealthSystem::~HealthSystem()
  10. {
  11. }
  12. voidHealthSystem::enter()
  13. {
  14. }//endforenter
  15. voidHealthSystem::excute(floatdt)
  16. {
  17. //GetalltheHealthComponentlist
  18. EntityManager::Component_List*pHealth=EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH);
  19. for(unsignedintentity=0;entity<EntityManager::getEntityManager()->getEntitySize();)
  20. {
  21. HealthComponent*health=(HealthComponent*)(*pHealth)[entity];
  22. if(health!=NULL)
  23. {
  24. EntityTypeComponent*pType=(EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE,entity);
  25. if(pType->type==EntityTypeComponent::PLAYER)
  26. {
  27. GameInfo::getGameInfo()->CUR_HEALTH_PLAYER=health->health;
  28. }
  29. if(health->health==0)
  30. {
  31. if((EntityManager::getEntityManager()->getEntityFlag(entity)&COMPONENT_ANIMATE)==0)
  32. {
  33. switch(pType->type)
  34. {
  35. caseEntityTypeComponent::GREEN_CUBE:
  36. caseEntityTypeComponent::RED_CUBE:
  37. GameInfo::getGameInfo()->CUR_SCORE+=1;
  38. break;
  39. caseEntityTypeComponent::ORANGE_CUBE:
  40. GameInfo::getGameInfo()->CUR_SCORE+=2;
  41. break;
  42. caseEntityTypeComponent::PURPLE_CUBE:
  43. GameInfo::getGameInfo()->CUR_SCORE+=3;
  44. break;
  45. }//endswitch
  46. EntityManager::getEntityManager()->removeEntity(entity);
  47. }
  48. else
  49. entity++;
  50. }//endif
  51. else
  52. entity++;
  53. }//endif
  54. else
  55. entity++;
  56. }//endfor
  57. }//endforexcute
  58. voidHealthSystem::exit()
  59. {
  60. }//endforexit

CreatorSystem

  1. #include"CreatorSystem.h"
  2. #include"EntityCreator.h"
  3. usingnamespaceShapeWar;
  4. CreatorSystem::CreatorSystem(int_priority)
  5. :System(_priority),
  6. frames(0)
  7. {
  8. }
  9. CreatorSystem::~CreatorSystem()
  10. {
  11. }
  12. voidCreatorSystem::enter()
  13. {
  14. }//endforenter
  15. voidCreatorSystem::excute(floatdt)
  16. {
  17. frames++;
  18. staticintdelta=0;
  19. delta=frames/1800;
  20. if(delta>=30)
  21. delta=30;
  22. if(frames%(60-delta)==0)
  23. {
  24. intvalue=rand()%100;
  25. floatvy=-60-(frames/300.0)*10;
  26. if(0<=value&&value<40)
  27. {
  28. EntityCreator::createGreenCube(0,vy);
  29. }
  30. elseif(40<=value&&value<80)
  31. {
  32. EntityCreator::createRedCube(0,vy);
  33. }
  34. elseif(80<=value&&value<90)
  35. {
  36. EntityCreator::createOrangeCube(0,0.6*vy);
  37. }
  38. elseif(90<=value&&value<100)
  39. {
  40. EntityCreator::createPurpleCube(0,0.4*vy);
  41. }
  42. }//endif
  43. }//endforexcute
  44. voidCreatorSystem::exit()
  45. {
  46. }//endforexit

InputSystem

  1. #include"InputSystem.h"
  2. #include"EntityMananger.h"
  3. #include"EntityCreator.h"
  4. #include"AudioSystem.h"
  5. usingnamespaceShapeWar;
  6. InputSystem::InputSystem(int_priority)
  7. :System(_priority)
  8. {
  9. }
  10. InputSystem::~InputSystem()
  11. {
  12. }
  13. voidInputSystem::enter()
  14. {
  15. }//endforenter
  16. voidInputSystem::excute(floatdt)
  17. {
  18. //GettheComponentlist
  19. EntityManager::Component_List*pPos=EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);
  20. EntityManager::Component_List*pType=EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);
  21. //Findtheplayerandtheun-shootedball
  22. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  23. intplayer=-1,ball=-1;
  24. for(unsignedinti=0;i<size;i++)
  25. {
  26. unsignedinttype=((EntityTypeComponent*)(*pType)[i])->type;
  27. if(type==EntityTypeComponent::PLAYER)
  28. {
  29. player=i;
  30. }//endif
  31. if(type==EntityTypeComponent::SPHERE_BALL)
  32. {
  33. _int64flag=EntityManager::getEntityManager()->getEntityFlag(i);
  34. if((flag&COMPONENT_VELOCITY)==0)
  35. {
  36. ball=i;
  37. }//endif
  38. }//endif
  39. if(player!=-1&&ball!=-1)
  40. break;
  41. }//endfor
  42. PositionComponent*pPlayer_Pos=NULL;
  43. PositionComponent*pBall_Pos=NULL;
  44. if(player!=-1)
  45. pPlayer_Pos=(PositionComponent*)(*pPos)[player];
  46. if(ball!=-1)
  47. pBall_Pos=(PositionComponent*)(*pPos)[ball];
  48. if(GetKeyState(VK_RIGHT)&0x8000)
  49. {
  50. if(pPlayer_Pos!=NULL)
  51. {
  52. pPlayer_Pos->x+=5;
  53. if(pPlayer_Pos->x>=320-22)
  54. pPlayer_Pos->x=320-22;
  55. if(pBall_Pos!=NULL)
  56. pBall_Pos->x=pPlayer_Pos->x;
  57. }
  58. }elseif(GetKeyState(VK_LEFT)&0x8000)
  59. {
  60. if(pPlayer_Pos!=NULL)
  61. {
  62. pPlayer_Pos->x-=5;
  63. if(pPlayer_Pos->x<=22)
  64. pPlayer_Pos->x=22;
  65. if(pBall_Pos!=NULL)
  66. pBall_Pos->x=pPlayer_Pos->x;
  67. }
  68. }
  69. staticintnFrame=0;
  70. if((GetKeyState(VK_SPACE)&0x8000)&&(nFrame>=15))
  71. {
  72. VelocityComponent*pVelocity=newVelocityComponent();
  73. pVelocity->vx=0;
  74. pVelocity->vy=600;
  75. EntityManager::getEntityManager()->addComponent(pVelocity,COMPONENT_VELOCITY,ball);
  76. //Createanotherball
  77. EntityCreator::createSphereBall(pPlayer_Pos->x,pPlayer_Pos->y);
  78. //PlayerEffect
  79. AudioSystem::sharedAudioSystem()->playSound("Shoot.wav");
  80. nFrame=0;
  81. }
  82. nFrame++;
  83. }//endforexcute
  84. voidInputSystem::exit()
  85. {
  86. }//endforexit

CollidDetectionSystem

  1. #include"CollidDetectionSystem.h"
  2. #include"EntityMananger.h"
  3. #include"AudioSystem.h"
  4. usingnamespaceShapeWar;
  5. CollidDetectionSystem::CollidDetectionSystem(int_priority)
  6. :System(_priority)
  7. {
  8. }
  9. CollidDetectionSystem::~CollidDetectionSystem()
  10. {
  11. }
  12. voidCollidDetectionSystem::enter()
  13. {
  14. }//endforenter
  15. voidCollidDetectionSystem::excute(floatdt)
  16. {
  17. //GetallPositionComponentlist
  18. EntityManager::Component_List*pPos=EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);
  19. //GetalltheCollidableComponentlist
  20. EntityManager::Component_List*pCollid=EntityManager::getEntityManager()->getComponentList(COMPONENT_COLLID);
  21. //GetalltheEntityTypeComponentlist
  22. EntityManager::Component_List*pType=EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);
  23. //GetalltheHealthComponentlist
  24. EntityManager::Component_List*pHealth=EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH);
  25. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  26. //Findallsphereball
  27. std::vector<unsignedint>index_array;
  28. for(unsignedinti=0;i<size;i++)
  29. {
  30. if(((EntityTypeComponent*)(*pType)[i])->type==EntityTypeComponent::SPHERE_BALL)
  31. {
  32. if((EntityManager::getEntityManager()->getEntityFlag(i)&COMPONENT_VELOCITY)==COMPONENT_VELOCITY)
  33. {
  34. index_array.push_back(i);
  35. }//endif
  36. }//endif
  37. }//endfor
  38. for(unsignedinti=0;i<index_array.size();i++)
  39. {
  40. CollidableComponent*collidAreaA=((CollidableComponent*)((*pCollid)[index_array[i]]));
  41. PositionComponent*posA=((PositionComponent*)((*pPos)[index_array[i]]));
  42. collidAreaA->min_x=posA->x-16;
  43. collidAreaA->min_y=posA->y-16;
  44. collidAreaA->max_x=posA->x+16;
  45. collidAreaA->max_y=posA->y+16;
  46. size=EntityManager::getEntityManager()->getEntitySize();
  47. for(unsignedintj=0;j<size;j++)
  48. {
  49. if((EntityManager::getEntityManager()->getEntityFlag(j)&COMPONENT_COLLID)==COMPONENT_COLLID&&
  50. ((EntityTypeComponent*)(*pType)[j])->type!=EntityTypeComponent::SPHERE_BALL)
  51. {
  52. CollidableComponent*collidAreaB=((CollidableComponent*)((*pCollid)[j]));
  53. PositionComponent*posB=((PositionComponent*)((*pPos)[j]));
  54. collidAreaB->min_x=posB->x-16;
  55. collidAreaB->min_y=posB->y-16;
  56. collidAreaB->max_x=posB->x+16;
  57. collidAreaB->max_y=posB->y+16;
  58. if(collidAreaA->min_x>collidAreaB->max_x
  59. ||collidAreaA->max_x<collidAreaB->min_x)continue;
  60. if(collidAreaA->min_y>collidAreaB->max_y||
  61. collidAreaA->max_y<collidAreaB->min_y)continue;
  62. HealthComponent*cube=(HealthComponent*)(*pHealth)[j];
  63. cube->health--;
  64. if(cube->health==0)
  65. {
  66. AnimateComponent*pAnimate=newAnimateComponent();
  67. pAnimate->animate=newCCAnimate();
  68. CCAnimation*pAnimation=CCAnimation::create();
  69. for(inti=0;i<10;i++)
  70. {
  71. charbuffer[32];
  72. sprintf(buffer,"Explosion000%d.png",i);
  73. pAnimation->addSpriteFrameWithFileName(buffer);
  74. }//endfor
  75. pAnimation->setDelayPerUnit(1.0/10);
  76. pAnimate->animate->initWithAnimation(pAnimation);
  77. pAnimate->frames=60;
  78. //AddtheAnimateComponenttotheentity
  79. EntityManager::getEntityManager()->addComponent(pAnimate,COMPONENT_ANIMATE,j);
  80. //RemovetheCollidDetectionComponent
  81. EntityManager::getEntityManager()->removeComponent(COMPONENT_COLLID,j);
  82. //RemovetheVelocityComponent
  83. EntityManager::getEntityManager()->removeComponent(COMPONENT_VELOCITY,j);
  84. }//endif
  85. HealthComponent*ball=(HealthComponent*)(*pHealth)[index_array[i]];
  86. ball->health--;
  87. //Playhurteffect
  88. AudioSystem::sharedAudioSystem()->playSound("Hurt.wav");
  89. break;
  90. }//endif
  91. }//endforcube
  92. }//endforsphereball
  93. }//endforexcute
  94. voidCollidDetectionSystem::exit()
  95. {
  96. }//endforexit

BoundaryCheckSystem

  1. #include"BoundaryCheckSystem.h"
  2. #include"EntityMananger.h"
  3. usingnamespaceShapeWar;
  4. BoundaryCheckSystem::BoundaryCheckSystem(intpriority)
  5. :System(priority)
  6. {
  7. }
  8. BoundaryCheckSystem::~BoundaryCheckSystem()
  9. {
  10. }
  11. voidBoundaryCheckSystem::enter()
  12. {
  13. }//endforenter
  14. voidBoundaryCheckSystem::excute(floatdt)
  15. {
  16. //GetallPositionComponentlist
  17. EntityManager::Component_List*pPos=EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);
  18. //GetalltheEntityTypeComponentlist
  19. EntityManager::Component_List*pType=EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);
  20. unsignedintsize=EntityManager::getEntityManager()->getEntitySize();
  21. //FindthePlayer'shealthComponent
  22. unsignedintplayer_entity=-1;
  23. for(inti=0;i<size;i++)
  24. {
  25. if(((EntityTypeComponent*)(*pType)[i])->type==EntityTypeComponent::PLAYER)
  26. {
  27. player_entity=i;
  28. break;
  29. }
  30. }//endfor
  31. HealthComponent*health=(HealthComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_HEALTH,player_entity);
  32. //Checkiftheentityisoutofthescreen
  33. for(unsignedinti=0;i<size;)
  34. {
  35. if(((EntityTypeComponent*)(*pType)[i])->type==EntityTypeComponent::SPHERE_BALL)
  36. {
  37. if(((PositionComponent*)(*pPos)[i])->y>480)
  38. {
  39. EntityManager::getEntityManager()->removeEntity(i);
  40. size-=1;
  41. continue;
  42. }
  43. }//endifforsphereball
  44. else
  45. {
  46. if(((PositionComponent*)(*pPos)[i])->y<0)
  47. {
  48. EntityManager::getEntityManager()->removeEntity(i);
  49. size-=1;
  50. health->health--;
  51. continue;
  52. }
  53. }
  54. i++;
  55. }//endfor
  56. }//endforexcute
  57. voidBoundaryCheckSystem::exit()
  58. {
  59. }//endforexit

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

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


系统缺点

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

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



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

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

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

ShapeWar_SourceCode.zip

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是