วัตถุเกมควรรับรู้ซึ่งกันและกันอย่างไร?


18

ฉันพบว่ามันยากที่จะหาวิธีจัดระเบียบวัตถุในเกมเพื่อให้เป็น polymorphic แต่ในเวลาเดียวกันไม่ใช่ polymorphic

นี่คือตัวอย่าง: สมมติว่าเราต้องการวัตถุทั้งหมดของเราและupdate() draw()ในการทำเช่นนั้นเราจำเป็นต้องกำหนดคลาสพื้นฐานGameObjectซึ่งมีสองวิธีเสมือนบริสุทธิ์และให้ polymorphism kicks ใน:

class World {
private:
    std::vector<GameObject*> objects;
public:
    // ...
    update() {
        for (auto& o : objects) o->update();
        for (auto& o : objects) o->draw(window);
    }
};

เมธอดการอัพเดตควรดูแลสิ่งที่ระบุว่าอ็อบเจ็กต์คลาสเฉพาะต้องอัพเดต ความจริงก็คือแต่ละวัตถุจำเป็นต้องรู้เกี่ยวกับโลกรอบตัวพวกเขา ตัวอย่างเช่น:

  • เหมืองจำเป็นต้องรู้ว่ามีใครปะทะกับมันหรือไม่
  • ทหารควรรู้ว่าทหารของทีมอื่นอยู่ใกล้หรือไม่
  • ซอมบี้ควรรู้ว่าสมองที่อยู่ใกล้ที่สุดภายในรัศมีนั้นอยู่ที่ไหน

สำหรับการติดต่อเรื่อย ๆ (เช่นคนแรก) on_collide(GameObject*)ผมคิดว่าตรวจสอบการชนอาจมอบหมายว่าจะทำอย่างไรในกรณีที่เฉพาะเจาะจงของการชนกับวัตถุที่ตัวเองมี

ข้อมูลอื่น ๆ ส่วนใหญ่ (เช่นอีกสองตัวอย่าง) อาจถูกสอบถามโดยโลกของเกมที่ส่งผ่านไปยังupdateวิธีการ ตอนนี้โลกไม่ได้แยกแยะวัตถุขึ้นอยู่กับชนิดของพวกเขา (มันเก็บวัตถุทั้งหมดในภาชนะ polymorphic เดี่ยว) ดังนั้นสิ่งที่ในความเป็นจริงมันจะกลับมาพร้อมกับที่เหมาะสำหรับเป็นภาชนะของworld.entities_in(center, radius) GameObject*แต่แน่นอนว่าทหารไม่ต้องการโจมตีทหารอื่นจากทีมของเขาและซอมบี้ก็ไม่ได้พูดถึงซอมบี้ตัวอื่น ดังนั้นเราต้องแยกแยะพฤติกรรม วิธีแก้ไขอาจเป็นดังนี้:

void TeamASoldier::update(const World& world) {
    auto list = world.entities_in(position, eye_sight);
    for (const auto& e : list)
        if (auto enemy = dynamic_cast<TeamBSoldier*>(e))
            // shoot towards enemy
}

void Zombie::update(const World& world) {
    auto list = world.entities_in(position, eye_sight);
    for (const auto& e : list)
        if (auto enemy = dynamic_cast<Human*>(e))
            // go and eat brain
}

แต่แน่นอนว่าจำนวนdynamic_cast<>เฟรมต่อเฟรมอาจสูงอย่างน่ากลัวและเราทุกคนรู้ว่าdynamic_castจะช้าแค่ไหน ปัญหาเดียวกันนี้ยังใช้กับon_collide(GameObject*)ตัวแทนที่เรากล่าวถึงก่อนหน้านี้

ดังนั้นวิธีที่เหมาะในการจัดระเบียบรหัสเพื่อให้วัตถุสามารถรับรู้วัตถุอื่น ๆ และสามารถละเว้นพวกเขาหรือดำเนินการตามชนิดของพวกเขา


