zoukankan      html  css  js  c++  java
  • 怎么样cocos2d-x正在使用ECS(实体-包裹-制)建筑方法来开发一款游戏?

    简介

                  在我的博客,我翻译的几篇文章ECS文章。这些文章都是从Game Development站点。假设你对这个架构方式还不是非常了解的话。欢迎阅读理解 组件-实体-系统实现 组件-实体-系统

                  我发现这个架构方式,是在浏览GameDev上的文章的时候了解到的。非常久曾经。就知道了有这么个架构方法。仅仅是一直没有机会自己实践下。这一次,我就抽出时间,依据网上对ECS系统的讨论,採用了一种实现方法。来实现一个。

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


    ShapeWar

                 我一直认为。作为游戏程序猿,可以自己独立的绘制游戏中的图片资源是一件非常好玩的事情。

    所以,没有美术功底的我。就选择了一种复古风格的艺术——像素艺术来学习。经过一段时间的学习,发现做像素画还是非常有趣的,所以我就将我曾经做的简单的像素图片,来融合成如今的这个游戏实例——ShapeWar 。

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

    !。

                好了,游戏规则非常easy,来看看游戏的截图吧!

                     好了。这个游戏非常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_SourceCode.zip

           ShapeWar_exe.zip(部分资源来作为网络,请不要用于商业用途哦!

    !。)

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    升级windows 11小工具
    windows 10更新升级方法
    您需要了解的有关 Oracle 数据库修补的所有信息
    Step by Step Apply Rolling PSU Patch In Oracle Database 12c RAC Environment
    Upgrade Oracle Database Manually from 12.2.0.1 to 19c
    如何应用版本更新 12.2.0.1.210420(补丁 32507738 – 2021 年 4 月 RU)
    xtrabackup 安装、备份和恢复
    Centos_Lvm expand capacity without restarting CentOS
    Centos_Lvm_Create pv vg lv and mount
    通过全备+relaylog同步恢复被drop的库或表
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4828633.html
Copyright © 2011-2022 走看看