ประโยชน์หลักสองประการที่ฉันได้ยินมาอย่างต่อเนื่องเกี่ยวกับระบบเอนทิตี้คือ 1) การสร้างเอนทิตีแบบใหม่ได้ง่ายเนื่องจากไม่ต้องยุ่งเกี่ยวกับลำดับชั้นการสืบทอดที่ซับซ้อนและ 2) ประสิทธิภาพแคช
โปรดทราบว่า (1) เป็นข้อดีของการออกแบบโดยใช้องค์ประกอบไม่ใช่แค่ ES / ECS คุณสามารถใช้ส่วนประกอบได้หลายวิธีที่ไม่มีส่วน "ระบบ" และใช้งานได้ดี (และเกมอินดี้และ AAA จำนวนมากใช้สถาปัตยกรรมดังกล่าว)
โมเดลอ็อบเจ็กต์ Unity มาตรฐาน (การใช้GameObject
และMonoBehaviour
อ็อบเจ็กต์) ไม่ใช่ ECS แต่เป็นการออกแบบโดยใช้คอมโพเนนต์ แน่นอนว่าคุณสมบัติ Unity ECS ที่ใหม่กว่านั้นเป็น ECS จริง
ระบบจำเป็นต้องทำงานกับส่วนประกอบได้มากกว่าหนึ่งองค์ประกอบนั่นคือทั้งระบบเรนเดอร์และระบบฟิสิกส์จำเป็นต้องเข้าถึงส่วนประกอบที่แปลงสภาพ
ECS บางตัวเรียงลำดับส่วนประกอบคอนเทนเนอร์ตาม Entity ID ซึ่งหมายความว่าส่วนประกอบที่เกี่ยวข้องในแต่ละกลุ่มจะอยู่ในลำดับเดียวกัน
ซึ่งหมายความว่าถ้าคุณเป็นเส้นตรงทำซ้ำมากกว่ากราฟิกองค์ประกอบที่คุณยังเป็นเส้นตรงทำซ้ำมากกว่าที่สอดคล้องเปลี่ยนส่วนประกอบ คุณอาจจะข้ามการแปลงบางส่วน (เนื่องจากคุณอาจมีปริมาณทริกเกอร์ฟิสิกส์ที่คุณไม่ได้แสดงหรือดังกล่าว) แต่เนื่องจากคุณข้ามไปข้างหน้าในหน่วยความจำเสมอเพื่อให้ได้รับประสิทธิภาพ
นี่คล้ายกับวิธีที่คุณมีโครงสร้างของอาร์เรย์ (SOA) เป็นแนวทางที่แนะนำสำหรับ HPC ซีพียูและแคชสามารถจัดการกับอาเรย์เชิงเส้นหลายแบบได้เกือบจะดีพอ ๆ กับที่มันสามารถจัดการกับอาเรย์เชิงเส้นเดียวและดีกว่าที่มันสามารถจัดการกับการเข้าถึงหน่วยความจำแบบสุ่ม
กลยุทธ์อื่นที่ใช้ในการใช้งาน ECS บางอย่างรวมถึง Unity ECS คือการจัดสรรส่วนประกอบตาม Archetype ของเอนทิตีที่เกี่ยวข้อง นั่นคือทุกหน่วยงานที่มีความแม่นยำชุดของส่วนประกอบ (คนPhysicsBody
, Transform
) จะได้รับการจัดสรรแยกต่างหากจากหน่วยงานที่มีส่วนประกอบที่แตกต่างกัน (เช่นPhysicsBody
, Transform
, และ Renderable
)
ระบบในการออกแบบดังกล่าวทำงานโดยการค้นหาต้นแบบทั้งหมดที่ตรงกับความต้องการของพวกเขา (ที่มีชุดของส่วนประกอบที่จำเป็น) การวนซ้ำรายการของแม่แบบนั้นและวนซ้ำส่วนประกอบที่เก็บอยู่ภายใน Archetype ที่ตรงกันแต่ละชุด สิ่งนี้ทำให้การเข้าถึงองค์ประกอบ O (1) แบบเชิงเส้นและเป็นจริงอย่างสมบูรณ์ภายใน Archetype และอนุญาตให้ระบบค้นหาเอนทิตีที่เข้ากันได้กับค่าใช้จ่ายที่ต่ำมาก (โดยการค้นหารายการต้นแบบเล็ก ๆ
คุณสามารถมีพอยน์เตอร์เก็บส่วนประกอบไปยังส่วนประกอบอื่น ๆ หรือพอยน์เตอร์ไปยังเอนทิตีที่เก็บพอยน์เตอร์ไปยังส่วนประกอบ
ส่วนประกอบที่อ้างถึงส่วนประกอบอื่น ๆ ในเอนทิตีเดียวกันไม่จำเป็นต้องเก็บอะไรเลย ในการอ้างอิงองค์ประกอบบนเอนทิตีอื่น ๆ เพียงแค่เก็บ ID เอนทิตี
หากส่วนประกอบได้รับอนุญาตให้มีอยู่มากกว่าหนึ่งครั้งสำหรับเอนทิตีเดียวและคุณต้องการอ้างอิงอินสแตนซ์เฉพาะให้เก็บ ID ของเอนทิตีอื่นและดัชนีส่วนประกอบสำหรับเอนทิตีนั้น การปรับใช้ ECS จำนวนมากไม่อนุญาตให้ใช้กรณีนี้โดยเฉพาะเนื่องจากทำให้การดำเนินการเหล่านี้มีประสิทธิภาพน้อยลง
คุณสามารถมั่นใจได้ว่าทุกองค์ประกอบของอาเรย์นั้นมีค่า 'n' ขนาดใหญ่โดยที่ 'n' คือจำนวนเอนทิตีที่ยังมีชีวิตอยู่ในระบบ
ใช้จุดจับ (เช่นดัชนี + เครื่องหมายรุ่น) และไม่ใช่ตัวชี้จากนั้นคุณสามารถปรับขนาดอาร์เรย์ได้โดยไม่ต้องกลัวว่าจะทำลายการอ้างอิงวัตถุ
นอกจากนี้คุณยังสามารถใช้วิธี "อาร์เรย์ chunked" (อาร์เรย์ของอาร์เรย์) คล้ายกับstd::deque
การใช้งานทั่วไปจำนวนมาก(แม้ว่าจะไม่มีขนาดอันน่าสงสารขนาดเล็กของการใช้งานดังกล่าว) หากคุณต้องการอนุญาตให้พอยน์เตอร์ด้วยเหตุผลบางอย่างหรือถ้าคุณวัดปัญหาด้วย อาร์เรย์ปรับขนาดประสิทธิภาพ
ประการที่สองนี่คือทั้งหมดที่สมมติว่าเอนทิตีถูกประมวลผลเชิงเส้นในรายการทุกเฟรม / เห็บ แต่ในความเป็นจริงนี่ไม่ใช่กรณี
มันขึ้นอยู่กับเอนทิตี ใช่สำหรับหลายกรณีการใช้งานไม่เป็นความจริง ที่จริงนี่คือเหตุผลที่ฉันเน้นความแตกต่างอย่างมากระหว่างการออกแบบโดยใช้ส่วนประกอบ (ดี) และเอนทิตีระบบ (รูปแบบเฉพาะของ CBD)
องค์ประกอบบางอย่างของคุณจะง่ายต่อการประมวลผลเชิงเส้น แม้ในกรณีการใช้งาน "ต้นไม้หนัก" โดยปกติเราได้เห็นประสิทธิภาพเพิ่มขึ้นอย่างแน่นอนจากการใช้อาร์เรย์ที่บรรจุแน่น
นักพัฒนาบางคนยังพบว่าข้อได้เปรียบด้านประสิทธิภาพของการใช้โครงสร้างข้อมูลที่มีการกระจายข้อมูลเชิงเส้นมีมากกว่าข้อได้เปรียบด้านประสิทธิภาพของการใช้โครงสร้างแบบทรี "ฉลาดขึ้น" ทุกอย่างขึ้นอยู่กับเกมและกรณีการใช้งานที่เฉพาะเจาะจงแน่นอน
สมมติว่าคุณใช้เซกเตอร์ / พอร์ทัลเรนเดอร์หรืออ็อคทรีเพื่อทำการคัดแยก คุณอาจเก็บเอนทิตีต่อเนื่องกันภายในเซกเตอร์ / โหนด แต่คุณกำลังจะกระโดดไปรอบ ๆ ไม่ว่าคุณจะชอบหรือไม่
คุณจะแปลกใจว่าอาร์เรย์ยังคงช่วยคุณได้มากแค่ไหน คุณกำลังกระโดดไปรอบ ๆ ในพื้นที่หน่วยความจำขนาดเล็กกว่า "ที่ใดก็ได้" และแม้แต่กับการกระโดดทั้งหมดที่คุณยังมีแนวโน้มที่จะจบลงด้วยการแคช ด้วยต้นไม้ที่มีขนาดเล็กกว่าหรือน้อยกว่าคุณอาจจะสามารถดึงข้อมูลทั้งหมดลงในแคชและไม่เคยมีแคชที่พลาดบนต้นไม้ต้นนั้น
นอกจากนี้ยังมีโครงสร้างต้นไม้ที่สร้างขึ้นเพื่อให้อยู่ในอาร์เรย์ที่แน่นหนา ตัวอย่างเช่นด้วย octree ของคุณคุณสามารถใช้โครงสร้างคล้ายกอง (ผู้ปกครองก่อนเด็กพี่น้องซึ่งอยู่ติดกัน) และตรวจสอบให้แน่ใจว่าแม้ว่าคุณ "เจาะลึก" ต้นไม้คุณจะวนไปข้างหน้าเสมอในอาเรย์ CPU ปรับการเข้าถึง / การค้นหาแคชของหน่วยความจำให้เหมาะสม
ซึ่งเป็นจุดสำคัญที่จะทำให้ x86 CPU เป็นสัตว์ร้ายที่ซับซ้อน ซีพียูกำลังเรียกใช้โปรแกรมเพิ่มประสิทธิภาพไมโครโค้ดบนรหัสเครื่องของคุณแบ่งเป็นไมโครโค้ดขนาดเล็กและคำสั่งการเรียงลำดับใหม่การทำนายรูปแบบการเข้าถึงหน่วยความจำ ฯลฯ รูปแบบการเข้าถึงข้อมูลมีความสำคัญมากกว่าที่จะปรากฏชัดเจนหากคุณมีความเข้าใจระดับสูง CPU หรือแคชทำงานอย่างไร
จากนั้นคุณมีระบบอื่น ๆ ซึ่งอาจต้องการเอนทิตีที่จัดเก็บตามลำดับอื่น
คุณสามารถจัดเก็บได้หลายครั้ง เมื่อคุณตัดอาร์เรย์ของคุณไปที่รายละเอียดขั้นต่ำที่เปลือยเปล่าคุณอาจพบว่าคุณประหยัดหน่วยความจำจริง ๆ(เนื่องจากคุณได้ลบตัวชี้ 64- บิตและสามารถใช้ดัชนีขนาดเล็กลง) ด้วยวิธีนี้
คุณสามารถแทรกเอนทิตี้ของอาร์เรย์แทนการเก็บอาร์เรย์แยก แต่คุณยังคงสูญเสียความจำ
นี่คือสิ่งที่ตรงกันข้ามกับการใช้แคชที่ดี หากสิ่งที่คุณใส่ใจคือการแปลงและข้อมูลกราฟิกเหตุใดจึงทำให้เครื่องใช้เวลาในการดึงข้อมูลอื่น ๆ ทั้งหมดสำหรับฟิสิกส์และ AI และอินพุตและดีบักและอื่น ๆ
นั่นคือจุดที่มักจะให้ความสนใจกับวัตถุเกม ECS vs เสาหิน (แม้ว่าจะไม่สามารถใช้ได้จริง ๆ เมื่อเปรียบเทียบกับสถาปัตยกรรมที่อิงองค์ประกอบอื่น ๆ )
สำหรับสิ่งที่คุ้มค่าการใช้งาน ECS "ระดับการผลิต" ส่วนใหญ่ซึ่งฉันทราบว่าใช้หน่วยเก็บข้อมูลแบบอินเตอร์เล็ด วิธี Archetype ที่เป็นที่นิยมที่ฉันพูดถึงก่อนหน้านี้ (ตัวอย่างเช่นใช้ใน Unity ECS) เป็นโครงสร้างที่สร้างขึ้นอย่างชัดเจนเพื่อใช้หน่วยเก็บข้อมูล interleaved สำหรับคอมโพเนนต์ที่เกี่ยวข้องกับ Archetype
AI นั้นไม่มีจุดหมายหากไม่สามารถส่งผลกระทบต่อสถานะการแปลงสภาพหรือภาพเคลื่อนไหวที่ใช้สำหรับการแสดงผลของเอนทิตี
เนื่องจาก AI ไม่สามารถเข้าถึงการแปลงข้อมูลเชิงเส้นได้อย่างมีประสิทธิภาพไม่ได้หมายความว่าไม่มีระบบอื่นใดที่สามารถใช้การเพิ่มประสิทธิภาพการจัดวางข้อมูลได้อย่างมีประสิทธิภาพ คุณสามารถใช้อาร์เรย์ที่อัดแน่นสำหรับการแปลงข้อมูลโดยไม่หยุดระบบเกมตรรกะจากการทำสิ่งต่าง ๆ ตามปกติระบบเกมตรรกะมักจะทำสิ่งต่าง ๆ
นอกจากนี้คุณยังลืมแคชรหัส เมื่อคุณใช้วิธีการของระบบของ ECS (ซึ่งแตกต่างจากสถาปัตยกรรมของคอมโพเนนต์ที่ไร้เดียงสามากกว่า) คุณรับประกันได้ว่าคุณกำลังรันโค้ดขนาดเล็กที่เหมือนกันและไม่กระโดดข้ามไปมาผ่านตารางฟังก์ชันเสมือนไปยังUpdate
ฟังก์ชันสุ่มที่กระจายอยู่ทั่ว ไบนารีของคุณ ดังนั้นในกรณีของ AI คุณต้องการเก็บส่วนประกอบ AI ที่แตกต่างกันทั้งหมดของคุณ (เพราะแน่นอนว่าคุณมีมากกว่าหนึ่งรายการเพื่อให้คุณสามารถเขียนพฤติกรรม!) ในที่เก็บข้อมูลแยกต่างหากและประมวลผลแต่ละรายการแยกต่างหากเพื่อให้ได้รหัสแคชที่ดีที่สุด
ด้วยคิวเหตุการณ์ที่ล่าช้า (ที่ระบบสร้างรายการของเหตุการณ์ แต่ไม่ส่งไปจนกว่าระบบจะเสร็จสิ้นการประมวลผลเอนทิตีทั้งหมด) คุณสามารถมั่นใจได้ว่าโค้ดแคชของคุณถูกใช้อย่างดีในขณะที่เก็บเหตุการณ์
การใช้วิธีการที่แต่ละระบบรู้ว่าคิวเหตุการณ์ที่จะอ่านจากเฟรมคุณสามารถทำให้เหตุการณ์การอ่านเร็วขึ้น หรือเร็วกว่าโดยไม่มีอย่างน้อย
จำไว้ว่าการแสดงนั้นไม่สมบูรณ์ คุณไม่จำเป็นต้องกำจัดแคชตัวสุดท้ายทุกตัวเพื่อเริ่มเห็นประโยชน์ด้านประสิทธิภาพของการออกแบบเชิงข้อมูลที่ดี
ยังคงมีการวิจัยอย่างแข็งขันในการทำให้ระบบเกมจำนวนมากทำงานได้ดีขึ้นกับสถาปัตยกรรม ECS และรูปแบบการออกแบบที่เน้นข้อมูล คล้ายกับบางสิ่งที่น่าอัศจรรย์ที่เราเคยทำกับ SIMD ในไม่กี่ปีที่ผ่านมา (เช่น JSON parsers) เรากำลังเห็นสิ่งต่าง ๆ ที่ทำด้วยสถาปัตยกรรม ECS ที่ดูเหมือนจะไม่ง่ายสำหรับสถาปัตยกรรมเกมคลาสสิก แต่มีจำนวน ประโยชน์ที่ได้รับ (ความเร็วมัลติเธรดการทดสอบ ฯลฯ )
หรืออาจมีวิธีไฮบริดที่ทุกคนใช้ แต่ไม่มีใครพูดถึง
นี่คือสิ่งที่ฉันได้สนับสนุนในอดีตโดยเฉพาะอย่างยิ่งสำหรับผู้ที่มีความสงสัยเกี่ยวกับสถาปัตยกรรมของ ECS: ใช้วิธีการที่มุ่งเน้นข้อมูลที่ดีไปยังส่วนประกอบที่ประสิทธิภาพมีความสำคัญ ใช้สถาปัตยกรรมที่เรียบง่ายกว่าซึ่งความเรียบง่ายจะช่วยปรับปรุงเวลาในการพัฒนา อย่าสวมแตรรองเท้าทุกชิ้นส่วนเป็นคำจำกัดความที่เข้มงวดขององค์ประกอบเช่น ECS พัฒนาสถาปัตยกรรมองค์ประกอบของคุณในแบบที่คุณสามารถใช้วิธีการที่คล้ายกับ ECS ที่พวกเขาเข้าใจได้ง่ายและใช้โครงสร้างส่วนประกอบที่เรียบง่ายกว่าซึ่งวิธีที่คล้ายกันกับ ECS ไม่สมเหตุสมผล (หรือใช้ความรู้สึกน้อยกว่าโครงสร้างแบบต้นไม้เป็นต้น) .
โดยส่วนตัวแล้วฉันเป็นผู้เปลี่ยนมาใช้พลังที่แท้จริงของ ECS เอง แม้ว่าสำหรับฉันแล้วปัจจัยในการตัดสินใจเป็นสิ่งที่ไม่ค่อยได้พูดถึงเกี่ยวกับ ECS: มันทำให้การทดสอบการเขียนสำหรับระบบเกมและตรรกะนั้นแทบจะไม่สำคัญเมื่อเทียบกับการออกแบบที่ใช้องค์ประกอบที่ลอจิกที่ยึดแน่น เนื่องจากสถาปัตยกรรมของ ECS วางตรรกะทั้งหมดในระบบซึ่งเพิ่งใช้ส่วนประกอบและผลิตการอัพเดทส่วนประกอบการสร้างชุด "จำลอง" ของส่วนประกอบเพื่อทดสอบพฤติกรรมของระบบนั้นค่อนข้างง่าย เนื่องจากตรรกะของเกมส่วนใหญ่ควรอยู่ในระบบ แต่เพียงผู้เดียวนั่นหมายความว่าการทดสอบระบบทั้งหมดของคุณจะให้รหัสที่ค่อนข้างครอบคลุมของตรรกะเกมของคุณ ระบบสามารถใช้การอ้างอิงแบบจำลอง (เช่นอินเทอร์เฟซ GPU) สำหรับการทดสอบที่มีความซับซ้อนน้อยกว่าหรือมีผลกระทบต่อประสิทธิภาพมากกว่าคุณ
นอกจากนี้คุณอาจสังเกตว่าผู้คนจำนวนมากพูดคุยเกี่ยวกับ ECS โดยไม่เข้าใจว่ามันคืออะไร ฉันเห็น Unity แบบคลาสสิคที่เรียกว่า ECS ด้วยความถี่ที่ตกต่ำซึ่งแสดงให้เห็นว่าผู้พัฒนาเกมจำนวนมากเกินไปถือเอา "ECS" กับ "คอมโพเนนต์" และไม่สนใจส่วน คุณเห็นความรักมากมายบน ECS บนอินเทอร์เน็ตเมื่อคนส่วนใหญ่สนับสนุนการออกแบบที่ใช้ส่วนประกอบไม่ใช่ ECS จริง ณ จุดนี้มันเกือบไม่มีจุดหมายที่จะโต้แย้งมัน; ECS เสียหายจากความหมายดั้งเดิมเป็นคำทั่วไปและคุณอาจยอมรับว่า "ECS" ไม่ได้หมายถึงสิ่งเดียวกันกับ "ECS เชิงข้อมูล" : /