ECS 游戲架構 應用


轉載自: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.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9.   
  10. #pragma once  
  11.   
  12. #include<vector>  
  13. #include"Component.h"  
  14. using namespace std ;  
  15.   
  16. namespace ShapeWar  
  17. {  
  18.  
  19.  
  20.   
  21. class EntityManager  
  22. {  
  23. private:  
  24.     EntityManager();  
  25.     ~EntityManager();  
  26.   
  27.       
  28. public:  
  29.     static EntityManager* getEntityManager();  
  30.   
  31.       
  32. public:  
  33.      
  34.  
  35.   
  36.     _int64 createEntity() ;  
  37.   
  38.      
  39.  
  40.   
  41.     void removeEntity(_int64 entity);  
  42.   
  43.      
  44.  
  45.  
  46.  
  47.  
  48.   
  49.     void registComponents(_int64 component_size);  
  50.   
  51.      
  52.  
  53.   
  54.     void addComponent(Component* component, _int64 component_type, _int64 entity);  
  55.   
  56.      
  57.  
  58.   
  59.     void removeComponent(_int64 component_type, _int64 entity);  
  60.   
  61.      
  62.  
  63.   
  64.     std::vector<Component*>* getComponentList(_int64 component_type) const ;  
  65.   
  66.      
  67.  
  68.   
  69.     Component* getComponent(_int64 component_type, _int64 entity);  
  70.   
  71.      
  72.  
  73.   
  74.     _int64 getEntityFlag(_int64 entity) const ;  
  75.   
  76.      
  77.  
  78.   
  79.     void setEntityFlag(_int64 entity, _int64 entity_type);  
  80.   
  81.      
  82.  
  83.   
  84.     unsigned int getEntitySize() const ;  
  85.   
  86.      
  87.  
  88.   
  89.     typedef std::vector<Component*> Component_List;  
  90.   
  91. private:  
  92.      
  93.  
  94.   
  95.     void _destroy();  
  96.   
  97. private:  
  98.     std::vector<_int64> m_EntityFlagArray ;                             
  99.     <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.  
  2.  
  3.   
  4. 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.         
  2.  
  3.   
  4. class System  
  5. {  
  6. public:  
  7.     System(int _priority);  
  8.     virtual ~System();  
  9.   
  10. public:  
  11.     virtual void enter() = 0 ;  
  12.     virtual void excute(float dt) = 0;  
  13.     virtual void exit() = 0 ;  
  14.   
  15. public:  
  16.     int priority ;  
  17. };  
        /**
	* 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.  
  2.  
  3.   
  4. class SystemManager  
  5. {  
  6. private:  
  7.     SystemManager();  
  8.     ~SystemManager();  
  9.   
  10.       
  11. public:  
  12.     static SystemManager* getSystemManager() ;  
  13.   
  14.       
  15. public:  
  16.      
  17.  
  18.   
  19.     void addSystem(System * system);  
  20.   
  21.      
  22.  
  23.   
  24.     void update(float dt);  
  25.   
  26.      
  27.  
  28.   
  29.     void pause();  
  30.   
  31.      
  32.  
  33.   
  34.     void resume();  
  35.   
  36. private:  
  37.      
  38.  
  39.   
  40.     void _destroy();  
  41.   
  42. private:  
  43.     std::vector<System*> system_list ;  
  44.     bool bPaused ;  
  45. };  
	/**
	* 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.   
  6.       
  7.     for(int i = 0  ; i < system_list.size() ; i ++)  
  8.     {  
  9.         system_list[i]->excute(dt);  
  10.     }  
  11. }  
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.   
  5. RenderSystem::RenderSystem(int _priority, CCNode* _scene)  
  6.     :System(_priority),  
  7.     scene(_scene)  
  8. {  
  9.   
  10. }  
  11.   
  12. RenderSystem::~RenderSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void RenderSystem::enter()  
  18. {  
  19.   
  20. }  
  21.   
  22. void RenderSystem::excute(float dt)  
  23. {  
  24.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  25.     for(unsigned int i = 0 ; i < size ; i ++)  
  26.     {  
  27.         _int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);  
  28.         if((flag & (COMPONENT_RENDER | COMPONENT_POSITION)) == (COMPONENT_RENDER | COMPONENT_POSITION))  
  29.         {  
  30.             RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);  
  31.             PositionComponent* pPos =  (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION,i);  
  32.   
  33.             if(pRender->sprite->getParent() == NULL)  
  34.             {  
  35.                 EntityTypeComponent* pType = (EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE,i);  
  36.                 if(pType->type != EntityTypeComponent::PLAYER)  
  37.                 {  
  38.                     pRender->sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.0/60, 5)));  
  39.                     scene->addChild(pRender->sprite);  
  40.                 }  
  41.                 else  
  42.                     scene->addChild(pRender->sprite, 10);  
  43.             }  
  44.   
  45.             pRender->sprite->setPosition(ccp(pPos->x, pPos->y));  
  46.         }  
  47.     }  
  48.   
  49. }  
  50.   
  51. void RenderSystem::exit()  
  52. {  
  53.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  54.     for(unsigned int i = 0 ; i < size ; i ++)  
  55.     {  
  56.         RenderComponent* pRender = (RenderComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_RENDER,i);  
  57.         pRender->sprite->stopAllActions();  
  58.         pRender->sprite->removeFromParentAndCleanup(true);  
  59.     }  
  60. }  
#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.   
  5. MovementSystem::MovementSystem(int _priority)  
  6.     :System(_priority)  
  7. {  
  8.   
  9. }  
  10.   
  11. MovementSystem::~MovementSystem()  
  12. {  
  13.   
  14. }  
  15.   
  16. void MovementSystem::enter()  
  17. {  
  18.   
  19. }  
  20.   
  21. void MovementSystem::excute(float dt)  
  22. {  
  23.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  24.     for(unsigned int i =  0 ; i < size ; i ++)  
  25.     {  
  26.         _int64 flag = EntityManager::getEntityManager()->getEntityFlag(i);  
  27.           
  28.         if((flag & (COMPONENT_POSITION | COMPONENT_VELOCITY)) == (COMPONENT_POSITION | COMPONENT_VELOCITY))  
  29.         {  
  30.             PositionComponent* pPos = (PositionComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_POSITION, i);  
  31.             VelocityComponent* pVelocity = (VelocityComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_VELOCITY, i);  
  32.   
  33.             pPos->x += (1.0 / 60) * pVelocity->vx ;  
  34.             pPos->y += (1.0 / 60) * pVelocity->vy ;  
  35.         }  
  36.     }  
  37. }  
  38.   
  39. void MovementSystem::exit()  
  40. {  
  41.   
  42. }  
#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.   
  6. HealthSystem::HealthSystem(int priority)  
  7.     :System(priority)  
  8. {  
  9.   
  10. }  
  11.   
  12. HealthSystem::~HealthSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void HealthSystem::enter()  
  18. {  
  19.   
  20. }  
  21.   
  22. void HealthSystem::excute(float dt)  
  23. {  
  24.       
  25.     EntityManager::Component_List* pHealth = EntityManager::getEntityManager()->getComponentList(COMPONENT_HEALTH);  
  26.   
  27.     for(unsigned int entity = 0 ; entity < EntityManager::getEntityManager()->getEntitySize() ;)  
  28.     {  
  29.         HealthComponent* health = (HealthComponent*)(*pHealth)[entity] ;  
  30.   
  31.         if(health != NULL)  
  32.         {  
  33.             EntityTypeComponent* pType = (EntityTypeComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_ENTITY_TYPE, entity);  
  34.             if(pType->type == EntityTypeComponent::PLAYER)  
  35.             {  
  36.                 GameInfo::getGameInfo()->CUR_HEALTH_PLAYER = health->health ;  
  37.             }  
  38.   
  39.             if(health->health == 0)  
  40.             {  
  41.                 if((EntityManager::getEntityManager()->getEntityFlag(entity) & COMPONENT_ANIMATE) == 0)  
  42.                 {  
  43.                     switch(pType->type)  
  44.                     {  
  45.                     case EntityTypeComponent::GREEN_CUBE:  
  46.                     case EntityTypeComponent::RED_CUBE:  
  47.                         GameInfo::getGameInfo()->CUR_SCORE += 1 ;  
  48.                         break ;  
  49.                       
  50.                     case EntityTypeComponent::ORANGE_CUBE:  
  51.                         GameInfo::getGameInfo()->CUR_SCORE += 2 ;  
  52.                         break ;  
  53.   
  54.                     case EntityTypeComponent::PURPLE_CUBE:  
  55.                         GameInfo::getGameInfo()->CUR_SCORE += 3 ;  
  56.                         break ;  
  57.                     }  
  58.   
  59.                     EntityManager::getEntityManager()->removeEntity(entity);  
  60.                 }  
  61.                 else  
  62.                     entity ++ ;  
  63.             }  
  64.             else  
  65.                 entity ++ ;  
  66.         }  
  67.         else  
  68.             entity ++ ;  
  69.     }  
  70. }  
  71.   
  72. void HealthSystem::exit()  
  73. {  
  74.   
  75. }  
#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.   
  5. CreatorSystem::CreatorSystem(int _priority)  
  6.     :System(_priority),  
  7.     frames(0)  
  8. {  
  9.   
  10. }  
  11.   
  12. CreatorSystem::~CreatorSystem()  
  13. {  
  14.   
  15. }  
  16.   
  17. void CreatorSystem::enter()  
  18. {  
  19.   
  20. }  
  21.   
  22. void CreatorSystem::excute(float dt)  
  23. {  
  24.     frames ++ ;  
  25.   
  26.     static int delta = 0 ;  
  27.     delta = frames / 1800 ;  
  28.   
  29.     if(delta >= 30)  
  30.         delta = 30 ;  
  31.   
  32.     if(frames % (60 - delta ) == 0)  
  33.     {  
  34.         int value = rand()%100 ;  
  35.         float vy = -60 - (frames / 300.0) * 10 ;  
  36.   
  37.         if(0 <= value&& value < 40)  
  38.         {  
  39.             EntityCreator::createGreenCube(0, vy);  
  40.         }  
  41.         else if(40 <= value&& value < 80)  
  42.         {  
  43.             EntityCreator::createRedCube(0, vy);  
  44.         }  
  45.         else if(80 <= value && value < 90)  
  46.         {  
  47.             EntityCreator::createOrangeCube(0, 0.6*vy);  
  48.         }  
  49.         else if(90 <= value && value<100)  
  50.         {  
  51.             EntityCreator::createPurpleCube(0,0.4*vy);  
  52.         }  
  53.     }  
  54.   
  55. }  
  56.   
  57. void CreatorSystem::exit()  
  58. {  
  59.   
  60. }  
#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.   
  5. BoundaryCheckSystem::BoundaryCheckSystem(int priority)  
  6.     :System(priority)  
  7. {  
  8.   
  9. }  
  10.   
  11. BoundaryCheckSystem::~BoundaryCheckSystem()  
  12. {  
  13.   
  14. }  
  15.   
  16. void BoundaryCheckSystem::enter()  
  17. {  
  18.   
  19. }  
  20.   
  21. void BoundaryCheckSystem::excute(float dt)  
  22. {  
  23.       
  24.     EntityManager::Component_List* pPos = EntityManager::getEntityManager()->getComponentList(COMPONENT_POSITION);  
  25.   
  26.       
  27.     EntityManager::Component_List* pType = EntityManager::getEntityManager()->getComponentList(COMPONENT_ENTITY_TYPE);  
  28.       
  29.     unsigned int size = EntityManager::getEntityManager()->getEntitySize();  
  30.   
  31.       
  32.     unsigned int player_entity = -1 ;  
  33.     for(int i = 0 ; i < size ; i ++)  
  34.     {  
  35.         if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::PLAYER)  
  36.         {  
  37.             player_entity = i ;  
  38.             break ;  
  39.         }  
  40.     }  
  41.   
  42.     HealthComponent * health = (HealthComponent*)EntityManager::getEntityManager()->getComponent(COMPONENT_HEALTH, player_entity);  
  43.   
  44.       
  45.     for(unsigned int i = 0 ; i < size ; )  
  46.     {  
  47.         if(((EntityTypeComponent*)(*pType)[i])->type == EntityTypeComponent::SPHERE_BALL)  
  48.         {  
  49.             if(((PositionComponent*)(*pPos)[i])->y > 480)  
  50.             {  
  51.                 EntityManager::getEntityManager()->removeEntity(i);  
  52.                 size -= 1 ;  
  53.                 continue ;  
  54.             }  
  55.         }  
  56.         else  
  57.         {  
  58.             if(((PositionComponent*)(*pPos)[i])->y < 0)  
  59.             {  
  60.                 EntityManager::getEntityManager()->removeEntity(i);  
  61.                 size -= 1 ;  
  62.                 health->health-- ;  
  63.                 continue ;  
  64.             }  
  65.         }  
  66.   
  67.         i ++ ;  
  68.     }  
  69. }  
  70.   
  71. void BoundaryCheckSystem::exit()  
  72. {  
  73.       
  74. }  
#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(部分資源來至於網絡,請大家不要用於商業用途哦!!!)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM