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


11

ฉันทำดันเจี้ยนเล็ก ๆ น้อย ๆ ในอวกาศและฉันอยากจะฟังคำแนะนำบางอย่างเกี่ยวกับวิธีสร้างแบ็กเอนด์ของเครื่องยนต์ที่ดีกว่า โดยทั่วไปปัจจุบันทุกอย่างขึ้นอยู่กับ crapload ของผู้จัดการ:

  • BackgroundManager: มีAddBackground(image, parallax)วิธีในการสร้างเอฟเฟกต์ฉากหลังที่ยอดเยี่ยม
  • ConfigManager: อ่าน / สร้างไฟล์ config และเก็บข้อมูลที่อ่านจากไฟล์ config นั้น
  • DrawManager: มีDraw(sprite)วิธีการวาดสิ่งที่จะหน้าจอและสิ่งที่ชอบSetView(view), GetView()ฯลฯ
  • EntityManager: เก็บเอนทิตีที่ใช้งานอยู่ทั้งหมดและมีวิธีการเพิ่มลบและค้นหาเอนทิตี
  • DungeonManager (ที่จริงเรียกว่า GridManager แต่นี่คือความเรียบง่าย): มีวิธีการชอบGenerateDungeon(), PlaceEnemies(), PlaceFinish()ฯลฯ

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

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

((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()

นั่นน่าเกลียดจริงๆ

ดังนั้นนักพัฒนาเกมเพื่อนคุณมีความคิดในการแก้ปัญหานี้บ้างไหม? ฉันจะชื่นชม!


ฉันไม่แน่ใจว่าจะช่วยคุณได้อย่างไร แต่ฉันขอแนะนำให้อ่านหนังสือที่ดีเกี่ยวกับรูปแบบการออกแบบ ฉันอ่านรูปแบบการออกแบบ Head First และเข้าใจง่ายมาก ตัวอย่างอยู่ใน Java แต่ฉันคิดว่ามันจะใกล้เคียงกับ C # เพื่อช่วยคุณ ขออภัยถ้าคำแนะนำของฉันเป็นสามเณรเกินไป
Amplify91

คุณใช้ส่วนต่อประสานกับการ์ดกราฟิกคืออะไร XNA? SlimDX?
BlueRaja - Danny Pflughoeft

@BlueRaja: ฉันกำลังใช้ SFML.NET
Dlaor

ในกรณีดังกล่าวคุณควรใช้วิธีการปกติในการจัดการกับปัญหาเหล่านี้ใน C #: ดูลิงค์สุดท้ายของฉันด้านล่างรวมถึงการฉีดพึ่งพาคืออะไร?
BlueRaja - Danny Pflughoeft

คำตอบ:


10

สิ่งที่เกี่ยวกับเครื่องยนต์ component-based ?

คุณจะได้เรียนหลักชื่อEngineซึ่งจะเก็บรายชื่อของที่ตัวเองจะถือรายการGameScreensComponents

เครื่องยนต์มีUpdateและDrawวิธีการและการโทรทั้งGameScreen's UpdateและDrawวิธีการที่ตัวเองไปผ่านทุกองค์ประกอบและการโทรและUpdateDraw

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

มันง่ายกว่ามากในการรักษาโค้ดเช่นนี้เนื่องจากคุณเพิ่งจะผ่านลำดับชั้นของชั้นเรียนขนาดใหญ่และไม่ต้องค้นหาBackgroundManagerภูมิหลังที่แตกต่างกันทั้งหมด คุณเพียงแค่มีScrollingBackground, ParallaxBackground, StaticBackgroundฯลฯ ซึ่งมาจากทุกBackgroundระดับ

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

คุณจะไม่มีBackgroundManagerชั้นเรียนอีกต่อไปแต่Backgroundชั้นเรียนที่จะจัดการตัวเองแทน

เมื่อเกมของคุณเริ่มต้นสิ่งที่คุณต้องทำก็คือ:

// when declaring variables:
Engine engine;

// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());

// when updating:
engine.Update();

// when drawing:
engine.Draw();

และนี่คือรหัสเริ่มต้นสำหรับเกมของคุณ

จากนั้นสำหรับหน้าจอเมนูหลัก:

class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
    const int ENEMY_COUNT = 10;

    StaticBackground background;
    Player player;
    List<Enemy> enemies;

    public override void Initialize()
    {
        background = new StaticBackground();
        player = new Player();
        enemies = new List<Enemy>();

        base.AddComponent(background); // defined within the GameScreen class
        base.AddComponent(player);
        for (int i = 0; i < ENEMY_COUNT; ++i)
        {
            Enemy newEnemy = new Enemy();
            enemies.Add(newEnemy);
            base.AddComponent(newEnemy);
        }
    }
}

คุณได้รับความคิดทั่วไป

นอกจากนี้คุณยังจะต้องอ้างอิงการอ้างอิงEngineภายในGameScreenคลาสทั้งหมดของคุณเพื่อให้สามารถเพิ่มหน้าจอใหม่แม้ในGameScreenคลาส (เช่นเมื่อผู้ใช้คลิกที่ปุ่มStartGameในขณะที่อยู่ภายในMainMenuScreenคุณสามารถเปลี่ยนไปใช้GameplayScreen)

ไปที่Componentคลาสเดียวกัน: มันควรเก็บการอ้างอิงของพาGameScreenเรนต์เพื่อให้เข้าถึงEngineคลาสและพาเรนต์GameScreenเพื่อเพิ่มคอมโพเนนต์ใหม่ (เช่นคุณสามารถสร้างคลาสที่เกี่ยวข้องกับ HUD เรียกว่าDrawableButtonซึ่งมี DrawableTextส่วนประกอบและStaticBackgroundส่วนประกอบ)

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

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


1
XNA รองรับการใช้งานนอกสถานที่ทั้งหมดนี้ผ่านทางGameComponents และบริการ ไม่จำเป็นต้องแยกEngineชั้นเรียน
BlueRaja - Danny Pflughoeft

+1 สำหรับคำตอบนี้ นอกจากนี้มันจะทำให้รู้สึกที่จะนำรูปวาดในเธรดแยกจากเธรดการปรับปรุงเกม
f20k

1
@ BlueLaja - DannyPflughoeft: แน่นอน แต่ฉันคิดว่า XNA ไม่ได้ใช้ดังนั้นฉันจึงอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับวิธีการทำ
Jesse Emond

@ f20k: ใช่แล้ว XNA ทำได้เหมือนกัน ฉันไม่รู้ว่ามันถูกนำไปใช้อย่างไร = / ต้องมีบทความอยู่บนอินเทอร์เน็ตเกี่ยวกับวิธีการทำ :)
Jesse Emond

ใช่ฉันไม่ได้ใช้ XNA แต่ดูเหมือนจะเป็นทางเลือกที่ดีสำหรับสิ่งที่ฉันทำ ฉันกำลังอ่านหนังสือ "รูปแบบการออกแบบหัวแรก" เพื่อดูว่ามีทางเลือกอื่นอีกหรือไม่ ไชโย!
Dlaor

1

สิ่งที่คุณต้องการทำคือแยกรหัสของคุณออกเป็นส่วนประกอบและบริการ XNA มีการสนับสนุนในตัวสำหรับสิ่งเหล่านี้อยู่แล้ว

ดูบทความนี้บนGameComponentsและบริการ จากนั้นดูคำตอบนี้เกี่ยวกับการใช้บริการใน XNA และคำตอบทั่วไปเพิ่มเติมเกี่ยวกับบริการในภาษาใด ๆ


-3

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


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