คำแนะนำเกี่ยวกับสถาปัตยกรรมเกม / รูปแบบการออกแบบ


16

ฉันได้ทำงานกับ RPG 2d มานานแล้วและฉันก็ตระหนักว่าฉันได้ตัดสินใจออกแบบที่ไม่ดี โดยเฉพาะอย่างยิ่งมีบางสิ่งที่ทำให้ฉันมีปัญหาดังนั้นฉันจึงสงสัยว่าคนอื่น ๆ ที่ใช้ในการเอาชนะพวกเขาหรือใช้

สำหรับพื้นหลังเล็กน้อยฉันเริ่มทำงานในเวลาว่างของฉันเมื่อฤดูร้อนปีที่แล้ว ตอนแรกฉันสร้างเกมใน C # แต่ประมาณ 3 เดือนที่แล้วตัดสินใจเปลี่ยนเป็น C ++ ฉันต้องการได้รับการจัดการที่ดีใน C ++ เพราะมันไม่นานมานี้เพราะฉันใช้มันอย่างหนักและคิดว่าโครงการที่น่าสนใจเช่นนี้จะเป็นแรงจูงใจที่ดี ฉันใช้ห้องสมุดเพิ่มอย่างกว้างขวางและใช้ SFML สำหรับกราฟิกและ FMOD สำหรับเสียง

ฉันมีโค้ดที่ค่อนข้างเป็นธรรม แต่กำลังพิจารณาที่จะทิ้งมันและเริ่มต้นใหม่

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

1. การพึ่งพาตามวัฏจักร เมื่อฉันทำเกมใน C # ฉันไม่ต้องกังวลเกี่ยวกับสิ่งนี้เพราะมันไม่มีปัญหา การย้ายไปที่ C ++ นี้ได้กลายเป็นปัญหาใหญ่พอสมควรและทำให้ฉันคิดว่าฉันอาจออกแบบสิ่งต่าง ๆ ไม่ถูกต้อง ฉันไม่สามารถจินตนาการได้ว่าจะแยกชั้นเรียนของฉันอย่างไรและยังให้พวกเขาทำสิ่งที่ฉันต้องการ นี่คือตัวอย่างบางส่วนของห่วงโซ่การพึ่งพา:

ฉันมีคลาสเอฟเฟ็กต์สถานะ ชั้นเรียนมีวิธีการมากมาย (ใช้ / นำไปใช้, ขีด, ฯลฯ ) เพื่อใช้เอฟเฟกต์กับอักขระ ตัวอย่างเช่น

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

ฟังก์ชั่นนี้จะถูกเรียกว่าทุกครั้งที่ตัวละครที่มีผลกระทบสถานะจะเปลี่ยน มันจะใช้เพื่อใช้เอฟเฟกต์เช่น Regen, Poison เป็นต้นอย่างไรก็ตามมันยังแนะนำการพึ่งพาคลาส BaseCharacter และคลาส BattleField โดยปกติแล้วคลาส BaseCharacter จำเป็นต้องติดตามผลกระทบสถานะใดบ้างที่พวกเขากำลังใช้งานอยู่ในปัจจุบันเพื่อให้การพึ่งพาวงจร Battlefield จำเป็นต้องติดตามฝ่ายต่อสู้และกลุ่มปาร์ตี้มีรายชื่อ BaseCharacters ที่แนะนำการพึ่งพาวงจรอื่น

2 - กิจกรรม

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

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

และในชั้นเรียนของฉัน

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

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

3 - กราฟิก

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

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

ฉันออกแบบตัวจัดการหน้าจอที่จะผลักและป็อปหน้าจอตามการป้อนข้อมูลของผู้ใช้

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

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

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

คำตอบ:


15