1
ฉันคิดว่าคุณกำลังมองหาการปรับใช้ C ++ RTTI ที่หลากหลาย อย่างไรก็ตามคำถามของคุณดูเหมือนจะไม่เพียงเกี่ยวกับกลไกของ RTTI ที่รอบคอบเท่านั้น สิ่งที่คุณต้องการนั้นเป็นสิ่งจำเป็นสำหรับมิดเดิลแวร์ใด ๆ ที่เกมจะใช้ (ระบบอนิเมชั่น, ฟิสิกส์เพื่อตั้งชื่อ) ขึ้นอยู่กับรายการของแบบสอบถามที่รองรับคุณสามารถโกงวิธีของ RTTI โดยใช้ ID และดัชนีในอาร์เรย์หรือคุณจะสิ้นสุดการออกแบบโปรโตคอลเต็มรูปแบบสำหรับการสนับสนุนทางเลือกที่ถูกกว่าเพื่อ dynamic_cast และ type_info
Teodron

ฉันแนะนำไม่ให้ใช้ระบบชนิดสำหรับตรรกะของเกม ตัวอย่างเช่นแทนที่จะขึ้นอยู่กับผลลัพธ์ของdynamic_cast<Human*>ใช้สิ่งที่ต้องการbool GameObject::IsHuman()ซึ่งจะส่งกลับfalseโดยค่าเริ่มต้น แต่ถูกแทนที่เพื่อกลับtrueในHumanชั้นเรียน
congusbongus

พิเศษ: คุณแทบจะไม่เคยส่งวัตถุจำนวนมากไปยังเอนทิตีอื่น ๆ ที่อาจสนใจพวกเขา นั่นเป็นการเพิ่มประสิทธิภาพที่เห็นได้ชัดว่าคุณต้องพิจารณาอย่างแท้จริง
teodron

@congusbongus การใช้ vtable และการIsAแทนที่แบบกำหนดเองได้รับการพิสูจน์แล้วว่าดีกว่าการหล่อแบบไดนามิกในทางปฏิบัติสำหรับฉันเท่านั้น สิ่งที่ดีที่สุดที่ต้องทำคือให้ผู้ใช้เรียงลำดับรายการข้อมูลแทนการวนซ้ำแบบสุ่มในเอนทิตีพูลทั้งหมด
Teodron

4
@ Jeffefrey: นึกคิดคุณไม่ได้เขียนรหัสเฉพาะประเภท คุณเขียนส่วนต่อประสาน -รหัสเฉพาะ ("ส่วนต่อประสาน" ในแง่ทั่วไป) ตรรกะของคุณสำหรับ a TeamASoldierและTeamBSoldierเหมือนกันจริงๆ - ยิงใส่ใครก็ได้ในทีมอื่น สิ่งที่ต้องการทั้งหมดของเอนทิตีอื่น ๆ คือGetTeam()วิธีการที่เฉพาะเจาะจงที่สุดและตามตัวอย่างของ congusbongus ที่สามารถแยกออกเป็นIsEnemyOf(this)ส่วนต่อประสานได้ รหัสไม่จำเป็นต้องสนใจเกี่ยวกับการจำแนกประเภทอนุกรมวิธานของทหารซอมบี้ผู้เล่น ฯลฯ มุ่งเน้นไปที่การโต้ตอบไม่ใช่ประเภท
Sean Middleditch

คำตอบ:


11

แทนที่จะใช้การตัดสินใจของแต่ละเอนทิตี้ในตัวคุณเองคุณสามารถเลือกรูปแบบการควบคุมได้ คุณจะมีคลาสตัวควบคุมส่วนกลางที่รับรู้วัตถุทั้งหมด (ซึ่งมีความสำคัญต่อพวกเขา) และควบคุมพฤติกรรมของพวกเขา

MovementController จะจัดการการเคลื่อนไหวของวัตถุทั้งหมดที่สามารถเคลื่อนที่ได้ (ทำการค้นหาเส้นทางปรับปรุงตำแหน่งตามเวกเตอร์การเคลื่อนไหวปัจจุบัน)

