จะพัฒนาเกมเอนทิตีแบบต่อเนื่องในเกมเทิร์นเบสได้อย่างไร?


9

จนถึงขณะนี้ระบบส่วนประกอบเอนทิตีที่ฉันใช้เคยทำงานเหมือนอาร์ทิมิสของ Java:

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

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

ผู้เล่น A ได้รับความเสียหายจากดาบ ในการตอบสนองต่อสิ่งนี้เกราะของ A จะเข้ามาและลดความเสียหายที่ได้รับ ความเร็วในการเคลื่อนที่ของ A นั้นลดลงด้วยเช่นกันเนื่องจากความอ่อนแอ

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

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

สรุปทั้งหมดนี้ดูเหมือนจะนำไปสู่ปัญหาเล็กน้อย:

  1. วงจรการประมวลผลที่สูญเปล่ามากมาย: ระบบส่วนใหญ่ (บันทึกสำหรับสิ่งต่าง ๆ ที่ทำงานอยู่ตลอดเวลาเช่นการเรนเดอร์) ก็ไม่มีอะไรคุ้มค่าที่จะทำเมื่อมันไม่ใช่ "การเลี้ยว" ในการทำงานและใช้เวลาส่วนใหญ่รอเกม สถานะการทำงานที่ถูกต้อง litters นี้ทุกระบบดังกล่าวด้วยการตรวจสอบที่มีการเติบโตในขนาดที่รัฐเพิ่มเติมจะถูกเพิ่มเข้าไปในเกม
  2. ในการตรวจสอบว่าระบบสามารถประมวลผลเอนทิตีที่มีอยู่ในเกมได้หรือไม่พวกเขาต้องการวิธีในการตรวจสอบสถานะเอนทิตี / ระบบอื่น ๆ ที่ไม่เกี่ยวข้อง (ระบบที่รับผิดชอบในการจัดการความเสียหายจำเป็นต้องรู้ว่ามีการใช้เกราะหรือไม่) นี่อาจทำให้ระบบยุ่งเหยิงด้วยความรับผิดชอบหลายอย่างหรือสร้างความต้องการระบบเพิ่มเติมโดยไม่มีจุดประสงค์อื่น แต่เพื่อสแกนคอลเล็กชั่นเอนทิตีหลังจากแต่ละรอบการประมวลผลและสื่อสารกับกลุ่มผู้ฟังโดยบอกพวกเขาเมื่อมันไม่เป็นไร

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

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

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

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

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


คุณไม่ต้องการสำรวจความคิดเห็นเพื่อให้กิจกรรมเกิดขึ้น เหตุการณ์จะเกิดขึ้นก็ต่อเมื่อมันเกิดขึ้น อาร์ทิมิสอนุญาตให้ระบบสื่อสารกันได้หรือไม่?
Sidar

มันทำได้ แต่โดยการเชื่อมต่อพวกเขาโดยใช้วิธีการ
Aeris130

คำตอบ:


3

คำแนะนำของฉันมาจากประสบการณ์ที่ผ่านมาในโครงการสวมบทบาทที่เราใช้ระบบส่วนประกอบ ฉันจะบอกว่าฉันเกลียดการทำงานในรหัส gameside นั้นเพราะมันเป็นรหัสสปาเก็ตตี้ ดังนั้นฉันจึงไม่เสนอคำตอบมากที่นี่เพียงมุมมอง:

ตรรกะที่คุณอธิบายเพื่อจัดการกับความเสียหายของดาบให้กับผู้เล่น ... ดูเหมือนว่าระบบใดระบบหนึ่งควรรับผิดชอบทั้งหมดนี้

บางแห่งมีฟังก์ชัน HandleWeaponHit () มันจะเข้าถึง ArmorComponent ของผู้เล่นเพื่อรับชุดเกราะที่เกี่ยวข้อง มันจะเข้าถึง WeaponComponent ของอาวุธโจมตีของหน่วยงานเพื่อทำลายอาวุธ หลังจากคำนวณความเสียหายขั้นสุดท้ายแล้วมันจะสัมผัสกับ MovementComponent เพื่อให้ผู้เล่นบรรลุการลดความเร็ว

สำหรับรอบการประมวลผลที่สูญเปล่า ... HandleWeaponHit () ควรถูกเรียกใช้เมื่อจำเป็นเท่านั้น (เมื่อตรวจจับการโจมตีของดาบ)

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


