转载自:http://blog.csdn.net/i_dovelemon/article/details/30250049

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

 

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

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

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

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

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

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

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

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

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

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

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

组件

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

// File: Component.h
//------------------------------------------------------------------
// declaration : Copyright (c), by XJ , 2014 . All right reserved .
// brief : This file will define the Component base class of the
// Entity-Component-System.
// author : XJ
// date : 2014 / 6 / 8
// version : 1.0
//-------------------------------------------------------------------
#pragma once
#include<cocos2d.h>
using namespace cocos2d ; namespace ShapeWar
{
#define COMPONENT_NONE 0x0 class Component
{
public:
Component(){}
virtual ~Component(){}
}; /**
* Define the Render Component
*/
#define COMPONENT_RENDER (1 << 1)
class RenderComponent: public Component
{
public:
RenderComponent(){}
~RenderComponent()
{
sprite->removeFromParentAndCleanup(true);
delete sprite ;
} public:
CCSprite* sprite ;
}; /**
* Define the Position Component
*/
#define COMPONENT_POSITION (1 << 2 )
class PositionComponent: public Component
{
public:
PositionComponent(){}
~PositionComponent(){} public:
float x ;
float y ;
}; /**
* Define the Velocity Component
*/
#define COMPONENT_VELOCITY (1 << 3)
class VelocityComponent: public Component
{
public:
VelocityComponent(){}
~VelocityComponent(){} public:
float vx ;
float vy ;
}; /**
* Define the Health Component
*/
#define COMPONENT_HEALTH (1 << 4)
class HealthComponent: public Component
{
public:
HealthComponent(){}
~HealthComponent(){} public:
unsigned int health ;
}; /**
* Define the Collidable Component
* brief : Use the AABB's Min-Max representation
*/
#define COMPONENT_COLLID (1 << 5)
class CollidableComponent:public Component
{
public:
CollidableComponent(){}
~CollidableComponent(){} public:
float min_x ;
float min_y ;
float max_x ;
float max_y ;
}; /**
* Define the EntityType component
* brief : This component will indicate which type the entity is.
*/
#define COMPONENT_ENTITY_TYPE (1 << 6)
class EntityTypeComponent: public Component
{
public:
EntityTypeComponent(){}
~EntityTypeComponent(){} public:
static const unsigned int RED_CUBE = (1 << 1) ;
static const unsigned int PURPLE_CUBE = (1 << 2) ;
static const unsigned int ORANGE_CUBE = (1 << 3) ;
static const unsigned int GREEN_CUBE = (1 << 4) ;
static const unsigned int SPHERE_BALL = (1 << 5) ;
static const unsigned int PLAYER = (1 << 6) ; public:
unsigned int type ;
}; /**
* Define the AnimateComponent
*/
#define COMPONENT_ANIMATE (1 << 7)
class AnimateComponent:public Component
{
public:
AnimateComponent(){}
~AnimateComponent(){} public:
cocos2d::CCAnimate* animate ;
unsigned frames ;
}; };

从上面的代码中,大家可以看到,我首先定义了一个基类Component,然后让所有的组件都继承于这个基类。这里,我并没有用到继承,读者可以发现 Component中什么内容也没有。 我将其他组件继承于Component的组要原因是能够将他们统一的进行处理,仅此而已。

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

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

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