MineBehaviorController จะตรวจสอบเหมืองทั้งหมดและทหารทั้งหมดและสั่งให้เหมืองระเบิดเมื่อทหารเข้ามาใกล้เกินไป

ZombieBehaviorController จะตรวจสอบซอมบี้ทั้งหมดและทหารในละแวกของพวกเขาเลือกเป้าหมายที่ดีที่สุดสำหรับซอมบี้แต่ละตัวและสั่งให้ย้ายไปที่นั่นและโจมตีมัน

SoldierBehaviorController จะวิเคราะห์สถานการณ์ทั้งหมดแล้วทำตามคำแนะนำทางยุทธวิธีสำหรับทหารทุกคน (คุณย้ายไปที่นั่นคุณยิงสิ่งนี้คุณรักษาคนนั้น ... ) การดำเนินการจริงของคำสั่งระดับสูงเหล่านี้จะได้รับการจัดการโดยตัวควบคุมระดับล่าง เมื่อคุณใช้ความพยายามคุณสามารถทำให้ AI สามารถตัดสินใจสหกรณ์ได้อย่างชาญฉลาด


1
อาจเป็นที่รู้จักกันว่า "ระบบ" ที่จัดการตรรกะสำหรับส่วนประกอบบางประเภทในสถาปัตยกรรมเอ็นติตี้ - คอมโพเนนต์
Teodron

นั่นฟังดูเหมือนโซลูชันแบบ C คอมโพเนนต์ถูกจัดกลุ่มในstd::maps และเอนทิตีเป็น ID เท่านั้นจากนั้นเราต้องสร้างระบบประเภทบางประเภท (อาจมีส่วนประกอบของแท็กเนื่องจากตัวแสดงภาพจะต้องรู้ว่าจะวาดอะไร); และถ้าเราไม่ต้องการทำสิ่งนั้นเราจะต้องมีส่วนประกอบการวาดภาพ แต่มันต้องการส่วนประกอบตำแหน่งที่จะรู้ว่าจะดึงที่ไหนดังนั้นเราจึงสร้างการพึ่งพาระหว่างส่วนประกอบที่เราแก้ปัญหาด้วยระบบการส่งข้อความที่ซับซ้อนเป็นพิเศษ นี่คือสิ่งที่คุณกำลังแนะนำ?
รองเท้า

1
@ Jefffrey "นั่นฟังดูเหมือนโซลูชั่นแบบ C" - แม้ว่ามันจะเป็นจริงทำไมมันถึงเป็นสิ่งที่ไม่ดีล่ะ? ข้อกังวลอื่น ๆ อาจใช้ได้ แต่มีวิธีแก้ปัญหาสำหรับพวกเขา น่าเสียดายที่ความคิดเห็นสั้นเกินไปที่จะกล่าวถึงแต่ละข้ออย่างถูกต้อง
ฟิลิปป์

1
@Jefffrey ใช้วิธีการที่ตัวส่วนประกอบไม่มีตรรกะใด ๆ และ "ระบบ" รับผิดชอบการจัดการตรรกะทั้งหมดไม่สร้างการพึ่งพาระหว่างส่วนประกอบและไม่ต้องการระบบการส่งข้อความที่ซับซ้อนสุด (อย่างน้อยก็เกือบจะไม่ซับซ้อน) . ดูตัวอย่าง: gamadu.com/artemis/tutorial.html

1

ก่อนอื่นลองใช้คุณลักษณะเพื่อให้วัตถุยังคงเป็นอิสระต่อกันเมื่อใดก็ตามที่เป็นไปได้ โดยเฉพาะอย่างยิ่งคุณต้องการทำเช่นนั้นสำหรับมัลติเธรด ในตัวอย่างรหัสแรกของคุณชุดของวัตถุทั้งหมดสามารถแบ่งออกเป็นชุดที่ตรงกับจำนวนแกน CPU และได้รับการปรับปรุงอย่างมีประสิทธิภาพมาก