การทำเช่นนี้จะทำให้ฟังก์ชัน hit () เอาท์พุตมีพฤติกรรมเพิ่มขึ้น สมมติว่ามีศัตรูที่หัวเราะลงทุกครั้งที่มีดาบกระทบเป้าหมาย (เป้าหมายใด ๆ ) ที่อยู่ในแนวสายตา HandleWeaponHit ควรเป็นต้นเหตุของสิ่งนั้นจริงหรือ?
Aeris130

1
คุณมีลำดับการต่อสู้ที่แนบแน่นดังนั้นใช่การเข้าชมจะมีผลต่อการกระตุ้นเอฟเฟกต์ ไม่ใช่ทุกสิ่งที่จะต้องแยกออกเป็นระบบเล็ก ๆ ปล่อยให้ระบบนี้จัดการสิ่งนี้เพราะมันเป็น "ระบบการต่อสู้" ของคุณและมันก็จัดการ ... การต่อสู้ ...
Patrick Hughes

3

มันเป็นคำถามอายุหนึ่งปี แต่ตอนนี้ฉันกำลังเผชิญหน้ากับทฤษฏีเดียวกันกับเกมทำที่บ้านของฉันในขณะที่เรียน ECS ดังนั้นจึงมีเวทมนตร์บางอย่าง หวังว่ามันจะจบลงในการอภิปรายหรืออย่างน้อยความคิดเห็น

ฉันไม่แน่ใจว่าเป็นการละเมิดแนวคิดของ ECS หรือไม่ แต่ถ้า:

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

ตัวอย่าง:

  • UserInputSystem ยิงเหตุการณ์โจมตีด้วย[DamageDealerEntity, DamageReceiverEntity, Skill / Weapon ที่ใช้ข้อมูล]
  • CombatSystem สมัครเป็นสมาชิกและคำนวณโอกาสการหลบเลี่ยงสำหรับ DamageReceiver หากการหลีกเลี่ยงล้มเหลวมันจะทริกเกอร์เหตุการณ์ความเสียหายด้วยพารามิเตอร์เดียวกัน
  • DamageSystem สมัครรับข้อมูลเหตุการณ์ดังกล่าวและถูกเรียกใช้
  • DamageSystem ใช้ Strength, BaseWeapon damage, ประเภทของมันและเขียนไปยัง IncomingDamageComponent ใหม่ด้วย[DamageDealerEntity, FinalOutgoingDamage, DamageType]และแนบไปกับผู้รับความเสียหาย Entity / Entities
  • DamageSystem ยิง OutgoingDamageCalculated
  • ArmorSystem ถูกเรียกใช้โดยรับเอนทิตี้รับหรือค้นหาโดย IncomingDamage นี้ใน Entities เพื่อรับ IncomingDamageComponent (อันสุดท้ายน่าจะดีกว่าสำหรับการโจมตีหลายครั้งด้วยการแพร่กระจาย) และคำนวณเกราะและความเสียหายที่ใช้กับมัน เป็นตัวเลือกที่ทำให้เกิดเหตุการณ์สำหรับการทำลายดาบ
  • ArmorSystems ลบ IncomingDamageComponent ในแต่ละเอนทิตีและแทนที่ด้วย DamageReceivedComponent ด้วยตัวเลขที่คำนวณสุดท้ายซึ่งจะส่งผลให้ HP และการลดความเร็วจากบาดแผล
  • ArmorSystems ส่งเหตุการณ์ IncomingDamageCalculated
  • ระบบความเร็วจะสมัครและคำนวณความเร็วใหม่
  • HealthSystem สมัครสมาชิกและลด HP จริง
  • ฯลฯ
  • ยังไงก็เถอะทำความสะอาด

ข้อดี:

  • ระบบทริกเกอร์ซึ่งให้ข้อมูลระหว่างกลางสำหรับเหตุการณ์ลูกโซ่ที่ซับซ้อน
  • Decoupling โดย EventBus

