ฉันกำลังใช้ชุดตัวเลือกของระบบเอนทิตีที่มี:
ระดับ Entityที่มีน้อยกว่าประชาชนที่ผูกส่วนประกอบเข้าด้วยกัน
กลุ่มของคลาสคอมโพเนนต์ที่ไม่มี "ตรรกะของคอมโพเนนต์" ซึ่งเป็นข้อมูลเท่านั้น
กลุ่มของคลาสระบบ (aka "ระบบย่อย", "ผู้จัดการ") สิ่งเหล่านี้ทำการประมวลผลตรรกะเอนทิตีทั้งหมด ในกรณีพื้นฐานส่วนใหญ่ระบบจะทำซ้ำรายการของเอนทิตีที่พวกเขาสนใจและดำเนินการกับแต่ละรายการ
วัตถุชั้น MessageChannelที่ใช้ร่วมกันโดยระบบเกมทั้งหมด แต่ละระบบสามารถสมัครสมาชิกเพื่อรับฟังข้อความบางประเภทและยังสามารถใช้ช่องทางในการส่งข้อความไปยังระบบอื่น ๆ
ตัวแปรเริ่มต้นของการจัดการข้อความระบบมีดังนี้:
- เรียกใช้การอัปเดตในแต่ละระบบเกมตามลำดับ
หากระบบทำบางสิ่งบางอย่างกับองค์ประกอบและการกระทำนั้นอาจเป็นที่สนใจของระบบอื่น ๆ ระบบจะส่งข้อความที่เหมาะสม (ตัวอย่างเช่นการเรียกระบบ
messageChannel.Broadcast(new EntityMovedMessage(entity, oldPosition, newPosition))
เมื่อใดก็ตามที่มีการย้ายเอนทิตี)
แต่ละระบบที่สมัครรับข้อความที่ระบุจะได้รับวิธีการจัดการข้อความ
หากระบบกำลังจัดการเหตุการณ์และตรรกะการประมวลผลเหตุการณ์ต้องการข้อความอื่นที่จะเผยแพร่ข้อความจะได้รับการถ่ายทอดทันทีและวิธีการประมวลผลข้อความอีกสายหนึ่งถูกเรียก
ตัวแปรนี้ก็โอเคจนกระทั่งฉันเริ่มปรับระบบตรวจจับการชนกันให้เหมาะสม (มันช้าลงจริงๆเมื่อจำนวนของเอนทิตีเพิ่มขึ้น) ในตอนแรกมันแค่วนซ้ำเอนทิตีแต่ละคู่โดยใช้อัลกอริทึมแรงเดรัจฉานอย่างง่าย จากนั้นฉันก็เพิ่ม "ดัชนีเชิงพื้นที่" ที่มีตารางของเซลล์ที่เก็บเอนทิตีที่อยู่ภายในพื้นที่ของเซลล์ที่เฉพาะเจาะจงจึงอนุญาตให้ทำการตรวจสอบเฉพาะเอนทิตีในเซลล์ข้างเคียง
ทุกครั้งที่เอนทิตีย้ายระบบการชนจะตรวจสอบว่าเอนทิตีชนกับบางสิ่งในตำแหน่งใหม่หรือไม่ หากเป็นเช่นนั้นการชนจะได้รับการตรวจพบ และถ้าทั้งสองหน่วยงานชนกันเป็น "วัตถุทางกายภาพ" (ทั้งคู่มีองค์ประกอบ RigidBody และตั้งใจที่จะผลักกันออกไปเพื่อไม่ให้ครอบครองพื้นที่เดียวกัน) ระบบแยกตัวแข็งโดยเฉพาะขอให้ระบบการเคลื่อนไหวย้ายหน่วยงานไปบางส่วน ตำแหน่งเฉพาะที่จะแยกพวกเขา สิ่งนี้จะทำให้ระบบการเคลื่อนที่ส่งข้อความแจ้งเตือนเกี่ยวกับตำแหน่งเอนทิตีที่เปลี่ยนไป ระบบตรวจจับการชนหมายถึงการตอบสนองเพราะมันต้องการที่จะอัพเดทดัชนีอวกาศ
ในบางกรณีมันทำให้เกิดปัญหาเนื่องจากเนื้อหาของเซลล์ (รายการทั่วไปของวัตถุเอนทิตีใน C #) ได้รับการแก้ไขในขณะที่มันถูกทำซ้ำมากกว่าจึงทำให้เกิดข้อยกเว้นที่จะถูกโยนโดย iterator
ดังนั้น ... ฉันจะป้องกันไม่ให้ระบบการชนถูกขัดจังหวะในขณะที่ตรวจสอบการชนได้อย่างไร
แน่นอนฉันสามารถเพิ่มตรรกะ "ฉลาด" / "หากิน" เพื่อให้แน่ใจว่าเนื้อหาของเซลล์จะวนซ้ำอย่างถูกต้อง แต่ฉันคิดว่าปัญหาไม่ได้อยู่ในระบบการชนกันของตัวเอง (ฉันยังมีปัญหาที่คล้ายกันในระบบอื่น) ข้อความได้รับการจัดการตามที่พวกเขาเดินทางจากระบบไปยังระบบ สิ่งที่ฉันต้องการคือวิธีการตรวจสอบให้แน่ใจว่าวิธีการจัดการเหตุการณ์ที่เฉพาะเจาะจงทำให้งานเป็นไปได้โดยไม่หยุดชะงัก
สิ่งที่ฉันได้ลอง:
- คิวข้อความเข้ามา ทุกครั้งที่บางระบบออกอากาศข้อความจะได้รับการเพิ่มในคิวข้อความของระบบที่สนใจ ข้อความเหล่านี้จะได้รับการประมวลผลเมื่อการอัพเดตระบบเรียกว่าแต่ละเฟรม ปัญหา : หากระบบ A เพิ่มข้อความไปยังคิว B ของระบบมันจะทำงานได้ดีถ้าระบบ B หมายถึงได้รับการปรับปรุงในภายหลังกว่าระบบ A (ในเฟรมเกมเดียวกัน); มิฉะนั้นจะทำให้ข้อความประมวลผลเฟรมเกมถัดไป (ไม่เป็นที่ต้องการสำหรับบางระบบ)
- คิวข้อความขาออก ขณะที่ระบบกำลังจัดการเหตุการณ์ข้อความใด ๆ ที่เผยแพร่จะถูกเพิ่มในคิวข้อความขาออก ข้อความไม่จำเป็นต้องรอให้มีการประมวลผลการอัปเดตระบบพวกเขาจะจัดการ "ทันที" หลังจากตัวจัดการข้อความเริ่มต้นทำงานเสร็จ หากการจัดการข้อความทำให้ข้อความอื่น ๆ ถูกถ่ายทอดข้อความเหล่านั้นจะถูกเพิ่มในคิวขาออกดังนั้นข้อความทั้งหมดจะได้รับการจัดการเฟรมเดียวกัน ปัญหา: ถ้าระบบอายุการใช้งานเอนทิตี (ฉันใช้การจัดการอายุการใช้งานเอนทิตีกับระบบ) สร้างเอนทิตีมันจะแจ้งบางระบบ A และ B เกี่ยวกับมัน ในขณะที่ระบบ A ประมวลผลข้อความมันจะทำให้เกิดห่วงโซ่ของข้อความที่ทำให้เอนทิตีที่สร้างขึ้นถูกทำลายในที่สุด (ตัวอย่างเช่นเอนทิตีกระสุนถูกสร้างขึ้นตรงที่มันชนกับสิ่งกีดขวางซึ่งทำให้กระสุนถูกทำลายด้วยตนเอง) ในขณะที่ห่วงโซ่ข้อความกำลังได้รับการแก้ไขระบบ B ไม่ได้รับข้อความการสร้างเอนทิตี ดังนั้นหากระบบ B ยังสนใจข้อความการทำลายเอนทิตีมันจะได้รับและหลังจากการ "เชน" เสร็จสิ้นการแก้ไขก็จะได้รับข้อความการสร้างเอนทิตีเริ่มต้น สิ่งนี้ทำให้ข้อความการทำลายถูกละเว้นข้อความการสร้างจะเป็น "ยอมรับ"
แก้ไข - ตอบคำถามข้อเสนอแนะ:
- ใครจะแก้ไขเนื้อหาของเซลล์ในขณะที่ระบบการชนกันทำซ้ำมัน?
ในขณะที่ระบบการชนกันกำลังทำการตรวจสอบการชนกันของเอนทิตีและเป็นเพื่อนบ้านการชนอาจถูกตรวจพบและระบบเอนทิตีจะส่งข้อความที่จะตอบสนองทันทีโดยระบบอื่น ๆ ปฏิกิริยาต่อข้อความอาจทำให้ข้อความอื่น ๆ ถูกสร้างขึ้นและจัดการได้ทันที ดังนั้นระบบอื่น ๆ อาจสร้างข้อความที่ระบบการชนกันนั้นจะต้องดำเนินการทันที (ตัวอย่างเช่นเอนทิตีที่ถูกย้ายดังนั้นระบบการชนต้องอัปเดตดัชนีเชิงพื้นที่) แม้ว่าการตรวจสอบการชนกันก่อนหน้านี้ยังไม่เสร็จ
- คุณไม่สามารถทำงานกับคิวข้อความที่ส่งออกทั่วโลกได้หรือไม่
ฉันลองคิวเดี่ยวทั่วโลกเมื่อเร็ว ๆ นี้ มันทำให้เกิดปัญหาใหม่ ปัญหา:ฉันย้ายเอนทิตีแท็งก์ไปที่เอนทิตีแบบติดผนัง (แท็งก์ควบคุมด้วยแป้นพิมพ์) จากนั้นฉันตัดสินใจเปลี่ยนทิศทางของรถถัง ในการแยกรถถังและผนังแต่ละเฟรม CollidingRigidBodySeparationSystem จะย้ายถังออกจากผนังด้วยจำนวนที่น้อยที่สุดเท่าที่จะเป็นไปได้ ทิศทางการแยกควรตรงข้ามทิศทางการเคลื่อนที่ของรถถัง (เมื่อเกมวาดเริ่มขึ้นรถถังควรดูราวกับว่ามันไม่เคยเคลื่อนที่เข้ามาในกำแพง) แต่ทิศทางจะตรงข้ามกับทิศทางใหม่ดังนั้นการย้ายรถถังไปยังอีกด้านหนึ่งของกำแพงมากกว่าตอนแรกคือ สาเหตุที่เกิดปัญหา:นี่คือวิธีจัดการข้อความในขณะนี้ (รหัสที่ง่าย):
public void Update(int deltaTime)
{
m_messageQueue.Enqueue(new TimePassedMessage(deltaTime));
while (m_messageQueue.Count > 0)
{
Message message = m_messageQueue.Dequeue();
this.Broadcast(message);
}
}
private void Broadcast(Message message)
{
if (m_messageListenersByMessageType.ContainsKey(message.GetType()))
{
// NOTE: all IMessageListener objects here are systems.
List<IMessageListener> messageListeners = m_messageListenersByMessageType[message.GetType()];
foreach (IMessageListener listener in messageListeners)
{
listener.ReceiveMessage(message);
}
}
}
โค้ดมีลักษณะดังนี้ (สมมติว่าไม่ใช่กรอบเกมแรก):
- ระบบเริ่มการประมวลผล TimePassedMessage
- InputHandingSystemแปลงการกดปุ่มเป็นการกระทำเอนทิตี (ในกรณีนี้ลูกศรซ้ายจะเปลี่ยนเป็นการกระทำของ MoveWest) การกระทำของเอนทิตีถูกเก็บไว้ในส่วนประกอบของ ActionExecutor
- ActionExecutionSystemในการตอบสนองต่อการกระทำของนิติบุคคลเพิ่ม MovementDirectionChangeRequestedMessage ไปยังจุดสิ้นสุดของคิวข้อความ
- MovementSystemย้ายตำแหน่งเอนทิตีตามข้อมูลส่วนประกอบ Velocity และเพิ่มข้อความ PositionChangedMessage ไปยังจุดสิ้นสุดของคิว การเคลื่อนที่ทำได้โดยใช้ทิศทางการเคลื่อนที่ / ความเร็วของเฟรมก่อนหน้า (สมมุติว่าทิศเหนือ)
- ระบบหยุดการประมวลผล TimePassedMessage
- ระบบเริ่มประมวลผล MovementDirectionChangeRequestedMessage
- MovementSystemเปลี่ยนความเร็ว / ทิศทางการเคลื่อนย้ายเอนทิตีตามที่ร้องขอ
- ระบบหยุดประมวลผล MovementDirectionChangeRequestedMessage
- ระบบเริ่มประมวลผล PositionChangedMessage
- CollisionDetectionSystemตรวจพบว่าเนื่องจากเอนทิตีย้ายมันวิ่งเข้าไปในเอนทิตี้อื่น (รถถังเข้าไปในกำแพง) มันเพิ่ม CollisionOccuredMessage ให้กับคิว
- ระบบหยุดประมวลผล PositionChangedMessage
- ระบบเริ่มการประมวลผล CollisionOccuredMessage
- CollidingRigidBodySeparationSystemตอบสนองต่อการชนโดยการแยกถังและผนัง เนื่องจากผนังคงที่เฉพาะถังเท่านั้นที่ถูกย้าย ทิศทางการเคลื่อนไหวของรถถังถูกใช้เป็นตัวบ่งชี้ว่ารถถังมาจากไหน มันจะชดเชยในทิศทางตรงกันข้าม
BUG:เมื่อรถถังย้ายเฟรมนี้มันเคลื่อนที่โดยใช้ทิศทางการเคลื่อนที่จากเฟรมก่อนหน้า แต่เมื่อมันถูกแยกออกทิศทางการเคลื่อนไหวจากเฟรมนี้จะถูกใช้แม้ว่ามันจะแตกต่างกันแล้ว นั่นไม่ใช่วิธีที่ควรใช้!
เพื่อป้องกันข้อผิดพลาดนี้จำเป็นต้องบันทึกทิศทางการเคลื่อนไหวเก่าไว้ที่ใดที่หนึ่ง ฉันสามารถเพิ่มส่วนประกอบบางอย่างเพื่อแก้ไขข้อผิดพลาดเฉพาะนี้ แต่กรณีนี้ไม่ได้ระบุวิธีจัดการข้อความที่ผิดหรือเปล่า? ทำไมระบบแยกควรดูแลทิศทางการเคลื่อนไหวที่ใช้? ฉันจะแก้ปัญหานี้อย่างสง่างามได้อย่างไร?
- คุณอาจต้องการอ่าน gamadu.com/artemis เพื่อดูว่าพวกเขาทำอะไรกับ Aspects ซึ่งขั้นตอนด้านปัญหาที่คุณเห็น
อันที่จริงฉันคุ้นเคยกับอาร์ทิมิสมาระยะหนึ่งแล้ว ตรวจสอบว่าเป็นซอร์สโค้ดอ่านฟอรัม ฯลฯ แต่ฉันเคยเห็น "แง่มุม" ที่กล่าวถึงเพียงไม่กี่แห่งและเท่าที่ฉันเข้าใจแล้วพวกเขาก็หมายถึง "ระบบ" แต่ฉันไม่สามารถเห็นได้ว่าอาร์ทิมิสด้านขั้นตอนของปัญหาของฉัน มันไม่ได้ใช้ข้อความด้วยซ้ำ
- ดูเพิ่มเติมที่: "การสื่อสารของเอ็นติตี้: คิวข้อความ vs เผยแพร่ / สมัครสมาชิก vs สัญญาณ / สล็อต"
ฉันได้อ่านคำถาม gamedev.stackexchange ทั้งหมดเกี่ยวกับระบบนิติบุคคลแล้ว อันนี้ดูเหมือนจะไม่พูดถึงปัญหาที่ฉันเผชิญ ฉันพลาดอะไรไปรึเปล่า?
- จัดการกับทั้งสองกรณีต่างกันการอัพเดตกริดไม่จำเป็นต้องพึ่งพาข้อความการเคลื่อนไหวเนื่องจากเป็นส่วนหนึ่งของระบบการชน
ฉันไม่แน่ใจว่าคุณหมายถึงอะไร การใช้งานที่เก่ากว่าของ CollisionDetectionSystem จะตรวจสอบการชนกันของข้อมูลบนการอัปเดต (เมื่อจัดการ TimePassedMessage) แต่ฉันต้องลดการตรวจสอบให้น้อยที่สุดเนื่องจากประสิทธิภาพ ดังนั้นฉันจึงเปลี่ยนเป็นการตรวจสอบการชนเมื่อเอนทิตีย้าย (เอนทิตีส่วนใหญ่ในเกมของฉันเป็นแบบสแตติก)