แต่อย่างที่คุณพูดการมีปฏิสัมพันธ์กับวัตถุอื่นเป็นสิ่งจำเป็นสำหรับคุณสมบัติบางอย่าง นั่นหมายความว่าสถานะของวัตถุทั้งหมดจะต้องทำข้อมูลให้ตรงกันในบางจุด กล่าวอีกนัยหนึ่งแอปพลิเคชันของคุณต้องรอให้งานแบบขนานทั้งหมดเสร็จสิ้นก่อนจากนั้นจึงใช้การคำนวณที่เกี่ยวข้องกับการโต้ตอบ เป็นการดีที่จะลดจำนวนจุดการซิงโครไนซ์เหล่านี้เนื่องจากพวกเขามักจะบอกว่าเธรดบางตัวต้องรอให้คนอื่น ๆ เสร็จสิ้น

ดังนั้นฉันขอแนะนำให้บัฟเฟอร์ข้อมูลเหล่านั้นเกี่ยวกับวัตถุที่ต้องการจากภายในวัตถุอื่น ด้วยโกลบอลบัฟเฟอร์ดังกล่าวคุณสามารถอัพเดตออบเจ็กต์ทั้งหมดที่เป็นอิสระจากกัน แต่ขึ้นอยู่กับตัวเองและโกลบอลบัฟเฟอร์ซึ่งทั้งง่ายและรวดเร็วในการบำรุงรักษา ในการประทับเวลาคงที่ให้พูดว่าหลังจากแต่ละเฟรมอัปเดตบัฟเฟอร์ด้วยสถานะของวัตถุปัจจุบัน

ดังนั้นสิ่งที่คุณทำหนึ่งครั้งต่อหนึ่งเฟรมคือ 1 บัฟเฟอร์สถานะวัตถุปัจจุบันทั่วโลก 2. อัพเดตวัตถุทั้งหมดตามตัวเองและบัฟเฟอร์ 3. วาดวัตถุของคุณแล้วเริ่มต้นใหม่ด้วยการต่ออายุบัฟเฟอร์


1

ใช้ระบบที่ใช้องค์ประกอบซึ่งคุณมี GameObject ของ barebones ที่มี 1 ส่วนประกอบขึ้นไปซึ่งกำหนดพฤติกรรมของพวกเขา

ตัวอย่างเช่นสมมติว่าวัตถุบางอย่างควรเลื่อนไปทางซ้ายและขวาตลอดเวลา (แพลตฟอร์ม) คุณอาจสร้างองค์ประกอบดังกล่าวและแนบไปกับ GameObject

ทีนี้บอกว่าวัตถุเกมควรจะหมุนอย่างช้าๆตลอดเวลาคุณสามารถสร้างส่วนประกอบแยกต่างหากซึ่งทำเช่นนั้นและแนบกับ GameObject

จะเกิดอะไรขึ้นถ้าคุณต้องการมีแพลตฟอร์มที่เคลื่อนไหวที่หมุนได้ในรูปแบบการสืบทอดคลาสแบบดั้งเดิมซึ่งยากที่จะทำโดยไม่ต้องทำรหัสซ้ำ

ความสวยงามของระบบนี้คือแทนที่จะมีคลาส Rotatable หรือ MovingPlatform คุณสามารถแนบทั้งสององค์ประกอบเหล่านี้เข้ากับ GameObject และตอนนี้คุณมีแพลตฟอร์มการเคลื่อนย้ายที่หมุนอัตโนมัติ

ส่วนประกอบทั้งหมดมีคุณสมบัติ 'requireUpdate' ซึ่งในขณะที่เป็นจริง GameObject จะเรียกวิธีการ 'update' ในองค์ประกอบดังกล่าว ตัวอย่างเช่นสมมติว่าคุณมีองค์ประกอบที่ลากได้องค์ประกอบนี้เมื่อเลื่อนเมาส์ลง (หากอยู่เหนือ GameObject) สามารถตั้งค่า 'requireUpdate' เป็นจริงได้และจากนั้นให้วางเมาส์เป็นเท็จ อนุญาตให้ติดตามเมาส์ได้เฉพาะเมื่อเมาส์ไม่ทำงาน

