Entity-Component System แย่มากสำหรับการแยกส่วน / ซ่อนข้อมูลหรือไม่


11

ชื่อนี้มีความหมายเกินความจริงและมันอาจเป็นความไม่ชำนาญของฉันกับรูปแบบ แต่นี่คือเหตุผลของฉัน:

"ปกติ" หรือเนื้อหาที่ตรงไปตรงมาของการใช้งานเอนทิตีคือการใช้พวกเขาเป็นวัตถุและ subclassing พฤติกรรมที่พบบ่อย สิ่งนี้นำไปสู่ปัญหาแบบคลาสสิกของ "is EvilTreesubclass of Treeor Enemy?" หากเรายอมให้มีการสืบทอดหลายครั้งปัญหาเพชรจะเกิดขึ้น เราแทนสามารถดึงการทำงานรวมกันTreeและEnemyขึ้นไปลำดับชั้นซึ่งนำไปสู่การเรียนพระเจ้าหรือเราจงใจสามารถออกจากพฤติกรรมของเราTreeและEntityการเรียน (ทำให้พวกเขาเชื่อมต่อในกรณีที่รุนแรง) เพื่อให้EvilTreeสามารถดำเนินการที่ตัวเอง - ซึ่งจะนำไปสู่ SomewhatEvilTreeการทำสำเนารหัสถ้าเราเคยมี

ระบบ Entity-Component พยายามที่จะแก้ปัญหานี้โดยการหารTreeและEnemyวัตถุเป็นส่วนประกอบที่แตกต่างกัน - พูดPosition, HealthและAI- และดำเนินการระบบเช่นAISystemที่มีการเปลี่ยนแปลงตำแหน่งของ Entitiy ตามการตัดสินใจของเอไอ จนถึงดีมาก แต่จะเกิดอะไรขึ้นหากEvilTreeสามารถหยิบพลังและจัดการความเสียหายได้? ก่อนอื่นเราต้องการ a CollisionSystemและ a DamageSystem(เราอาจมีสิ่งเหล่านี้อยู่แล้ว) CollisionSystemความต้องการในการสื่อสารกับDamageSystemทุกครั้งที่สองสิ่งชนCollisionSystemส่งข้อความถึงDamageSystemสุขภาพเพื่อที่จะสามารถลบ ความเสียหายยังได้รับอิทธิพลจากการเติมพลังดังนั้นเราต้องเก็บมันไว้ที่ไหนซักแห่ง เราสร้างใหม่PowerupComponentที่เราแนบกับหน่วยงาน? แต่แล้วDamageSystemจำเป็นต้องรู้เกี่ยวกับบางสิ่งบางอย่างมันค่อนข้างจะไม่รู้อะไรเลย - นอกจากนี้ยังมีบางสิ่งที่สร้างความเสียหายที่ไม่สามารถรับพลังได้ (เช่นกSpike) เราอนุญาตให้มีPowerupSystemการแก้ไข a StatComponentที่ใช้สำหรับการคำนวณความเสียหายที่คล้ายกับคำตอบนี้หรือไม่? แต่ตอนนี้ทั้งสองระบบเข้าถึงข้อมูลเดียวกัน เมื่อเกมของเรามีความซับซ้อนมากขึ้นมันจะกลายเป็นกราฟพึ่งพาไม่มีตัวตนที่องค์ประกอบร่วมกันระหว่างระบบต่างๆ ณ จุดนี้เราสามารถใช้ตัวแปรคงที่ทั่วโลกและกำจัดแผ่นเหล็กทั้งหมด

มีวิธีที่มีประสิทธิภาพในการแก้ปัญหานี้หรือไม่? หนึ่งความคิดที่ฉันมีคือการปล่อยให้องค์ประกอบมีฟังก์ชั่นบางอย่างเช่นให้สิ่งStatComponent attack()ที่เพิ่งจะส่งกลับจำนวนเต็มโดยค่าเริ่มต้น แต่สามารถประกอบเมื่อมีการเพิ่มพลัง:

attack = getAttack compose powerupBy(20) compose powerdownBy(40)

สิ่งนี้ไม่ได้แก้ปัญหาที่attackต้องบันทึกไว้ในองค์ประกอบที่เข้าถึงได้โดยหลายระบบ แต่อย่างน้อยฉันก็สามารถพิมพ์ฟังก์ชั่นได้อย่างถูกต้องถ้าฉันมีภาษาที่รองรับอย่างเพียงพอ:

// In StatComponent
type Strength = PrePowerup | PostPowerup
type Damage = Int
type PrePowerup = Int
type PostPowerup = Int
attack: Strength = getAttack //default value, can be changed by systems
getAttack: PrePowerup

// these functions can be defined in other components or in PowerupSystems
powerupBy: Strength -> PostPowerup
powerdownBy: Strength -> PostPowerup
subtractArmor: Strength -> Damage

// in DamageSystem
dealDamage: Damage -> () = attack compose subtractArmor compose hurtSomeEntity

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

มีวิธีแก้ปัญหานี้หรือไม่? มีฟังก์ชั่น / รูปแบบที่ฉันพลาดในการแยก ECS ออกอย่างหมดจดมากขึ้นหรือไม่ FRP เหมาะกว่าสำหรับปัญหานี้หรือไม่? ปัญหาเหล่านี้เกิดจากความซับซ้อนของสิ่งที่ฉันพยายามเขียนโปรแกรมหรือไม่ เช่น FRP จะมีปัญหาที่คล้ายกัน



ฉันคิดถึงบล็อกของ Eric จริงๆ (ตั้งแต่เมื่อประมาณ C #)
OldFart

คำตอบ:


21

ECS ทำลายข้อมูลอย่างสมบูรณ์ซึ่งซ่อนตัวอยู่ นี่คือการแลกเปลี่ยนของรูปแบบ

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

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

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


7

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

สิ่งที่คุณต้องการสำหรับการนี้เป็นชัดแจ้งการอ้างอิง แต่ละระบบต้องมีความชัดเจนเกี่ยวกับข้อมูลที่จะอ่านและข้อมูลที่จะเขียน เมื่อระบบต้องการที่จะดึงข้อมูลส่วนหนึ่งก็จะต้องสามารถที่จะทำเช่นนี้อย่างชัดเจนเท่านั้น ในรูปแบบที่ง่ายที่สุดมันมีองค์ประกอบสำหรับแต่ละประเภทที่ต้องการ (เช่น RenderSystem ต้องการ RenderComponents และ PositionComponents) เป็นอาร์กิวเมนต์ของมันและคืนสิ่งที่มีการเปลี่ยนแปลง (เช่น RenderComponents เท่านั้น)

ด้วยวิธีนี้ฉันอย่างน้อยรับประกันการสั่งซื้อที่ถูกต้องของฟังก์ชั่นต่างๆที่เพิ่มโดยระบบ

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

FRP เหมาะกว่าสำหรับปัญหานี้หรือไม่? ปัญหาเหล่านี้เกิดจากความซับซ้อนของสิ่งที่ฉันพยายามเขียนโปรแกรมหรือไม่ เช่น FRP จะมีปัญหาที่คล้ายกัน

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

FRP จะไม่แก้ปัญหาการต้องใช้ข้อมูลที่คุณต้องการเพื่อดำเนินการบางอย่าง

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