实体

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

  1. #pragma once
  2. #include<vector>
  3. #include"Component.h"
  4. using namespace std ;
  5. namespace ShapeWar
  6. {
  7. class EntityManager
  8. {
  9. private:
  10. EntityManager();
  11. ~EntityManager();
  12. public:
  13. static EntityManager* getEntityManager();
  14. public:
  15. _int64 createEntity() ;
  16. void removeEntity(_int64 entity);
  17. void registComponents(_int64 component_size);
  18. void addComponent(Component* component, _int64 component_type, _int64 entity);
  19. void removeComponent(_int64 component_type, _int64 entity);
  20. std::vector<Component*>* getComponentList(_int64 component_type) const ;
  21. Component* getComponent(_int64 component_type, _int64 entity);
  22. _int64 getEntityFlag(_int64 entity) const ;
  23. void setEntityFlag(_int64 entity, _int64 entity_type);
  24. unsigned int getEntitySize() const ;
  25. typedef std::vector<Component*> Component_List;
  26. private:
  27. void _destroy();
  28. private:
  29. std::vector<_int64> m_EntityFlagArray ;
  30. <pre name="code" class="cpp">        std::vector<Component_List>  m_ComponentContainer ;
//File:EntityManager
//------------------------------------------------------------------
// declaration : Copyright (c), by XJ , 2014 . All right reserved .
// brief : This file will define the Entity of the Entity-Componet-
// System and the entity manager.
// author : XJ
// date : 2014 / 6 / 8
// version : 1.0
//-------------------------------------------------------------------
#pragma once #include<vector>
#include"Component.h"
using namespace std ; namespace ShapeWar
{
/**
* Define the EntityManager
*/
class EntityManager
{
private:
EntityManager();
~EntityManager(); /** Singleton getter*/
public:
static EntityManager* getEntityManager(); /** Core method */
public:
/**
* Create an empty entity
*/
_int64 createEntity() ; /**
* Remove an entity
*/
void removeEntity(_int64 entity); /**
* Register component
* brief : This method will make the entity manager to alloc the memory to store
* the registed componet.If you want to use one componet in the ECS , you
* must registed it at the start time.
*/
void registComponents(_int64 component_size); /**
* Add an component to the entity
*/
void addComponent(Component* component, _int64 component_type, _int64 entity); /**
* Remove an component of the entity
*/
void removeComponent(_int64 component_type, _int64 entity); /**
* Get component list
*/
std::vector<Component*>* getComponentList(_int64 component_type) const ; /**
* Get the specificed component of the entity
*/
Component* getComponent(_int64 component_type, _int64 entity); /**
* Get entity flag
*/
_int64 getEntityFlag(_int64 entity) const ; /**
* Set entity flag
*/
void setEntityFlag(_int64 entity, _int64 entity_type); /**
* Get the entity size
*/
unsigned int getEntitySize() const ; /**
* Define the Component_List
*/
typedef std::vector<Component*> Component_List; private:
/**
* Destroy all the component
*/
void _destroy(); private:
std::vector<_int64> m_EntityFlagArray ; //Contain the Entity flag
<pre name="code" class="cpp"> std::vector<Component_List> m_ComponentContainer ; //Contain all the entity

};};


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

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

  1. std::vector<Component_List>  m_ComponentContainer ;
std::vector<Component_List>  m_ComponentContainer ;				//Contain all the entity

而Component_List定义为如下:

  1. typedef std::vector<Component*> Component_List;
/**
* Define the Component_List
*/
typedef std::vector<Component*> Component_List;

也就是说,这个ComponentContainer,包含了所有的在游戏中使用的组件实例。同一种组件实例,放在同一个Component_List 中,然后不同的Component_List,放在Component_Container中。如果读者对这个不是很清楚的话,可以看下面的图片:

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

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

  1. std::vector<_int64> m_EntityFlagArray ;
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采用分治算法,设计了这个方法来获取位数。

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

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

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

  1. class System
  2. {
  3. public:
  4. System(int _priority);
  5. virtual ~System();
  6. public:
  7. virtual void enter() = 0 ;
  8. virtual void excute(float dt) = 0;
  9. virtual void exit() = 0 ;
  10. public:
  11. int priority ;
  12. };
        /**
* Define the base system class. All system will inherit from this base class.
*/
class System
{
public:
System(int _priority);
virtual ~System(); public:
virtual void enter() = 0 ;
virtual void excute(float dt) = 0;
virtual void exit() = 0 ; public:
int priority ;
};

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

  1. class SystemManager
  2. {
  3. private:
  4. SystemManager();
  5. ~SystemManager();
  6. public:
  7. static SystemManager* getSystemManager() ;
  8. public:
  9. void addSystem(System * system);
  10. void update(float dt);
  11. void pause();
  12. void resume();
  13. private:
  14. void _destroy();
  15. private:
  16. std::vector<System*> system_list ;
  17. bool bPaused ;
  18. };
	/**
* Define the system manager
*/
class SystemManager
{
private:
SystemManager();
~SystemManager(); /** Singleton getter*/
public:
static SystemManager* getSystemManager() ; /** Core method*/
public:
/**
* Add one system to the system list
*/
void addSystem(System * system); /**
* Update all the system
*/
void update(float dt); /**
* Pause all the system
*/
void pause(); /**
* Resume all the system
*/
void resume(); private:
/**
* Destroy all the systems
*/
void _destroy(); private:
std::vector<System*> system_list ;
bool bPaused ;
};

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

  1. void SystemManager::update(float dt)
  2. {
  3. if(bPaused == true)
  4. return ;
  5. for(int i = 0  ; i < system_list.size() ; i ++)
  6. {
  7. system_list[i]->excute(dt);
  8. }
  9. }
void SystemManager::update(float dt)
{
if(bPaused == true)
return ; //Excute all the system
for(int i = 0 ; i < system_list.size() ; i ++)
{
system_list[i]->excute(dt);
}// end for
}// end for update

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

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

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

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

RenderSystem

  1. #include"RenderSystem.h"
  2. #include"EntityMananger.h"
  3. using namespace ShapeWar ;
  4. RenderSystem::RenderSystem(int _priority, CCNode* _scene)
  5. :System(_priority),
  6. scene(_scene)
  7. {
  8. }
  9. RenderSystem::~RenderSystem()
  10. {
  11. }
  12. void RenderSystem::enter()
  13. {
  14. }
  15. void RenderSystem::excute(float dt)
  16. {
  17. unsigned int size = EntityManager::getEntityManager()->getEntitySize();
  18. for(unsigned int i = 0 ; i < size ; i ++)
  19. {
  20. _int64 flag = 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. }
  33. else
  34. scene->addChild(pRender->sprite, 10);
  35. }
  36. pRender->sprite->setPosition(ccp(pPos->x, pPos->y));
  37. }
  38. }
  39. }
  40. void RenderSystem::exit()
  41. {
  42. unsigned int size = EntityManager::getEntityManager()->getEntitySize();
  43. for(unsigned int i = 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. }
  49. }