หนึ่งในผู้พัฒนาของ Tony Hawk Pro Skater มีระบบ defacto เขียนขึ้นมาและมันก็คุ้มค่าที่จะอ่าน: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/


1

ชอบองค์ประกอบมากกว่ามรดก

คำแนะนำที่แข็งแกร่งที่สุดของฉันนอกเหนือจากนี้คือ: อย่าเข้ามาในความคิดของ "ฉันต้องการให้มันยืดหยุ่นอย่างยิ่งยวด" ความยืดหยุ่นนั้นยอดเยี่ยม แต่โปรดจำไว้ว่าในบางระดับในระบบ จำกัด ใด ๆเช่นเกมมีชิ้นส่วนอะตอมที่ใช้ในการสร้างทั้งหมด ไม่ทางใดก็ทางหนึ่งการประมวลผลของคุณขึ้นอยู่กับประเภทของอะตอมที่กำหนดไว้ล่วงหน้า กล่าวอีกนัยหนึ่งการจัดประเภทข้อมูล "ใด ๆ " (ถ้าเป็นไปได้) จะไม่ช่วยคุณในระยะยาวหากคุณไม่มีรหัสที่จะประมวลผล พื้นฐานรหัสทั้งหมดจะต้องแยกวิเคราะห์ / ประมวลผลข้อมูลตามข้อกำหนดที่รู้จัก ... ซึ่งหมายถึงชุดประเภทที่กำหนดไว้ล่วงหน้า ชุดใหญ่แค่ไหน? แล้วแต่คุณ.

บทความนี้นำเสนอข้อมูลเชิงลึกเกี่ยวกับหลักการขององค์ประกอบที่มีต่อมรดกในการพัฒนาเกมผ่านสถาปัตยกรรมเอนทิตีส่วนประกอบที่แข็งแกร่งและมีประสิทธิภาพ

ด้วยการสร้างเอนทิตีจากส่วนย่อย (ต่างกัน) ของส่วนประกอบบางส่วนที่กำหนดไว้ล่วงหน้าคุณจะได้นำเสนอ AIs ที่เป็นรูปธรรมวิธีการที่จะเข้าใจโลกและนักแสดงรอบ ๆ ชิ้นเล็กชิ้นน้อยโดยการอ่านสถานะขององค์ประกอบของนักแสดงเหล่านั้น


1

โดยส่วนตัวผมแนะนำให้เอาฟังก์ชั่นการดึงออกจากคลาส Object เอง ฉันยังแนะนำให้เก็บตำแหน่ง / พิกัดของวัตถุออกจากตัววัตถุเอง

