เหตุใดจึงเป็นความคิดที่ไม่ดีในการจัดเก็บวิธีการในเอนทิตีและส่วนประกอบ (พร้อมกับคำถามระบบ Entity อื่น ๆ )


16

นี่คือการติดตามคำถามนี้ซึ่งฉันตอบไป แต่สิ่งนี้จะจัดการกับหัวข้อที่เฉพาะเจาะจงมากขึ้น

คำตอบนี้ช่วยให้ฉันเข้าใจระบบ Entity ได้ดีกว่าบทความ

ฉันได้อ่าน(ใช่แล้ว) บทความเกี่ยวกับระบบ Entity และมันบอกฉันต่อไปนี้:

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

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

ชั้น Vector2D เก็บข้อมูล: พิกัด x และ y แต่ก็มีวิธีการซึ่งมีความสำคัญต่อความมีประโยชน์และแยกความแตกต่างของชั้นเรียนจากเพียงแค่องค์ประกอบสองแถว วิธีการตัวอย่างเช่น: add(), rotate(point, r, angle), substract(), normalize()และมาตรฐานอื่น ๆ ที่มีประโยชน์และจำเป็นอย่างยิ่งวิธีการที่ตำแหน่ง (ซึ่งเป็นกรณีของชั้น Vector2D) ควรจะมี

หากองค์ประกอบนั้นเป็นเพียงตัวยึดข้อมูลมันจะไม่สามารถมีวิธีการเหล่านี้ได้!

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

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

คิดแบบนี้ มีวัตถุที่สามารถวาดได้หลายประเภทในเกม ภาพธรรมดาเก่า, ภาพเคลื่อนไหว ( update(), getCurrentFrame()ฯลฯ ), การรวมกันของชนิดดั้งเดิมเหล่านี้และทั้งหมดของพวกเขาก็สามารถให้draw()วิธีการเพื่อให้ระบบแสดงผลที่แล้วไม่จำเป็นต้องดูแลเกี่ยวกับวิธีเทพดาของกิจการจะดำเนินการเท่านั้น เกี่ยวกับส่วนต่อประสาน (วาด) และตำแหน่ง จากนั้นฉันก็แค่ต้องการระบบภาพเคลื่อนไหวที่จะเรียกวิธีการเฉพาะภาพเคลื่อนไหวที่ไม่มีอะไรเกี่ยวข้องกับการแสดงผล

และอีกสิ่งหนึ่ง ... มีทางเลือกอื่นสำหรับอาร์เรย์เมื่อพูดถึงการจัดเก็บส่วนประกอบหรือไม่ ฉันไม่เห็นที่อื่นที่จะเก็บส่วนประกอบอื่นนอกเหนือจากอาร์เรย์ในคลาส Entity ...

บางทีนี่อาจเป็นวิธีที่ดีกว่า: จัดเก็บองค์ประกอบเป็นคุณสมบัติอย่างง่ายของเอนทิตี entity.positionยกตัวอย่างเช่นเป็นส่วนประกอบตำแหน่งจะได้รับการติดกาวที่

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


คุณกำลังทำการแก้ไขจำนวนมากเพื่อรับตราสัญลักษณ์อื่นหรือไม่? เพราะนั่นคือซนซนมันยังคงชนหัวข้อโบราณมากมาย
jhocking

คำตอบ:


25

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

แก้ไข

ชี้แจงเป้าหมายของคุณกำลังไม่ได้ที่จะหลีกเลี่ยงการOOP นั่นอาจเป็นเรื่องยากในภาษาทั่วไปส่วนใหญ่ที่ใช้ในปัจจุบัน คุณกำลังพยายามลดการสืบทอดให้น้อยที่สุดซึ่งเป็นส่วนใหญ่ของ OOP แต่ไม่จำเป็น คุณต้องการกำจัดวัตถุ -> MobileObject-> Creature-> Bipedal-> การสืบทอดประเภทบุคคล

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

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

เอนทิตีและส่วนประกอบจะถูกเก็บไว้ในผู้จัดการแยกต่างหาก

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


1
อืมมันช่วยให้สถานการณ์ง่ายขึ้นมากขอบคุณ! ฉันคิดว่ามีเวทมนต์บางอย่าง "คุณจะต้องเสียใจในภายหลัง" เกิดอะไรขึ้นและฉันก็ไม่เห็นเลย!
jcora

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

2
ฉันได้เรียนรู้จากคำตอบสุดท้ายของฉันในเรื่องนี้ Disclaimer: ฉันไม่ได้พูดนี้เป็นวิธีที่จะทำมัน :)
MichaelHouse

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