#include"RenderSystem.h"
#include"EntityMananger.h"
using namespace ShapeWar ; RenderSystem::RenderSystem(int _priority, CCNode* _scene)
:System(_priority),
scene(_scene)
{ } RenderSystem::~RenderSystem()
{ } void RenderSystem::enter()
{ }// ed for enter void RenderSystem::excute(float dt)
{
unsigned int size = EntityManager::getEntityManager()->getEntitySize();
for(unsigned int i = 0 ; i < size ; i ++)
{
_int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);
if((flag & (COMPONENT_RENDER | COMPONENT_POSITION)) == (COMPONENT_RENDER | COMPONENT_POSITION))
{
RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);
PositionComponent* pPos = (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION,i); if(pRender->sprite->getParent() == NULL)
{
EntityTypeComponent* pType = (EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE,i);
if(pType->type != EntityTypeComponent::PLAYER)
{
pRender->sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.0/60, 5)));
scene->addChild(pRender->sprite);
}// end for PLAYER
else
scene->addChild(pRender->sprite, 10);
} pRender->sprite->setPosition(ccp(pPos->x, pPos->y));
}
}// end for sprite }// end for excute void RenderSystem::exit()
{
unsigned int size = EntityManager::getEntityManager()->getEntitySize();
for(unsigned int i = 0 ; i < size ; i ++)
{
RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);
pRender->sprite->stopAllActions();
pRender->sprite->removeFromParentAndCleanup(true);
}// end for
}// end for exit

MovementSystem

  1. #include"MovementSystem.h"
  2. #include"EntityMananger.h"
  3. using namespace ShapeWar ;
  4. MovementSystem::MovementSystem(int _priority)
  5. :System(_priority)
  6. {
  7. }
  8. MovementSystem::~MovementSystem()
  9. {
  10. }
  11. void MovementSystem::enter()
  12. {
  13. }
  14. void MovementSystem::excute(float dt)
  15. {
  16. unsigned int size = EntityManager::getEntityManager()->getEntitySize();
  17. for(unsigned int i =  0 ; i < size ; i ++)
  18. {
  19. _int64 flag = 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. }
  28. }
  29. void MovementSystem::exit()
  30. {
  31. }