วิธีการวาด () นั้นกำลังจะจัดการกับ API การเรนเดอร์ระดับต่ำทั้ง OpenGL, OpenGL ES, Direct3D, เลเยอร์ของคุณบน API เหล่านั้นหรือเอ็นจิ้น API อาจเป็นไปได้ว่าคุณต้องสลับระหว่าง (ถ้าคุณต้องการสนับสนุน OpenGL + OpenGL ES + Direct3D เป็นต้น

GameObject นั้นควรมีข้อมูลพื้นฐานเกี่ยวกับรูปลักษณ์ภายนอกเช่นตาข่ายหรืออาจเป็นชุดที่ใหญ่กว่ารวมถึงอินพุต shader สถานะภาพเคลื่อนไหวและอื่น ๆ

นอกจากนี้คุณจะต้องการไปป์ไลน์กราฟิคแบบยืดหยุ่น จะเกิดอะไรขึ้นถ้าคุณต้องการสั่งวัตถุตามระยะทางจากกล้อง หรือประเภทวัสดุ จะเกิดอะไรขึ้นถ้าคุณต้องการวาดวัตถุ 'ที่เลือก' ให้เป็นสีอื่น จะเกิดอะไรขึ้นถ้าแทนที่จะส่งเป็น soo ในขณะที่คุณเรียกใช้ฟังก์ชั่นการวาดบนวัตถุแทนมันจะใส่ไว้ในรายการคำสั่งของการกระทำเพื่อให้การเรนเดอร์ใช้ (อาจจำเป็นสำหรับเธรด) คุณสามารถทำสิ่งนั้นกับระบบอื่น แต่มันเป็น PITA

สิ่งที่ฉันแนะนำคือแทนที่จะวาดโดยตรงคุณผูกวัตถุทั้งหมดที่คุณต้องการโครงสร้างข้อมูลอื่น การเชื่อมโยงนั้นจำเป็นต้องมีการอ้างอิงไปยังตำแหน่งวัตถุและข้อมูลการแสดงผลเท่านั้น

ระดับ / ชิ้น / พื้นที่ / แผนที่ / ฮับ / ทั้งโลกของคุณ / สิ่งที่ได้รับดัชนีพิเศษนี้มีวัตถุและส่งกลับพวกเขาตามแบบสอบถามการประสานงานและอาจเป็นรายการที่เรียบง่ายหรือบางอย่างเช่น Octree มันอาจจะเป็นสิ่งห่อหุ้มสำหรับสิ่งที่นำไปใช้โดยเอนจิ้นฟิสิกส์ของบุคคลที่สามในฐานะฉากฟิสิกส์ มันช่วยให้คุณทำสิ่งต่าง ๆ เช่น "ค้นหาวัตถุทั้งหมดที่อยู่ในมุมมองของกล้องพร้อมกับพื้นที่พิเศษรอบ ๆ พวกมัน" หรือสำหรับเกมที่ง่ายกว่าซึ่งคุณสามารถแสดงทุกสิ่งได้ทั่วทั้งรายการ

ดัชนีพิเศษไม่จำเป็นต้องมีข้อมูลการระบุตำแหน่งจริง พวกมันทำงานโดยเก็บวัตถุไว้ในโครงสร้างต้นไม้โดยสัมพันธ์กับตำแหน่งของวัตถุอื่น พวกเขาสามารถเป็นแคชแคชแบบสูญเสียซึ่งช่วยให้สามารถค้นหาวัตถุได้อย่างรวดเร็วตามตำแหน่งของวัตถุ ไม่จำเป็นต้องทำซ้ำพิกัด X, Y, Z ที่แท้จริงของคุณ ต้องบอกว่าคุณทำได้ถ้าคุณต้องการที่จะเก็บ

ในความเป็นจริงแล้ววัตถุในเกมของคุณไม่จำเป็นต้องมีข้อมูลตำแหน่งของตัวเองด้วยซ้ำ ตัวอย่างเช่นวัตถุที่ไม่ได้ใส่ในระดับไม่ควรมีพิกัด x, y, z ที่ไม่มีเหตุผล คุณสามารถมีสิ่งนั้นในดัชนีพิเศษ หากคุณต้องการค้นหาพิกัดของวัตถุตามการอ้างอิงจริงของมันคุณจะต้องมีการเชื่อมโยงระหว่างวัตถุกับกราฟฉาก (กราฟฉากใช้สำหรับคืนวัตถุตามพิกัด แต่ช้าที่พิกัดคืนตามวัตถุ) .

เมื่อคุณเพิ่มวัตถุในระดับ มันจะทำสิ่งต่อไปนี้:

1) สร้างโครงสร้างตำแหน่ง:

 class Location { 
     float x, y, z; // Or a special Coordinates class, or a vec3 or whatever.
     SpacialIndex& spacialIndex; // Note this could be the area/level/map/whatever here
 };

สิ่งนี้อาจเป็นการอ้างอิงถึงวัตถุในเอ็นจิ้นฟิสิกส์ของบุคคลที่สาม หรืออาจเป็นพิกัดออฟเซ็ตที่มีการอ้างอิงไปยังตำแหน่งอื่น (สำหรับกล้องติดตามหรือวัตถุหรือตัวอย่างที่แนบมา) ด้วยความหลากหลายมันอาจเป็นได้ทั้งขึ้นอยู่กับว่ามันเป็นวัตถุแบบคงที่หรือแบบไดนามิก โดยการอ้างอิงถึงดัชนีพิเศษที่นี่เมื่อพิกัดได้รับการปรับปรุงดัชนีอวกาศอาจเกินไป