โดยรวมแล้วฉันจะไม่พูดอะไรที่คุณระบุไว้ควรทำให้คุณสนใจระบบและเริ่มต้นใหม่ นี่คือสิ่งที่โปรแกรมเมอร์ทุกคนต้องการทำประมาณ 50-75% ของวิธีการผ่านโครงการใด ๆ ที่พวกเขากำลังทำงานอยู่ แต่มันนำไปสู่วงจรการพัฒนาที่ไม่สิ้นสุดและไม่เคยทำอะไรเลย ดังนั้นเพื่อที่จะฟีดกลับในแต่ละส่วน

  1. นี่อาจเป็นปัญหา แต่มักจะเป็นเรื่องน่ารำคาญมากกว่าสิ่งอื่นใด คุณใช้ #pragma หนึ่งครั้งหรือ #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... #endif ที่ด้านบนหรือรอบ ๆ ไฟล์. h ตามลำดับหรือไม่ วิธีนี้ไฟล์. h มีอยู่เพียงครั้งเดียวภายในแต่ละขอบเขต หากคุณเป็นคำแนะนำของฉันจะกลายเป็นการลบข้อความ #include ทั้งหมดและคอมไพล์โดยเพิ่มข้อความที่จำเป็นในการคอมไพล์เกมอีกครั้ง

  2. ฉันเป็นแฟนของระบบประเภทนี้และไม่เห็นอะไรผิดปกติ อะไรคือกิจกรรมใน C # โดยทั่วไปจะถูกแทนที่ด้วย Event System หรือ Messaging System (สามารถค้นหาคำถามที่นี่เพื่อหาข้อมูลเพิ่มเติม) กุญแจสำคัญในที่นี้คือการรักษาสิ่งเหล่านี้ให้น้อยที่สุดเมื่อสิ่งที่จำเป็นต้องเกิดขึ้นซึ่งดูเหมือนว่าคุณกำลังทำอยู่แล้วไม่ควรเป็นกังวลน้อยที่สุด

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

ดังนั้นโดยรวมแล้วฉันไม่เห็นการรีสตาร์ทที่คุ้มค่ากับสิ่งที่คุณกำลังประสบ คุณอาจต้องการระบบเหตุการณ์ที่เป็นทางการมากขึ้นมาแทนที่ถนน แต่มันจะมาทันเวลา วงจร Cyclic เป็นอุปสรรคสำหรับโปรแกรมเมอร์ C / C ++ ทุกคนที่ต้องข้ามผ่านไปเรื่อย ๆ และการทำงานเพื่อแยกกราฟิกทั้งหมดดูเหมือนเป็นตรรกะ 'ขั้นตอนต่อไป'

หวังว่านี่จะช่วยได้!


#ifdef ไม่ได้ช่วยแก้ปัญหาแบบวงกลม
The Duck คอมมิวนิสต์

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

ขอบคุณสำหรับคำแนะนำ :) ดีใจที่รู้ว่าฉันอยู่ในเส้นทางที่ถูกต้อง
127817

4

การขึ้นต่อเนื่องตามวัฏจักรของคุณไม่ควรเป็นปัญหาตราบใดที่คุณกำลังประกาศคลาสที่คุณสามารถทำได้ในไฟล์ส่วนหัวและ #including พวกเขาในไฟล์. cpp (หรืออะไรก็ตาม)

สำหรับระบบเหตุการณ์สองข้อเสนอแนะ:

1) หากคุณต้องการคงรูปแบบที่คุณใช้อยู่ตอนนี้ให้พิจารณาเปลี่ยนเป็น boost :: unordered_map แทน std :: map การแม็พกับสตริงเนื่องจากคีย์ช้าโดยเฉพาะตั้งแต่. NET ทำสิ่งที่ดีภายใต้ประทุนเพื่อช่วยเร่งความเร็วให้กับสิ่งต่างๆ ใช้ unordered_map แฮชสตริงเพื่อเปรียบเทียบโดยทั่วไปเร็วขึ้น

2) ลองเปลี่ยนไปใช้บางสิ่งที่มีประสิทธิภาพมากขึ้นเช่นเพิ่มสัญญาณ :: หากคุณทำเช่นนั้นคุณสามารถทำสิ่งที่ดีเช่นทำให้เกมวัตถุของคุณสามารถติดตามได้โดยการเพิ่มจาก :: สัญญาณ :: ติดตามได้และให้ผู้ทำลายล้างทำความสะอาดทุกอย่างแทนที่จะต้องถอนการลงทะเบียนด้วยตนเองจากระบบเหตุการณ์ นอกจากนี้คุณยังสามารถมีสัญญาณหลายชี้ไปที่แต่ละช่อง (หรือในทางกลับกันผมจำไม่ได้ตั้งชื่อแน่นอน) ดังนั้นมันจึงคล้ายกับการทำ+=บนdelegateใน C # ปัญหาที่ใหญ่ที่สุดที่มีสัญญาณเพิ่ม :: คือต้องมีการรวบรวมมันไม่ได้เป็นเพียงส่วนหัวดังนั้นขึ้นอยู่กับแพลตฟอร์มของคุณมันอาจเป็นความเจ็บปวดที่จะลุกขึ้นและวิ่ง

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