#include"MovementSystem.h"
#include"EntityMananger.h"
using namespace ShapeWar ; MovementSystem::MovementSystem(int _priority)
:System(_priority)
{ } MovementSystem::~MovementSystem()
{ } void MovementSystem::enter()
{ }// end for enter void MovementSystem::excute(float dt)
{
unsigned int size = EntityManager::getEntityManager()->getEntitySize();
for(unsigned int i = 0 ; i < size ; i ++)
{
_int64 flag = EntityManager::getEntityManager()->getEntityFlag(i); if((flag & (COMPONENT_POSITION | COMPONENT_VELOCITY)) == (COMPONENT_POSITION | COMPONENT_VELOCITY))
{
PositionComponent* pPos = (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION, i);
VelocityComponent* pVelocity = (VelocityComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_VELOCITY, i); pPos->x += (1.0 / 60) * pVelocity->vx ;
pPos->y += (1.0 / 60) * pVelocity->vy ;
}
}// end for
}// end for excute void MovementSystem::exit()
{ }// end for exit

HealthSystem

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

CreatorSystem

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

InputSystem

#include "InputSystem.h"
#include "EntityMananger.h"
#include "EntityCreator.h"
#include "AudioSystem.h"
using namespace ShapeWar ; InputSystem::InputSystem(int _priority)
:System(_priority)
{ } InputSystem::~InputSystem()
{ } void InputSystem::enter()
{ }// end for enter void InputSystem::excute(float dt)
{
//Get the Component list
EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);
EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE); //Find the player and the un-shooted ball
unsigned int size = EntityManager::getEntityManager()->getEntitySize(); int player = -1 , ball = -1 ;
for(unsigned int i = 0 ; i < size ; i ++)
{
unsigned int type = ((EntityTypeComponent*)(*pType)[i])->type ;
if(type == EntityTypeComponent::PLAYER)
{
player = i ;
}// end if if(type == EntityTypeComponent::SPHERE_BALL)
{
_int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);
if((flag & COMPONENT_VELOCITY) == 0)
{
ball = i ;
} // end if
}// end if if(player != -1 && ball != -1)
break ;
}// end for PositionComponent* pPlayer_Pos = NULL ;
PositionComponent* pBall_Pos = NULL ; if(player != -1)
pPlayer_Pos = (PositionComponent*)(*pPos)[player] ;
if(ball != -1)
pBall_Pos = (PositionComponent*)(*pPos)[ball] ; if(GetKeyState(VK_RIGHT) & 0x8000)
{
if(pPlayer_Pos != NULL)
{
pPlayer_Pos->x += 5 ;
if(pPlayer_Pos->x >= 320 - 22)
pPlayer_Pos->x = 320 - 22 ; if(pBall_Pos != NULL)
pBall_Pos->x = pPlayer_Pos->x ;
}
}else if(GetKeyState(VK_LEFT)&0x8000)
{
if(pPlayer_Pos != NULL)
{
pPlayer_Pos->x -= 5 ;
if(pPlayer_Pos->x <= 22)
pPlayer_Pos->x = 22 ; if(pBall_Pos != NULL)
pBall_Pos->x = pPlayer_Pos->x ;
}
} static int nFrame = 0 ;
if((GetKeyState(VK_SPACE)& 0x8000) && (nFrame >= 15))
{
VelocityComponent* pVelocity = new VelocityComponent();
pVelocity->vx = 0 ;
pVelocity->vy = 600 ;
EntityManager::getEntityManager()->addComponent(pVelocity, COMPONENT_VELOCITY, ball); //Create another ball
EntityCreator::createSphereBall(pPlayer_Pos->x, pPlayer_Pos->y); //Player Effect
AudioSystem::sharedAudioSystem()->playSound("Shoot.wav");
nFrame = 0 ;
} nFrame ++ ; }// end for excute void InputSystem::exit()
{ }// end for exit

CollidDetectionSystem

