สิ่งที่ฉันพลาดใน Unity3D ECS เมื่อเปรียบเทียบกับ "Entitas"

ฉันใช้เวลา (สองสามชั่วโมงสองสามวัน) เพื่อดูการใช้งาน ECS ใหม่และทันสมัยใน Unity3D ฉันต้องบอกว่าฉันชอบสิ่งที่ฉันเห็น ฉันเข้าใจอย่างถ่องแท้ว่าฉันอาจจะพลาดบางสิ่งบางอย่างไป หรือว่ามันเร็วเกินไปและสิ่งต่าง ๆ กำลังจะเกิดขึ้น แต่เมื่อมองจากสถานะปัจจุบันฉันมี (ครึ่ง) สิ่งที่ฉันพลาดไปเมื่อเปรียบเทียบ ถึง "เอนทิทัส"

ขั้นแรกให้เริ่มต้นด้วยคุณสมบัติเล็กๆ น้อยๆ:

องค์ประกอบที่เป็นเอกลักษณ์

เมื่อทำงานกับ ECS เรามักจะคิดเป็นคอลเลคชันหรือเป็นกลุ่มหากคุณต้องการ

ให้ “สิ่งของ” ทั้งหมดที่มีตำแหน่งแก่ฉัน (หรือแค่ให้ทุกตำแหน่งแก่ฉัน) ขอประทานทุกสิ่งที่มีตำแหน่งและความเร็วแก่ข้าพเจ้า

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

ตามความเป็นจริง PlayerInput ใน "โปรเจ็กต์ตัวอย่างเกมยิงสองแท่ง" เป็นตัวเลือกที่ดีที่จะเป็นองค์ประกอบที่มีเอกลักษณ์ เกมดังกล่าวไม่มีองค์ประกอบผู้เล่นหลายคนในท้องถิ่น ดังนั้นจึงปลอดภัยที่จะบอกว่าควรมีเพียงเอนทิตีเดียวที่มีองค์ประกอบ PlayerInput ในการใช้งานปัจจุบัน เราเห็นได้ใน UpdatePlayerHud และ EnemyShootSystem ว่ามีความเป็นเอกลักษณ์โดยนัย แต่ไม่ได้บังคับ และไม่ได้ให้ความเรียบง่ายและประโยชน์ที่เราจะได้รับจากการกำหนดส่วนประกอบให้มีเอกลักษณ์

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

ตอนนี้เรามาพูดถึงคุณลักษณะที่สองที่ซับซ้อนมากขึ้น:

พฤติกรรมปฏิกิริยา

คุณลักษณะนี้มีความหมายเชิงลึก แต่แรงจูงใจเริ่มแรกนั้นค่อนข้างเรียบง่าย เราต้องการหลีกเลี่ยงการมีส่วนประกอบของแท็กจำนวนมาก

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

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

ความยุ่งยากทั้งหมดนี้สามารถลดความซับซ้อนลงได้ เมื่อเราแนะนำแนวคิดของระบบปฏิกิริยา

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

การแนะนำแนวคิดของระบบปฏิกิริยา ซึ่งเปลี่ยนวิธีการอย่างมาก เราได้นำ UI และระบบการเล่นเกมมาใช้

อย่างไรก็ตาม มีอีกด้านหนึ่งของพฤติกรรมปฏิกิริยาซึ่งเป็นผลข้างเคียงที่น่าสนใจ โดยพิจารณาจากรายละเอียดการใช้งานระบบปฏิกิริยา

วิธีที่เรานำระบบปฏิกิริยาไปใช้คือการทำให้บริบท เอนทิตี และกลุ่มสามารถสังเกตได้ ระบบรีแอกทีฟทุกระบบจะมีตัวสะสมซึ่งจะถูกระบายออกเมื่อระบบรีแอกทีฟถูกดำเนินการ รายละเอียดสามารถพบได้ในหนังสือของฉัน "EntitasCookBook" ผลข้างเคียงที่น่าสนใจที่ผมได้กล่าวไปแล้วคือ:

หากทุกอย่างสังเกตได้ เราก็สามารถนำการจัดทำดัชนีและการบันทึกไปใช้ได้อย่างง่ายดาย

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

ตามความเป็นจริง ใน "ตัวอย่าง boids" มีงาน HashPositions ซึ่งสร้างโครงสร้างข้อมูลที่คล้ายคลึงกัน อย่างไรก็ตาม เท่าที่ฉันเข้าใจ มันถูกสร้างขึ้นในแต่ละเฟรมตั้งแต่เริ่มต้น โดยที่ดัชนีสามารถอัปเดตแบบค่อยเป็นค่อยไป

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

คุณลักษณะสุดท้ายที่ฉันพูดถึง ครึ่ง คือ AnyOf matcher ฉันเรียกมันว่า ครึ่งหนึ่ง ของฟีเจอร์ เพราะว่า IMHO เป็นสิ่งที่ดีที่มี

ใน Entitas เราได้รับกลุ่มของเอนทิตีโดยการกำหนดสิ่งที่เรียกว่า matcher อีกครั้ง ฉันจะอ้างถึงบท "กลุ่ม" ของ EntitasCookBook หากคุณสนใจในรายละเอียด อย่างไรก็ตาม แนวคิดนี้มีพื้นฐานมาจากคุณลักษณะที่คล้ายกันใน "Artemis ECS" ในอาร์เทมิส เรียกว่า Aspect

ด้วย AnyOf เราสามารถขอเอนทิตีซึ่งมีองค์ประกอบรายการใดรายการหนึ่งได้ เมื่อระบบขอเอนทิตีที่มีตัวจับคู่ ส่วนใหญ่จะหมายความว่าส่วนประกอบเหล่านั้นไม่จำเป็นสำหรับการแปลงข้อมูล แต่เพื่อการจำแนกประเภทเอนทิตีที่เหมาะสม คล้ายกับแนวคิดเบื้องหลัง SubtractiveComponent ใน Unity ECS

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

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

และเช่นเคย ฉันยินดีที่จะพูดคุยทุกเรื่องเกี่ยวกับ ECS ดังนั้นเขียนความคิดเห็นหรือติดต่อเราทาง Twitter ได้เลย