怎么样cocos2d-x正在使用ECS(实体-包裹-制)建筑方法来开发一款游戏?
简介
在我的博客,我翻译的几篇文章ECS文章。这些文章都是从Game Development站点。假设你对这个架构方式还不是非常了解的话。欢迎阅读理解
组件-实体-系统和实现 组件-实体-系统。
我发现这个架构方式,是在浏览GameDev上的文章的时候了解到的。非常久曾经。就知道了有这么个架构方法。仅仅是一直没有机会自己实践下。这一次,我就抽出时间,依据网上对ECS系统的讨论,採用了一种实现方法。来实现一个。
我非常喜欢做游戏,所以相同的,还是用游戏实例来实践这个架构方法。我将会採用cocos2d-x来作为游戏开发的主要工具。这是一款十分强大的跨平台游戏引擎,感兴趣的读者。能够自行搜索了解。
ShapeWar
我一直认为。作为游戏程序猿,可以自己独立的绘制游戏中的图片资源是一件非常好玩的事情。
所以,没有美术功底的我。就选择了一种复古风格的艺术——像素艺术来学习。经过一段时间的学习,发现做像素画还是非常有趣的,所以我就将我曾经做的简单的像素图片,来融合成如今的这个游戏实例——ShapeWar 。
这个游戏很简单。玩家通过键盘上的左右键来移动发射器,通过按下space键,来进行攻击,将全部掉落下来的立方体全都打掉。假设有立方体遗漏掉,那么将会丢掉一颗血,直到玩家死亡为止。这个游戏。開始的时候。可能会很easy,可是,立方体下落的速度是逐渐添加的。到了后面。假设玩家还可以坚持住的话,那很了不起!
!。
好了,游戏规则非常easy,来看看游戏的截图吧!
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaV9kb3ZlbGVtb24=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
好了。这个游戏非常easy。有兴趣的同学,能够到这里来下载。试玩一下,而且在留言中,告诉我。你最高得了多少分哦!
!!
架构设计
从上面的截图,大家也可以明确。游戏仅仅有两个场景,各自是開始场景。和游戏进行场景。须要完毕的功能例如以下:
- 可以产生立方体。控制立方体产生
- 可以控制发射器,发射出球体
- 可以检測出球体和立方体之间的碰撞
- 对不同的立方体,须要碰撞不同的次数才干消灭
- 立方体消灭之后,要播放动画
- 玩家拥有血量和积分
这个游戏大致就有这些功能。
在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值而已。所以。我并没有专门为这个概念定义什么。它在我开发的游戏中,不过一个下标值而已。可是,我们须要知道,游戏中那么多的实体。须要进行统一的管理。所以为此,我创建了例如以下的一个类,用来对游戏中全部的实体进行管理。
//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
};};
正如读者看到的那样,这个类是一个单例类,里面提供了非常多的方法。要理解这个类,我们先来看看组件是怎样在这个类里面进行保存的。
在这个类中,我定义了一个这种成员:
std::vector<Component_List> m_ComponentContainer ; //Contain all the entity
而Component_List定义为例如以下:
/**
* Define the Component_List
*/
typedef std::vector<Component*> Component_List;
也就是说,这个ComponentContainer,包括了全部的在游戏中使用的组件实例。同一种组件实例。放在同一个Component_List中,然后不同的Component_List,放在Component_Container中。假设读者对这个不是非常清楚的话。能够看以下的图片:
从上图中能够看出,这是一个二维的空间盒子,纵向表示了一个组件类型中全部的组件实例,横向表示了一个实体拥有哪些组件。所以,这里的实体,也就是这里的容器中的下标了。
好了。在明确了组件是怎样保存了的之后。我们还须要了解在EntityManager中定义的这个数组是什么意思:
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採用分治算法。设计了这种方法来获取位数。
好了,通过上面的描写叙述,读者应该明确我是以怎么样的方式来维护游戏中全部的实体的了!
。!
系统
在实现了上面的组件,实体之后。接下来就应该实现系统了。我这里实现系统的方式。是依据这篇博客中提出的方法来实现的。
首先,抽象一个系统的类,例如以下所看到的:
/**
* 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 ;
};
在这个抽象的系统中,我定义了一个优先级。这样,我们就能够定义哪一些系统须要在另外一些系统之前进行执行。有了系统之后。我们就须要一个管理的方式,所以,在定义了一个系统管理器,例如以下所看到的:
/**
* 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方法例如以下所看到的:
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
非常easy,它调用已经依据优先级排好序的系统中的excute方法,来运行每个系统的任务。
在我的这个简单的游戏中。我定义了例如以下的几个系统,依据优先级从低到进行排序:
- RenderSystem。负责进行渲染
- MovementSystem。 负责进行实体的移动
- HealthSystem,负责推断哪些实体已死亡
- CreatorSystem,负责游戏中立方体的创建规则
- InputSystem, 负责处理键盘输入
- CollidDetectionSystem,负责进行碰撞检測
- BoundaryCheckSystem。负责进行边界检查。当立方体和球体出了边界之后,进行对应的操作
以下我们来分别看看这些系统的实现过程:
RenderSystem
#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
#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
#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
#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
#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系统,实现更加弹性的设计。通过使用组合的方法,大大减少系统的耦合性。同一时候,这里将数据和处理过程。通过组建和系统的方法实现了分离。通过这种系统,我们非常easy的可以实现网络游戏,由于仅仅须要对组件数据进行单独的传输就可以,而且非常easy的实现诸如关卡保存。这种内容。
可是,不论什么事情都是双面的。在带来这些优点的同一时候,在另外的方面也会带来限制。
系统缺点
通过上面的描写叙述,我们大概能够明白这种系统有例如以下的缺点:
- 内存利用较低。
我们在容器中为每个实体都开辟了相同大的空间,假设某个实体并不具有那样的组件的时候,那个空间依旧为它保留着,这浪费了大量的空间
- 同一个实体。没有办法拥有同一个组件的两份实例。也就说,对于像动画这种组件,一个实体,可能不仅仅有一个动画属性。它可能须要在死亡时,同一时候播放两种动画,那么这个系统就没有办法完毕这种工作。
- 最重要的一个缺点就是性能问题。读者可能发现,系统和实体的交互方式,全然是系统主动的轮询。来进行系统的处理。我们知道,高效的设计方法,应该是让实体在有须要的时候,调用系统来进行工作。假设系统持续的执行。在非常多情况下,系统并没有做什么有效的工作。
所以,应该将这种主动轮询的方式改成由事件驱动的可能更好一点。可是,博主临时没有想到怎样设计这种系统。可能在后面的实践中。掌握这种设计方法的时候。再来向大家讲述。
好了,ECS架构实践的第一篇博客就到此结束了。
假设您有什么不明确的地方。或者发现了文中设计上的缺陷,欢迎大家在评论中指出。
毕竟,旁观者清,当局者迷。希望可以和大家互相的学习!
互相进步。
这个游戏的源码和程序以及上传至CSDN,感兴趣的同学能够自行下载来阅读和试玩,不要忘了在评论中给出你获得的最高分哦,大家比比看谁的反应是最好的哦哦!
。!
ShapeWar_exe.zip(部分资源来作为网络,请不要用于商业用途哦!
!。)
版权声明:本文博主原创文章,博客,未经同意不得转载。
怎么样cocos2d-x正在使用ECS(实体-包裹-制)建筑方法来开发一款游戏?的更多相关文章
- GsonFormat插件主要用于使用Gson库将JSONObject格式的String 解析成实体,该插件可以加快开发进度,使用非常方便,效率高。
GsonFormat插件主要用于使用Gson库将JSONObject格式的String 解析成实体,该插件可以加快开发进度,使用非常方便,效率高. 插件地址:https://plugins.jetbr ...
- 通过sql语句查询出来的结果字段没有到对应实体类时的处理方法
通过sql语句查询出来的结果字段没有到对应实体类时的处理方法,对于Person类获取用户第一个名字和年龄两个字段,常见的有两种方式: 1.在创建一个与查询结果字段对应的类,然后通过构造函数实现: Qu ...
- Visual 2015创建新项,缺少ADO.NET 实体数据模型的解决方法
现在的某度查资料真的很麻烦,突然我自身的VS2015创建EF的时候找不到 ADO.NET 实体数据模型,但是使用CodeFrist是可以生成数据表的.所有特别郁闷. 打开界面如下 某度半天,都没有查出 ...
- 云服务器 ECS Linux 系统盘数据转移方法
转自:https://help.aliyun.com/knowledge_detail/41400.html 问题描述 购买云服务器 ECS Linux 服务器时,未购买数据盘,使用一段时间后,随着业 ...
- 字符串js编码转换成实体html编码的方法(防范XSS攻击)
js代码在html页面中转换成实体html编码的方法一: <!DOCTYPE html><html> <head> <title>js代码转换成实 ...
- hql语句查询实体类采用list方法的返回结果集
在hibernate中,用hql语句查询实体类,采用list方法的返回结果为一个List,该List中封装的对象分为以下三种情况:1.查询全部字段的情况下,如"from 实体类", ...
- 云服务器 ECS Linux 误删除文件恢复方法介绍
云服务器 ECS Linux 下,rm -rf 意味着一旦删除的文件是无法挽回的.但如果在没有文件覆盖操作的前提下,可以先尝试相关方式进行文件恢复. 本文对此进行简要说明. https://help ...
- 在动态引用DLL-A中,当参数是个实体,而实体的属性在另一个DLL-B中。。我们需要得到A这个实体并将其赋值,并将赋值的实体传人DLL-A的方法中。
string strPath = HttpContext.Current.Server.MapPath("/开放式DLL"); DirectoryInfo df = new Dir ...
- 几个检查当前运行的LINUX是在VM还是在实体机中的方法
昨天提到了VM中的逃逸问题,要想逃逸,首先要检测当前操作系统是否为VM,下面提供几个LINUX下的检查方法: 第一,首推facter virtual ,权限为普通用户,约定,普通用户命令提示符用$表示 ...
随机推荐
- virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区
virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区 virtenv 0.8.6 发布,virtenv 是一个用 QT4 开发的应用,用来配置和启动基于 LXC 的虚拟桌面环境.该容器 ...
- iOS安全攻击和防御(24):敏感的保护方案逻辑(1)
iOS安全攻击和防御(24):敏感的保护方案逻辑(1) Objective-C代码easy被hook.暴露信息太赤裸裸,为了安全,改用C来写吧! 当然不是所有代码都要C来写,我指的是敏感业务逻辑代码. ...
- shu_1241 邮局位置问题
http://202.121.199.212/JudgeOnline/problem.php?cid=1078&pid=5 分析: 由于题目中的距离是折线距离,所以能够分别考虑两个方向.又x方 ...
- loj1370(欧拉函数+线段树)
传送门:Bi-shoe and Phi-shoe 题意:给出多个n(1<=n<=1e6),求满足phi(x)>=n的最小的x之和. 分析:先预处理出1~1e6的欧拉函数,然后建立一颗 ...
- ActionBar本部分适用述评
http://note.youdao.com/share/?id=7f213cb64069bad221f4581507707294&type=note 因为把图片拿进来太麻烦,所以我给了一个直 ...
- vim配置(vimplus)
vim配置(vimplus) vimplus vimplus是vim的超级配置安装程序 github地址:https://github.com/chxuan/vimplus.git,欢迎star和fo ...
- Hbase配置中出现的问题总结
在create table的时候出现例如以下问题 1. ERROR: java.io.IOException: Table Namespace Manager not ready yet, try a ...
- Linux C 编程内存泄露检測工具(二):memwatch
Memwatch简单介绍 在三种检測工具其中,设置最简单的算是memwatch,和dmalloc一样,它能检測未释放的内存.同一段内存被释放多次.位址存取错误及不当使用未分配之内存区域.请往http: ...
- V5
系统设置--关于手机--版本号点5下--进去开发模式--打开开发选项--打开USB调试.然后在连接第三方助手软件 http://bbs.ztehn.com/thread-19037-1-1.html
- DIV水平和垂直居中的实现
在div的宽度和高度固定的情况下,实现div水平和垂直居中普遍采用如下的方式: <!DOCTYPE html> <html> <head> <style ty ...