#include"CollidDetectionSystem.h"
#include"EntityMananger.h"
#include"AudioSystem.h"
using namespace ShapeWar ; CollidDetectionSystem::CollidDetectionSystem(int _priority)
:System(_priority)
{ } CollidDetectionSystem::~CollidDetectionSystem()
{ } void CollidDetectionSystem::enter()
{ }// end for enter void CollidDetectionSystem::excute(float dt)
{
//Get all PositionComponent list
EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION); //Get all the CollidableComponent list
EntityManager::Component_List* pCollid = EntityManager::getEntityManager()->getComponentList(COMPONENT_COLLID); //Get all the EntityTypeComponent list
EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE); //Get all the HealthComponent list
EntityManager::Component_List* pHealth = EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH); unsigned int size = EntityManager::getEntityManager()->getEntitySize(); //Find all sphere ball
std::vector<unsigned int> index_array ;
for(unsigned int i = 0 ; i < size ; i ++)
{
if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)
{
if((EntityManager::getEntityManager()->getEntityFlag(i) & COMPONENT_VELOCITY) == COMPONENT_VELOCITY)
{
index_array.push_back(i);
}// end if
}// end if
}// end for for(unsigned int i = 0 ; i < index_array.size() ; i ++)
{
CollidableComponent* collidAreaA = ((CollidableComponent*)((*pCollid)[index_array[i]])) ;
PositionComponent* posA = ((PositionComponent*)((*pPos)[index_array[i]])) ;
collidAreaA->min_x = posA->x - 16 ;
collidAreaA->min_y = posA->y - 16 ;
collidAreaA->max_x = posA->x + 16 ;
collidAreaA->max_y = posA->y + 16 ; size = EntityManager::getEntityManager()->getEntitySize();
for(unsigned int j = 0 ; j < size ; j ++)
{
if((EntityManager::getEntityManager()->getEntityFlag(j) & COMPONENT_COLLID) == COMPONENT_COLLID &&
((EntityTypeComponent*)(*pType)[j])->type != EntityTypeComponent::SPHERE_BALL)
{
CollidableComponent* collidAreaB = ((CollidableComponent*)((*pCollid)[j])) ;
PositionComponent* posB = ((PositionComponent*)((*pPos)[j])) ;
collidAreaB->min_x = posB->x - 16 ;
collidAreaB->min_y = posB->y - 16 ;
collidAreaB->max_x = posB->x + 16 ;
collidAreaB->max_y = posB->y + 16 ; if(collidAreaA->min_x > collidAreaB->max_x
||collidAreaA->max_x < collidAreaB->min_x) continue ;
if(collidAreaA->min_y > collidAreaB->max_y ||
collidAreaA->max_y < collidAreaB->min_y) continue ; HealthComponent* cube = (HealthComponent*)(*pHealth)[j] ;
cube->health -- ; if(cube->health == 0)
{
AnimateComponent* pAnimate = new AnimateComponent();
pAnimate->animate = new CCAnimate(); CCAnimation* pAnimation = CCAnimation::create(); for(int i = 0 ; i < 10 ; i ++)
{
char buffer[32] ;
sprintf(buffer,"Explosion000%d.png",i);
pAnimation->addSpriteFrameWithFileName(buffer);
}// end for pAnimation->setDelayPerUnit(1.0/10);
pAnimate->animate->initWithAnimation(pAnimation);
pAnimate->frames = 60 ; //Add the Animate Component to the entity
EntityManager::getEntityManager()->addComponent(pAnimate, COMPONENT_ANIMATE, j); //Remove the CollidDetection Component
EntityManager::getEntityManager()->removeComponent(COMPONENT_COLLID, j); //Remove the Velocity Component
EntityManager::getEntityManager()->removeComponent(COMPONENT_VELOCITY, j); }// end if HealthComponent* ball = (HealthComponent*)(*pHealth)[index_array[i]] ;
ball->health -- ; //Play hurt effect
AudioSystem::sharedAudioSystem()->playSound("Hurt.wav"); break ;
}// end if
}// end for cube
}// end for sphere ball
}// end for excute void CollidDetectionSystem::exit()
{ }// end for exit