หากคุณกังวลเกี่ยวกับการจัดสรรหน่วยความจำแบบไดนามิกให้ใช้พูลหน่วยความจำ

2) การเชื่อมโยง / เชื่อมโยงระหว่างวัตถุของคุณที่ตั้งและกราฟฉาก

typedef std::pair<Object, Location> SpacialBinding.

3) การผูกจะถูกเพิ่มเข้าไปในดัชนีพิเศษภายในระดับที่จุดที่เหมาะสม

เมื่อคุณกำลังเตรียมที่จะเรนเดอร์

1) รับกล้อง (มันจะเป็นวัตถุอื่นยกเว้นที่ตั้งจะติดตามตัวละครของผู้เล่นและนักแสดงของคุณจะมีการอ้างอิงพิเศษกับมันในความเป็นจริงนั่นคือทั้งหมดที่มันต้องการจริงๆ)

2) รับ SpacialBinding ของกล้อง

3) รับดัชนีพิเศษจากการผูก

4) ค้นหาวัตถุที่กล้องมองเห็น

5A) คุณต้องมีการประมวลผลข้อมูลภาพ พื้นผิวที่อัปโหลดไปยัง GPU และอื่น ๆ สิ่งนี้จะทำได้ดีที่สุดล่วงหน้า (เช่นในระดับโหลด) แต่อาจจะทำที่รันไทม์ (สำหรับโลกเปิดคุณสามารถโหลดสิ่งต่าง ๆ เมื่อคุณเข้าใกล้ก้อน แต่ควรจะทำล่วงหน้า)

5B) เลือกสร้างต้นไม้เรนเดอร์แคชถ้าคุณต้องการจัดเรียงเชิงลึก / วัสดุหรือติดตามวัตถุใกล้เคียงอาจปรากฏให้เห็นในภายหลัง มิฉะนั้นคุณสามารถสืบค้นดัชนีพิเศษทุกครั้งที่มันขึ้นอยู่กับข้อกำหนดของเกม / ประสิทธิภาพ

ผู้สร้างภาพของคุณจะต้องมีวัตถุ RenderBinding ที่จะเชื่อมโยงระหว่างวัตถุพิกัด

class RenderBinding {
    Object& object;
    RenderInformation& renderInfo;
    Location& location // This could just be a coordinates class.
}

จากนั้นเมื่อคุณแสดงผลให้เรียกใช้ผ่านรายการ

ฉันใช้การอ้างอิงข้างต้น แต่อาจเป็นตัวชี้อัจฉริยะตัวชี้ดิบตัวจัดการวัตถุและอื่น ๆ

แก้ไข:

class Game {
    weak_ptr<Camera> camera;
    Level level1;

    void init() {
        Camera camera(75.0_deg, 1.025_ratio, 1000_meters);
        auto template_player = loadObject("Player.json")
        auto player = level1.addObject(move(player), Position(1.0, 2.0, 3.0));
        level1.addObject(move(camera), getRelativePosition(player));

        auto template_bad_guy = loadObject("BadGuy.json")
        level1.addObject(template_bad_guy, {10, 10, 20});
        level1.addObject(template_bad_guy, {10, 30, 20});
        level1.addObject(move(template_bad_guy), {50, 30, 20});
    }

    void render() {
        camera->getFrustrum();
        auto level = camera->getLocation()->getLevel();
        auto object = level.getVisible(camera);
        for(object : objects) {
            render(objects);
        }
    }

    void render(Object& object) {
        auto ri = object.getRenderInfo();
        renderVBO(ri.getVBO());
    }

