Donercomponents เป็นกรอบโมเดลวัตถุเกมที่ใช้ C ++ 14 สำหรับการพัฒนาวิดีโอเกม
หากคุณไม่คุ้นเคยกับแนวคิดนี้ฉันขอแนะนำให้คุณดูโครงการตัวอย่างหรือการสอนและลองดู!
คุณสามารถได้รับการเผยแพร่ที่มีเสถียรภาพที่นี่
หรือคุณสามารถตรวจสอบเวอร์ชันการพัฒนาปัจจุบันด้วย:
git clone https://github.com/Donerkebap13/DonerComponents.git
อย่าลืมเรียกใช้ git submodule update --init --recursive หลังจากนั้น
คุณสามารถติดต่อฉันโดยตรงทางอีเมลหากคุณมีข้อเสนอแนะหรือคุณพบข้อผิดพลาดใด ๆ โปรดอย่าลังเลที่จะสร้างปัญหาใหม่
Donercomponents_asteroids_example เป็น ตัวอย่างโครงการ ที่ฉันสร้างขึ้นเพื่อแสดงวิธีการใช้ donerconents มันเป็นโคลน Asteroids-Wannabe ที่เรียบง่ายจริงๆ ความตั้งใจของฉันกับโครงการนั้นคือการแสดงคุณสมบัติทั้งหมดที่เฟรมเวิร์กรองรับในตอนนี้เช่น:
ที่นี่ฉันจะพยายามแสดงให้เห็นถึงการใช้งานพื้นฐานของระบบหลักของ donerconents หลังจากอ่านสิ่งนี้คุณจะมีความรู้พื้นฐานเกี่ยวกับวิธีการจัดระเบียบและวิธีการใช้งาน เพื่อความเข้าใจที่ลึกซึ้งยิ่งขึ้นฉันขอแนะนำให้คุณดูโครงการตัวอย่าง
CDonerComponentsSystems เป็น ซิงเกิลตัน ที่เริ่มต้นและให้การเข้าถึงระบบที่ แตก ต่างกันทั้งหมด ควรเริ่มต้น:
# 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 ที่กำหนดพฤติกรรมของมัน นอกจากนี้ยังมีข้อมูลเกี่ยวกับพ่อแม่และลูก ๆ นอกจากนี้ยังสามารถรับข้อความ POD และส่งต่อไปยังส่วนประกอบและลูก ๆ ของมัน สุดท้าย แต่ไม่ท้ายสุดมันสามารถติดแท็กได้
การสร้าง GameObject ใหม่นั้นง่ายเหมือน:
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement(); จะส่งคืน DonerComponents::CGameObject ตราบใดที่มันยังไม่หมด GameObjects ที่จะสร้าง โดยค่าเริ่มต้น donercomponents สามารถมี 4096 gameObjects ที่ยังมีชีวิตอยู่ในเวลาเดียวกัน ค่านี้สามารถแก้ไขได้ผ่าน Flag คอมไพเลอร์ -DMAX_GAME_OBJECTS=4096 ที่มี ค่าสูงสุด 8.192 GameObjects
donercomponents สนับสนุนคำจำกัดความของ prefabs ดังนั้นผู้ใช้สามารถกำหนดลำดับชั้นของ GameObject ที่เฉพาะเจาะจงสำหรับการนำกลับมาใช้ใหม่ทุกที่ที่ต้องการ:
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);PREFAB สามารถโหลดได้จากไฟล์ 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 สตริงที่ได้รับคือการระบุส่วนประกอบในขณะที่แยกวิเคราะห์ gameObjects ของเราจาก ไฟล์ JSON พารามิเตอร์สุดท้ายคือจำนวนส่วนประกอบที่จะใช้ได้ เช่นเดียวกับ 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 ที่มีอยู่ทั้งหมด
คุณสามารถกำหนดข้อมูลที่จะได้รับการแก้ไขใน JSON โดยใช้ donerserializer คุณสามารถตรวจสอบวิธีการใช้งานได้ที่นี่ ที่นี่ฉันจะแสดงตัวอย่าง
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::CHandle คือ 32 บิต วิธีการทำงานใน donercomponents คือ เราไม่เคยเก็บพอยน์เตอร์ดิบ ของ DonerComponents::CGameObject หรือ DonerComponents::CComponent เรามักจะเก็บ DonerComponents::CHandle ดังนั้นเราจึงสามารถตรวจสอบว่าองค์ประกอบที่พวกเขาชี้ไปนั้นยังคงถูกต้องหรือไม่ 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 รองรับการโหลดจากดิสก์โดยใช้ JSON ขอบคุณ Donerserializer ดังนั้นจึงมีวิธีการสร้างสำเร็จรูปหรือฉากที่สามารถเก็บไว้เป็นสินทรัพย์แทนที่จะสร้างพวกเขาตั้งแต่เริ่มต้นในรหัสทุกครั้งที่เราเรียกใช้แอปพลิเคชันของเรา การใช้งานขั้นพื้นฐานมีดังนี้:
# 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 " );หลังจากทำเช่นนี้ prefab จะพร้อมใช้งานสำหรับฉากที่แยกวิเคราะห์ใหม่ใด ๆ ที่จะใช้
donercomponents รองรับสำเร็จรูปที่มี prefab อื่น ๆ สามารถแทนที่ข้อมูลขององค์ประกอบ:
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
}
]
}
}