BoundaryCheckSystem

  1. #include"BoundaryCheckSystem.h"
  2. #include"EntityMananger.h"
  3. using namespace ShapeWar ;
  4. BoundaryCheckSystem::BoundaryCheckSystem(int priority)
  5. :System(priority)
  6. {
  7. }
  8. BoundaryCheckSystem::~BoundaryCheckSystem()
  9. {
  10. }
  11. void BoundaryCheckSystem::enter()
  12. {
  13. }
  14. void BoundaryCheckSystem::excute(float dt)
  15. {
  16. EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);
  17. EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);
  18. unsigned int size = EntityManager::getEntityManager()->getEntitySize();
  19. unsigned int player_entity = -1 ;
  20. for(int i = 0 ; i < size ; i ++)
  21. {
  22. if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::PLAYER)
  23. {
  24. player_entity = i ;
  25. break ;
  26. }
  27. }
  28. HealthComponent * health = (HealthComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_HEALTH, player_entity);
  29. for(unsigned int i = 0 ; i < size ; )
  30. {
  31. if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)
  32. {
  33. if(((PositionComponent*)(*pPos)[i])->y > 480)
  34. {
  35. EntityManager::getEntityManager()->removeEntity(i);
  36. size -= 1 ;
  37. continue ;
  38. }
  39. }
  40. else
  41. {
  42. if(((PositionComponent*)(*pPos)[i])->y < 0)
  43. {
  44. EntityManager::getEntityManager()->removeEntity(i);
  45. size -= 1 ;
  46. health->health-- ;
  47. continue ;
  48. }
  49. }
  50. i ++ ;
  51. }
  52. }
  53. void BoundaryCheckSystem::exit()
  54. {
  55. }
#include"BoundaryCheckSystem.h"
#include"EntityMananger.h"
using namespace ShapeWar ; BoundaryCheckSystem::BoundaryCheckSystem(int priority)
:System(priority)
{ } BoundaryCheckSystem::~BoundaryCheckSystem()
{ } void BoundaryCheckSystem::enter()
{ }// end for enter void BoundaryCheckSystem::excute(float dt)
{
//Get all PositionComponent list
EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION); //Get all the EntityTypeComponent list
EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE); unsigned int size = EntityManager::getEntityManager()->getEntitySize(); //Find the Player's health Component
unsigned int player_entity = -1 ;
for(int i = 0 ; i < size ; i ++)
{
if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::PLAYER)
{
player_entity = i ;
break ;
}
}// end for HealthComponent * health = (HealthComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_HEALTH, player_entity); //Check if the entity is out of the screen
for(unsigned int i = 0 ; i < size ; )
{
if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)
{
if(((PositionComponent*)(*pPos)[i])->y > 480)
{
EntityManager::getEntityManager()->removeEntity(i);
size -= 1 ;
continue ;
}
}// end if for sphere ball
else
{
if(((PositionComponent*)(*pPos)[i])->y < 0)
{
EntityManager::getEntityManager()->removeEntity(i);
size -= 1 ;
health->health-- ;
continue ;
}
} i ++ ;
}// end for
}// end for excute void BoundaryCheckSystem::exit()
{ }// end for exit

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

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

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

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

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

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

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

ShapeWar_SourceCode.zip

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

 