    Object loadObject(string file) {
        Object object;
        // Load file from disk and set the properties
        // Upload mesh data, textures to GPU. Load shaders whatever.
        object.setHitPoints(// values from file);
        object.setRenderInfo(// data from 3D api);
    }
}

class Level {
    Octree octree;
    vector<ObjectPtr> objects;
    // NOTE: If your level is mesh based there might also be a BSP here. Or a hightmap for an openworld
    // There could also be a physics scene here.
    ObjectPtr addObject(Object&& object, Position& pos) {
        Location location(pos, level, object);
        objects.emplace_back(object);
        object->setLocation(location)
        return octree.addObject(location);
    }
    vector<Object> getVisible(Camera& camera) {
        auto f = camera.getFtrustrum();
        return octree.getObjectsInFrustrum(f);
    }
    void updatePosition(LocationPtr l) {
        octree->updatePosition(l);
    }
}

class Octree {
    OctreeNode root_node;
    ObjectPtr add(Location&& object) {
        return root_node.add(location);
    }
    vector<ObjectPtr> getObjectsInRadius(const vec3& position, const float& radius) { // pass to root_node };
    vector<ObjectPtr> getObjectsinFrustrum(const FrustrumShape frustrum;) {//...}
    void updatePosition(LocationPtr* l) {
        // Walk up from l.octree_node until you reach the new place
        // Check if objects are colliding
        // l.object.CollidedWith(other)
    }
}

class Object {
    Location location;
    RenderInfo render_info;
    Properties object_props;
    Position getPosition() { return getLocation().position; }
    Location getLocation() { return location; }
    void collidedWith(ObjectPtr other) {
        // if other.isPickup() && object.needs(other.pickupType()) pick it up, play sound whatever
    }
}

class Location {
    Position position;
    LevelPtr level;
    ObjectPtr object;
    OctreeNote octree_node;
    setPosition(Position position) {
        position = position;
        level.updatePosition(this);
    }
}

class Position {
    vec3 coordinates;
    vec3 rotation;
}

class RenderInfo {
    AnimationState anim;
}
class RenderInfo_OpenGL : public RenderInfo {
    GLuint vbo_object;
    GLuint texture_object;
    GLuint shader_object;
}

class Camera: public Object {
    Degrees fov;
    Ratio aspect;
    Meters draw_distance;
    Frustrum getFrustrum() {
        // Use above to make a skewed frustum box
    }
}

สำหรับทำให้สิ่งต่าง ๆ 'ตระหนักถึง' ซึ่งกันและกัน นั่นคือการตรวจจับการชนกัน มันจะถูกนำมาใช้ใน Octree อาจ คุณจะต้องให้การติดต่อกลับในวัตถุหลักของคุณ สิ่งนี้จัดการได้ดีที่สุดโดยเอ็นจิ้นฟิสิกส์ที่เหมาะสมเช่น Bullet ในกรณีนี้ให้แทนที่ Octree ด้วย PhysicsScene และ Position ด้วยลิงก์ไปยังสิ่งที่ต้องการ CollisionMesh.getPosition ()


ว้าวนี่ดูดีมาก ฉันคิดว่าฉันเข้าใจแนวคิดพื้นฐานแล้ว แต่ไม่มีตัวอย่างเพิ่มเติมฉันไม่สามารถรับมุมมองด้านนอกของเรื่องนี้ได้ คุณมีข้อมูลอ้างอิงหรือตัวอย่างสดเพิ่มเติมอีกหรือไม่? (ฉันจะอ่านคำตอบนี้ต่อไปสักพัก)
รองเท้า

ไม่มีตัวอย่างใด ๆ จริง ๆ เป็นเพียงสิ่งที่ฉันวางแผนจะทำเมื่อฉันมีเวลา ฉันจะเพิ่มขึ้นไม่กี่ของการเรียนโดยรวมและดูว่าจะช่วยให้มีนี้และนี้ มันเกี่ยวกับคลาสอ็อบเจ็กต์มากกว่าความสัมพันธ์หรือการเรนเดอร์ ในขณะที่ฉันไม่ได้นำไปใช้ด้วยตนเองอาจมีข้อผิดพลาดบิตที่จำเป็นต้องใช้ในการทำงานหรือประสิทธิภาพการทำงาน แต่ฉันคิดว่าโครงสร้างโดยรวมก็โอเค
ดาวิดซี. บิชอป
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.