1
ดี! นั่นจะเป็นเครื่องยนต์ที่น่ารักเมื่อทำเสร็จ ขอให้โชคดีกับมัน! ขอบคุณที่ถามคำถามที่น่าสนใจ
MichaelHouse

10

'นั่น' ไม่ใช่บทความที่ฉันเห็นด้วยอย่างยิ่งดังนั้นคำตอบของฉันจะค่อนข้างสำคัญฉันคิดว่า

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

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

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

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

ดี. วิธีการเป็นวิธีที่มีประสิทธิภาพในการจัดการรหัสของคุณ สิ่งสำคัญที่จะนำออกไปจากแนวคิด "หลีกเลี่ยง OOP" คือการหลีกเลี่ยงการใช้การสืบทอดทุกหนทุกแห่งเพื่อขยายฟังก์ชันการทำงาน ให้แบ่งการทำงานเป็นส่วนประกอบที่สามารถรวมกันเพื่อทำสิ่งเดียวกัน

คิดแบบนี้ มีวัตถุที่สามารถวาดได้หลายประเภทในเกม ภาพเก่าธรรมดาภาพเคลื่อนไหว (อัปเดต (), getCurrentFrame () ฯลฯ ), การรวมกันของประเภทดั้งเดิมเหล่านี้และทั้งหมดของพวกเขาสามารถให้วิธีการเสมอ () เพื่อระบบการเรนเดอร์ [... ]

แนวคิดของระบบที่ใช้องค์ประกอบคือคุณจะไม่มีคลาสแยกสำหรับสิ่งเหล่านี้ แต่จะมีคลาส Object / Entity เดียวและรูปภาพจะเป็น Object / Entity ที่มี ImageRenderer การเคลื่อนไหวจะเป็น Object / เอนทิตีที่มี AnimationRenderer ฯลฯ ระบบที่เกี่ยวข้องจะรู้วิธีแสดงองค์ประกอบเหล่านี้และดังนั้นจึงไม่จำเป็นต้องเป็นคลาสพื้นฐานใด ๆ ด้วยวิธีการ Draw ()

[... ] ซึ่งไม่จำเป็นต้องสนใจว่า sprite ของเอนทิตีถูกนำไปใช้งานเพียงเกี่ยวกับอินเตอร์เฟส (วาด) และตำแหน่ง จากนั้นฉันก็แค่ต้องการระบบภาพเคลื่อนไหวที่จะเรียกวิธีการเฉพาะภาพเคลื่อนไหวที่ไม่มีอะไรเกี่ยวข้องกับการแสดงผล

แน่นอน แต่วิธีนี้ใช้ไม่ได้กับส่วนประกอบต่างๆ คุณมี 3 ตัวเลือก:

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

และอีกสิ่งหนึ่ง ... มีทางเลือกอื่นสำหรับอาร์เรย์เมื่อพูดถึงการจัดเก็บส่วนประกอบหรือไม่ ฉันไม่เห็นที่อื่นที่จะเก็บส่วนประกอบอื่นนอกเหนือจากอาร์เรย์ในคลาส Entity ...

คุณสามารถจัดเก็บส่วนประกอบในระบบ อาร์เรย์ไม่ใช่ปัญหา แต่คุณเก็บส่วนประกอบไว้ที่ใด


+1 ขอบคุณสำหรับมุมมองอื่น มันเป็นการดีที่จะได้รับไม่กี่เมื่อจัดการกับเรื่องที่ไม่ชัดเจนเช่น! หากคุณกำลังจัดเก็บส่วนประกอบในระบบนั่นหมายความว่าส่วนประกอบสามารถแก้ไขได้ด้วยระบบเดียวเท่านั้น ตัวอย่างเช่นระบบการวาดภาพและระบบการเคลื่อนไหวจะเข้าถึงส่วนประกอบตำแหน่งได้ คุณเก็บไว้ที่ไหน
MichaelHouse

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

ฉันถูกต้อง @Kylotan หรือไม่ นั่นเป็นวิธีที่ผมจะทำมันดูเหมือนว่าตรรกะ ...
jcora

ในตัวอย่างของ Adam / T-Machine ความตั้งใจคือมี 1 ระบบต่อองค์ประกอบ แต่ระบบสามารถเข้าถึงและแก้ไขส่วนประกอบอื่น ๆ ได้อย่างแน่นอน (นี่เป็นอุปสรรคต่อการใช้ประโยชน์หลายเธรดของส่วนประกอบ แต่นั่นเป็นเรื่องที่แตกต่างกัน)
Kylotan

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

2

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


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