ECS 游戏架构 应用的更多相关文章

  1. ECS 游戏架构 实现

    转载自:http://blog.csdn.net/i_dovelemon/article/details/27230719 实现 组件-实体-系统 - 博客频道   这篇文章是在我前面文章,理解组件- ...

  2. ECS 游戏架构 理解

    转载自:http://blog.csdn.net/i_dovelemon/article/details/25798677 理解 组件-实体-系统 (ECS \CES)游戏编程模型 - 博客频道   ...

  3. 解构C#游戏框架uFrame兼谈游戏架构设计

    1.概览 uFrame是提供给Unity3D开发者使用的一个框架插件,它本身模仿了MVVM这种架构模式(事实上并不包含Model部分,且多出了Controller部分).因为用于Unity3D,所以它 ...

  4. 【转载】U3D 游戏引擎之游戏架构脚本该如何来写

    原文:http://tech.ddvip.com/2013-02/1359996528190113.html Unity3D 游戏引擎之游戏架构脚本该如何来写   2013-02-05 00:48:4 ...

  5. Unity《ATD》塔防RPG类3D游戏架构设计(二)

    目录 <ATD> 游戏模型 <ATD> 游戏逻辑 <ATD> UI/HUD/特效/音乐 结语 前篇:Unity<ATD>塔防RPG类3D游戏架构设计(一 ...

  6. Unity《ATD》塔防RPG类3D游戏架构设计(一)

    目录 <ATD> 游戏简介 <ATD> 整体结构 <ATD> 游戏机制 Buff机制 Skill机制(技能机制) 仇恨机制 <ATD> 游戏模型 策划案 ...

  7. `cocos2dx非完整` 游戏架构缩影 添加启动流程

    这期的话题可能不是很好, 我没有想到很好的词句去更好的表达. 我一直都是很固执的认为, 同一类型的游戏,在开发做的前期工作上面其实都是可以复用的,也就是大同小异的.从游戏启动,启动日志,启动检查,检查 ...

  8. Unity3D之游戏架构脚本该如何来写(转)

    这篇文章主要想大家说明一下我在Unity3D游戏开发中是如何写游戏脚本的,对于Unity3D这套游戏引擎来说入门极快,可是要想做好却非常的难.这篇文章的目的是让哪些已经上手Unity3D游戏引擎的朋友 ...

  9. U3D 游戏引擎之游戏架构脚本该如何来写

    这篇文章MOMO主要想大家说明一下我在Unity3D游戏开发中是如何写游戏脚本的,对于Unity3D这套游戏引擎来说入门极快,可是要想做好却非常的难.这篇文章的目的是让哪些已经上手Unity3D游戏引 ...

随机推荐

  1. STL容器的resize方法

    刚才写DFS程序,为了加个创建DFS树然后再次遍历的功能,把初始化的代码给封装到init()函数里,然后直接调用之后对同样是邻接表表示的DFS树进行DFS. 然后令人诧异的是竟然没有打印结果,调试发现 ...

  2. Fluent NHibernate AutoMapping Conventions

    前言 使用nhibernate在项目中不管是代码或者xml文件映射方式,如果项目较大编写映射也应该算一笔大的工作量了,使用Fluent Nhibernate自己编写映射规则,将从conventions ...

  3. Cousera 无法播放视频 解决办法 widows 和 linux

    查资料得知,Cousera无法播放课程视频原因在于DNS污染. 尽管通过FQ软件把视频看完了,在最后一课找到了这个解决办法,现在拿出来分享给大家: Windows: 请参照以下链接: http://j ...

  4. cordova 安装使用

    前人总结: Cordova是Apache软件基金会的一个产品.其前身是PhoneGap,由Nitobi开发,2011年10月,Adobe收够了Nitobi,并且PhoneGap项目也被贡献给Apach ...

  5. Window下MySql 5.6 安装后内存占用很高的问题

    Window下MySql 5.6 安装后内存占用很高的问题 刚刚准备玩一把mysql,初学者 环境是window 7和window sever 2008, mysql是最新的5.6, 发现的问题是安装 ...

  6. 关于SQLSERVER的全文目录跟全文索引的区别

    很久没有写随笔了,本来之前想写一篇关于SQLSERVER全文索引的随笔,可惜没有时间,一直拖到现在才有时间写,不好意思让各位久等了~ 先介绍一下SQLSERVER中的存储类对象,哈哈,先介绍一下概念嘛 ...

  7. 解析 Ceph: FileJournal 的作用

      很多的用户在提到 Ceph 性能的时候都会提到“写放大”这点,实际上就是 FileJournal 在起作用.只要使用默认的 FileStore,所有数据包括 metadata 都会在 FileJo ...

  8. python执行报错 configparser.NoSectionError: No section: 'section_1'

    场景:请求获取验证码模块regVC.py读取配置文件config.ini时,regVC.py模块单独执行正常,但通过run_all.py模块批量执行时报错,找不到section 解决办法:配置文件路径 ...

  9. ubuntu 固定静态IP

    http://jingyan.baidu.com/article/e5c39bf5bbe0e739d7603396.html 路由器每重启一次,IP都会改变,那么实验室的路由器一天要断电好几次,那么每 ...

  10. Python Twisted系列教程20: Twisted和Erlang

    作者:dave@http://krondo.com/twisted-and-erlang/  译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅读:也可以从”Twisted 入 ...