การจัดการอินพุตในการออกแบบตามส่วนประกอบ


12

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

การออกแบบที่ใช้ส่วนประกอบที่ฉันใช้นั้นเป็นไปตามชุดบล็อกของT = MachineและArtemisซึ่ง Entities เป็นแค่รหัส

มีสามแนวคิดหลักที่ฉันมีในการใช้การจัดการอินพุต:

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

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

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


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

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

1
@ ลุค B: หลังจากคิดแล้วฉันเห็นว่าคุณสามารถทำให้กล้องเป็นคลาสที่แยกจากกันโดยใช้ตัวชี้ไปยังเอนทิตีที่ต้องติดตาม
Grieverheart

คำตอบ:


8

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

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

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

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


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

4

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

อุปกรณ์อินพุตได้รับการจัดการโดยระบบระดับล่างซึ่งได้รับเหตุการณ์จากปุ่ม, ปุ่ม, แกน, เม้าส์, พื้นผิวแบบสัมผัส, มาตรวัดความเร่ง ...

เหตุการณ์เหล่านี้จะถูกส่งผ่านเลเยอร์ของตัวสร้างเจตนาที่ขึ้นอยู่กับบริบท

ตัวกำเนิดแต่ละตัวลงทะเบียนเพื่อป้อยอการเปลี่ยนแปลงจากส่วนประกอบหน่วยงานและระบบที่เกี่ยวข้องกับการทำงานของมัน

เครื่องกำเนิดไฟฟ้าเหล่านี้จะส่งข้อความ / ความตั้งใจสำหรับการกำหนดเส้นทางไปยังระบบความตั้งใจที่เอนทิตีมีส่วนประกอบหรือโดยตรงไปยังองค์ประกอบที่เหมาะสม

วิธีนี้คุณสามารถพึ่งพา "เสมอ" โดยมีอินพุตเดียวกันคือ JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...

และ "ทั้งหมด" งานอินพุตที่ขึ้นอยู่กับแพลตฟอร์มสกปรกยังคงอยู่นอกระบบของคุณ


เกี่ยวกับกล้องหากคุณต้องการเคลื่อนย้ายไปรอบ ๆ เครื่องเล่นมันสามารถลงทะเบียนองค์ประกอบความตั้งใจของตัวเองและฟังความตั้งใจที่คุณจะส่ง

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


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

2

InputComponent นำประโยชน์อะไรมาให้บ้าง แน่นอนว่ามันเป็นสิทธิพิเศษของคำสั่งอินพุตเพื่อตัดสินใจว่าเอนทิตีใดที่กำลังดำเนินการกับ ตัวอย่างคลาสสิกคือการทำให้ผู้เล่นกระโดด แทนที่จะมี InputComponent ในทุกเอนทิตีที่รับฟังเหตุการณ์ "กระโดด" ทำไมไม่มีคำสั่งข้ามค้นหาเอนทิตีที่ทำเครื่องหมาย "ผู้เล่น" และดำเนินการตามตรรกะที่จำเป็นเอง?

Action jump = () =>
{
    entities["player"].Transform.Velocity.Y += 5;
};

อีกตัวอย่างหนึ่งจาก OP:

Action moveRight = () =>
{
    foreach (var entity in entities.Tagged("player", "camera"))
        entity.Transform.Position.X += 5;
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.