จุดด้อย:

  • ฉันรู้สึกว่าฉันผสมสองวิธีในการส่งผ่านสิ่งต่าง ๆ : ในผู้ควบคุมเหตุการณ์และในส่วนประกอบชั่วคราว มันอาจจะเป็นสถานที่ที่อ่อนแอ ในทางทฤษฎีเพื่อรักษาสิ่งที่เป็นเนื้อเดียวกันฉันสามารถยิงได้เพียง enum เหตุการณ์ที่ไม่มีข้อมูลเพื่อให้ระบบจะหาพารามิเตอร์โดยนัยในองค์ประกอบของ Entity ตามแง่มุม ... ไม่แน่ใจว่าตกลงหรือไม่
  • ไม่แน่ใจว่าจะรู้ได้อย่างไรว่าระบบที่สนใจทั้งหมดได้รับการประมวลผล IncomingDamageCalculated เพื่อให้สามารถล้างข้อมูลได้และปล่อยให้เกิดการหมุนครั้งถัดไป บางทีการตรวจสอบบางอย่างกลับมาใน CombatSystem ...

2

โพสต์วิธีการแก้ปัญหาที่ฉันตัดสินในที่สุดคล้ายกับของยาโคฟเล

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

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

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

ตัวอย่าง: ผู้เล่นได้รับความเสียหายดังนั้นผู้เล่นจะถูกส่งเหตุการณ์ความเสียหาย DamageSystem สมัครรับความเสียหายจากเหตุการณ์ที่ส่งไปยังเอนทิตีที่มีองค์ประกอบด้านสุขภาพและมีเมธอด onEvent (เอนทิตีเหตุการณ์) ที่ลดความสมบูรณ์ขององค์ประกอบในเอนทิตีตามจำนวนที่ระบุในเหตุการณ์

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

บางครั้งระบบต้องก้าวออกไปข้างนอกเอนทิตีที่ได้รับอย่างไรก็ตาม เพื่อตอบสนองต่อ Eric Undersander ของฉันต่อไปจะเป็นการเพิ่มระบบที่เข้าถึงแผนที่เกมและค้นหาเอนทิตีของ FallsDownLaughingComponent ภายใน x space ของเอนทิตีที่ได้รับความเสียหายจากนั้นส่ง FallDownLaughingEvent ให้พวกเขา ระบบนี้จะต้องถูกกำหนดให้รับเหตุการณ์หลังจากระบบความเสียหายหากเหตุการณ์ความเสียหายไม่ได้ถูกยกเลิก ณ จุดนั้นความเสียหายนั้นจะได้รับการจัดการ

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

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

ในคิว: การเคลื่อนไหว

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

ในคิว: TurnOver

ตอนนี้บอกว่าผู้เล่นเหยียบกับดักสร้างความเสียหาย TrapSystem ได้รับข้อความการเคลื่อนไหวก่อนที่ TurnFinishedSystem ดังนั้นเหตุการณ์ความเสียหายจะถูกส่งก่อน ตอนนี้คิวจะมีลักษณะดังนี้:

ในคิว: ความเสียหาย TurnOver

ทุกอย่างเรียบร้อยแล้วเหตุการณ์ความเสียหายจะได้รับการประมวลผลจากนั้นจะสิ้นสุดลง อย่างไรก็ตามจะเกิดอะไรขึ้นถ้ามีการส่งเหตุการณ์เพิ่มเติมเพื่อตอบสนองต่อความเสียหาย? ตอนนี้คิวเหตุการณ์จะมีลักษณะดังนี้:

ในคิว: ความเสียหาย TurnOver, ResponseToDamage

กล่าวอีกนัยหนึ่งการเลี้ยวจะจบลงก่อนที่จะมีการตอบสนองต่อความเสียหายใด ๆ

เพื่อแก้ปัญหานี้ฉันลงเอยด้วยการใช้สองวิธีในการส่งเหตุการณ์: send (event, entity) และการตอบกลับ (event, eventToRespondTo, entity)

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

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

เมื่อการตอบสนองต่อเหตุการณ์ความเสียหายถูกส่งเหตุการณ์เหล่านั้นจะมีทั้งเหตุการณ์ความเสียหายรวมทั้งการเคลื่อนไหววางไว้ในคิวที่ดัชนี [2] ตราบใดที่ index [n] มีเหตุการณ์ในคิวเหตุการณ์เหล่านั้นจะถูกประมวลผลก่อนที่จะไปยัง [n-1] สิ่งนี้ให้ลำดับการประมวลผลของ:

การเคลื่อนไหว -> ความเสียหาย [1] -> ResponseToDamage [2] -> [2] ว่างเปล่า -> TurnOver [1] -> [1] ว่างเปล่า -> [0] ว่างเปล่า

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