DonerComponents는 비디오 게임을 개발하기위한 C ++ 14 구성 요소 기반 게임 객체 모델 프레임 워크입니다.
이 개념에 익숙하지 않은 경우 예제 프로젝트 나 튜토리얼을 살펴보고 시도해 보는 것이 좋습니다!
여기에서 안정적인 릴리스를 얻을 수 있습니다.
또는 다음과 같이 현재 개발 버전을 확인할 수 있습니다.
git clone https://github.com/Donerkebap13/DonerComponents.git
git submodule update --init --recursive 실행해야합니다.
제안이 있거나 버그를 찾으면 이메일을 통해 직접 저에게 연락 할 수 있습니다. 주저하지 말고 새 문제를 만들어주십시오.
Donercomponents_asteroids_example은 DonerComponents를 사용하는 방법을 보여주기 위해 내가 만든 예제 프로젝트 입니다. 정말 간단한 소행성 소행성 클론입니다. 그 프로젝트에 대한 나의 의도는 프레임 워크가 지금 지원하는 모든 기능을 다음과 같이 보여주는 것입니다.
여기서는 DonerComponents의 주요 시스템의 기본 사용법을 설명하려고합니다. 이것을 읽은 후에는 사물이 어떻게 구성되는지와 사용 방법에 대한 기본 지식이 있습니다. 더 깊이 이해하려면 예제 프로젝트를 살펴 보는 것이 좋습니다.
CDonerComponentsSystems 모든 DonerComponents 에 다른 시스템을 초기화하고 액세스하는 싱글 톤 입니다. 초기화해야합니다.
# include < DonerComponents/CDonerComponentsSystems.h >
DonerComponents::CDonerComponentsSystems::CreateInstance ();
DonerComponents::CDonerComponentsSystems::Get ()->Init();업데이트 :
float elapsed = ...;
DonerComponents::CDonerComponentsSystems::Get ()->Update(elapsed);그리고 파괴 :
DonerComponents::CDonerComponentsSystems::DestroyInstance (); DonerComponents::CGameObject DonerComponents의 주요 배우입니다. 이 클래스에는 동작을 정의하는 다른 DonerComponents::CComponent 포함될 수 있습니다. 또한 부모와 자녀에 대한 정보도 있습니다. 또한 포드 메시지를 받고 구성 요소와 어린이에게 전달할 수 있습니다. 마지막으로, 태그가 지정 될 수도 있습니다.
새 GameObject를 만드는 것만 큼 간단합니다.
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement(); GameObjects가 떨어지지 않는 한 유효한 DonerComponents::CGameObject 반환합니다. 기본적으로 DonerComponents는 동시에 4096 개의 GameObjects를 살 수 있습니다. 이 값은 컴파일러 플래그 -DMAX_GAME_OBJECTS=4096 통해 최대 8.192 GameObjects를 통해 수정 가능합니다.
DonerComponents는 프리 랩의 정의를 지원하므로 사용자는 필요한 곳마다 재사용하기 위해 특정 GameObject 계층을 정의 할 수 있습니다.
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);JSON 파일에서 조립식을 로드 할 수도 있습니다.
DonerComponents::CComponent DonerComponents의 모든 구성 요소의 기본 클래스입니다. 구성 요소는 집계에 의해 GameObject의 동작을 정의합니다. 특정 메시지를 듣고 그에 따라 작업을 수행 할 수 있습니다. 새로운 구성 요소는이 클래스에서 상속해야하며 필요한 경우 몇 가지 기본 방법을 구현할 수 있습니다. 사용자는 최대 512 개의 다른 구성 요소를 등록 할 수 있습니다.
다음은 새로운 구성 요소 생성의 예입니다. 모든 방법의 구현은 선택 사항입니다 .
# include < DonerComponents/component/CComponent.h >
class CCompFoo : public DonerComponents ::CComponent
{
public:
void DoInit () override { m_foo = 0 . 0f ; }
void DoUpdate ( float dt) override { m_foo += dt; }
void DoDestroy () override { m_foo = 0 . 0f ; }
void DoActivate () override { }
void DoDeactivate () override { }
private:
float m_foo;
};시스템 에이 구성 요소를 등록하려면 모든 GameObject를 사용할 수 있으므로 다음을 수행해야합니다.
# include < DonerComponents/component/CComponentFactoryManager.h >
static constexpr int amountOfFooComponentsAvailable = 512 ;
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, amountOfFooComponentsAvailable); DonerComponents::CDonerComponentsSystems 초기화 한 후 MACRO ADD_COMPONENT_FACTORY 사용하여 구성 요소를 시스템에 등록 할 수 있습니다. 수신하는 문자열은 JSON 파일 에서 GameObjects를 구문 분석하는 동안 구성 요소를 식별하는 것입니다. 마지막 매개 변수는 사용 가능한 구성 요소 수입니다. GameObjects와 마찬가지로 동시에 같은 종류의 최대 8.192 개의 구성 요소가 있습니다 .
컴포지트가 시스템에 등록되면 두 가지 방법으로 GameObject에 추가 할 수 있습니다.
DonerComponents::CComponent* component = gameObject->AddComponent<CCompFoo>();
// same as
CCompFoo* component = gameObject->AddComponent<CCompFoo>();
// or
DonerComponents::CComponent* component = gameObject-> AddComponent ( " foo " );
// same as
CCompFoo* component = gameObject-> AddComponent ( " foo " );DonerComponents 에서 구성 요소는 시스템에 등록 된 순서대로 한 번에 하나의 유형으로 유형별로 업데이트됩니다. 그래서 예에서 :
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, 128 );
ADD_COMPONENT_FACTORY ( " bar " , CCompBar, 128 ); 기존의 모든 CCompFoo 모든 기존 CCompBar 구성 요소를 업데이트하기 전에 순차적으로 업데이트됩니다.
Donerserializer를 사용하여 JSON 에서 노출 될 데이터를 정의 할 수 있습니다. 여기에서 사용하는 방법을 확인할 수 있습니다. 여기에서 나는 그 예를 보여줄 것입니다.
class CCompFoo : public DonerComponents ::CComponent
{
DONER_DECLARE_COMPONENT_AS_SERIALIZABLE (CCompFoo)
public:
CCompFoo ();
private:
float m_dummy1;
std::vector<std::string> m_dummy2;
};
DONER_DEFINE_REFLECTION_DATA (CCompFoo,
DONER_ADD_NAMED_VAR_INFO (m_dummy1, " dummy1 " ),
DONER_ADD_NAMED_VAR_INFO(m_dummy2, " dummy2 " )
) m_dummy1 및 m_dummy2 노출 한 후에는 JSON 에서 다음과 같이 값을 정의 할 수 있습니다.
{
"root" : {
"name" : " test1 " ,
"components" : [
{
"name" : " foo " ,
"dummy1" : 1.0 ,
"dummy2" : [ " Test1 " , " Test2 " , " Test3 " ]
}
]
}
}JSON 에서 읽는 방법에 대한 자세한 내용은 이것을 확인하십시오.
DonerComponents는 다른 GameObjects와 구성 요소간에 상호 작용하는 메시지 시스템을 지원합니다. 메시지는 사용자가 정의한 구조/클래스 일 수 있습니다. 일반적으로 데이터 만 포함되지만 논리는 없지만 이에 제한이 없습니다. 이것이 DonerComponents::CComponent 특정 메시지를들을 수있는 방법입니다.
// Somewhere in your code
struct SDummyMessage {
SDummyMessage ( int foo, int bar)
: m_foo(foo), m_bar(bar) {}
int m_foo = 0 ;
int m_bar = 0 ;
}
// Inside your component
CCompFoo::RegisterMessages () {
RegisterMessage (&CCompFoo::OnDummyMessage);
}
void CCompFoo::OnDummyMessage ( const SDummyMessage& message) {
// ...
}원하는 메시지를 등록한 후 다음과 같은 메시지를 보내기 시작할 수 있습니다.
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all gameObject's components.
gameObject-> SendMessage (message);
// This will propagate the message to all gameObject's components and its children's components.
gameObject-> SendMessage (message, DonerComponents::ESendMessageType::Recursive);
// This won't send the message to the current gameObject but it's children.
gameObject-> SendMessageToChildren (message);
// Same as before but recursively through all gameObject's children and children's children.
gameObject-> SendMessageToChildren (message, DonerComponents::ESendMessageType::Recursive); SendMessage 메시지를 동일한 프레임으로 바로 보냅니다. 프레임 끝까지 메시지 보내기를 지연시키려면 대신 후 PostMessage 사용하십시오.
마지막으로, 모든 살아있는 GameObjects에 메시지를 보내려면 BroadcastMessage 사용할 수 있습니다.
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all GameObjects alive.
gameObjectManager-> BroadcastMessage (message); DonerComponents::CHandle 일종의 단일 스레드 스마트 포인터 입니다. 그것들은 특정 DonerComponents::CGameObject 또는 DonerComponents::CComponent , 그들이 여전히 유효하거나 그렇지 않은지, 즉 코드의 다른 곳에서 파괴 된 경우 모든 순간에 알고 있습니다. DonerComponents 의 크기 DonerComponents::CHandle 32 비트입니다. DonerComponents에서 일하는 방식은 DonerComponents의 원시 포인터를 저장하지 않는다는 것입니다 DonerComponents::CGameObject 또는 DonerComponents::CComponent , 우리는 항상 DonerComponents::CHandle 저장하므로 포인트가 여전히 유효한지 확인할 수 있으므로, 우리는 Dangling Pointers에 접근하지 않습니다. 모든 DonerComponents::CHandle DonerComponents::CGameObject 또는 DonerComponents::CComponent 에 캐스트 할 수 있습니다. 캐스트가 유효하고 요소가 여전히 존재하면 유효한 포인터가 요소에 반환됩니다. 그렇지 않으면 nullptr 반환합니다. 예는 다음과 같습니다.
# include < DonerComponents/handle/CHandle.h >
# include < DonerComponents/gameObject/CGameObjectManager.h >
using namespace DonerComponents ;
CHandle gameObjectHandle = m_gameObjectManager-> GetNewElement ();
if (gameObjectHandle) {
// CGameObjectManager has return a valid CGameObject
} else {
// CGameObjectManager has run out of CGameObjects
}
CGameObject* gameObject = gameObjectHandle;
// gameObject will be valid as gameObjectHandle points to an alive gameObject
m_gameObjectManager-> DestroyGameObject (&gameObject);
// gameObject is nullptr at this point
// gameObjectHandle == false as it points to a destroyed gameObject.또한 핸들을 통해 메시지를 보낼 수 있습니다. 핸들이 유효하면 메시지가 제대로 전파됩니다. 그렇지 않으면 메시지가 무시됩니다.
DonerComponents::CHandle handle = gameObject;
SDummyMessage message ( 2 , 3 );
handle.SendMessage(message);
태그는 GameObjects에 더 많은 정보를 추가하는 방법이므로 필터링하고 특정 태그가있는 GameObjects에만 메시지를 보낼 수 있습니다. 시스템에 태그를 추가하는 두 가지 방법이 있으므로 나중에 사용할 수 있습니다. 첫 번째, 코드로 직접 선언합니다.
# include < DonerComponents/tags/CTagsManager.h >
DonerComponents::CTagsManager* tagsManager = DonerComponents::CDonerComponentsSystems::Get()-> GetTagsManager ();
tagsManager-> RegisterTag ( " Tag1 " );
tagsManager-> RegisterTag ( " TagN " );두 번째는 JSON 파일에서 구문 분석합니다.
tagsManager-> ParseTagsFromFile ( " path/to/your/tags.json " );tags.json 파일의 형식은 다음과 유사합니다.
{ "tags" : [ " Tag1 " , " tag2 " , " tagN " ] }DonerComponents는 Donerserializer 덕분에 JSON을 사용하여 디스크로드를 지원하므로 애플리케이션을 실행할 때마다 코드로 처음부터 자산을 작성하는 대신 자산으로 저장할 수있는 사전 랩 또는 장면을 만드는 방법이 있습니다. 기본 사용량은 다음과 같습니다.
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParseSceneFromFile( " path/to/your/scene.json " );Scene.json 파일의 형식은 다음과 유사합니다.
{
"root" : {
"name" : " test1 " ,
"tags" : [ " tag1 " , " tag2 " , " tag3 " ],
"components" : [
{
"name" : " comp_location " ,
"x" : 1 ,
"y" : -3 ,
"z" : 9
},
{
"name" : " comp_rotation " ,
"radians" : 0.2
}
],
"children" : [
{
"name" : " test11 " ,
"tags" : [ " tag1 " , " tag3 " ]
},
{
"name" : " test12 " ,
"initiallyActive" : false
}
]
}
} 장면을 구문 분석하는 대신 PREFAB를 구문 분석하여 DonerComponents::CPrefabManager : ParsePrefabFromFile 호출하면됩니다.
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParsePrefabFromFile( " path/to/your/prefab.json " );이 작업을 수행 한 후 새로운 구문 분석 장면이 사용할 수있는 프리 랩을 사용할 수 있습니다.
DonerComponents는 다른 조립식을 포함하는 사전 랩을 지원하여 구성 요소의 정보를 무시할 수 있습니다.
prefab1.json
{
"root" : {
"name" : " Prefab1 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 1 ,
"y" : -3 ,
"z" : 9
},
{
"name" : " comp_rotation " ,
"radians" : 0.2
},
{
"name" : " sprite " ,
"texture" : " res/common/textures/asteroid_med.png "
}
]
}
}prefab2.json
{
"root" : {
"name" : " Prefab2 " ,
"prefab" : " Prefab1 " ,
"components" : [
{
"name" : " sprite " ,
"texture" : " res/common/textures/flower_big.png "
}
]
}
}Scene.json
{
"root" : {
"name" : " root " ,
"prefab" : " Prefab2 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 0 ,
"y" : 0 ,
"z" : 0
}
]
}
}