ทางเลือกแทนมรดกหลายอย่างสำหรับสถาปัตยกรรมของฉัน (NPC ในเกมกลยุทธ์เรียลไทม์)?


11

การเข้ารหัสไม่ว่าหนักจริง ส่วนที่ยากคือการเขียนโค้ดที่อ่านเข้าใจง่าย ดังนั้นฉันต้องการได้นักพัฒนาที่ดีขึ้นและสร้างสถาปัตยกรรมที่มั่นคง

ดังนั้นฉันต้องการสร้างสถาปัตยกรรมสำหรับNPCในวิดีโอเกม มันเป็นเกม Realtime Strategy เช่น Starcraft, Age of Empires, Command & Conquers ฯลฯ ดังนั้นฉันจะมี NPC หลายแบบ NPC สามารถมีความสามารถจำนวนมาก (วิธีการ) ของเหล่านี้: Build(), และFarm()Attack()

ตัวอย่าง:
คนทำงานสามารถBuild()และFarm()
นักรบสามารถAttack()
CitizenสามารถBuild(), Farm()และAttack()
ชาวประมงสามารถFarm()และAttack()

ฉันหวังว่าทุกอย่างจะชัดเจน

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

โอเคฉันสามารถมีคลาสพื้นฐานได้ จริงๆแล้วฉันคิดว่านี่เป็นวิธีที่ดีในการยึดมั่นในหลักการของDRY ดังนั้นฉันสามารถมีวิธีการเหมือนWalkTo(x,y)ในคลาสพื้นฐานของฉันเนื่องจาก NPC ทุกคนจะสามารถเคลื่อนไหวได้ แต่ตอนนี้มาถึงปัญหาที่แท้จริง ฉันจะใช้ความสามารถของฉันได้ที่ไหน (จำ: Build(), Farm()และAttack())

เนื่องจากความสามารถจะประกอบไปด้วยตรรกะเดียวกันมันจะน่ารำคาญ / ทำลายหลักการ DRY เพื่อนำไปใช้กับ NPC แต่ละคน (Worker, Warrior, .. )

โอเคฉันสามารถใช้ความสามารถภายในคลาสพื้นฐานได้ เรื่องนี้จะต้องชนิดของตรรกะบางอย่างที่จะตรวจสอบถ้า NPC สามารถใช้ความสามารถในการเอ็กซ์IsBuilder, CanBuild.. ผมคิดว่ามันเป็นที่ชัดเจนสิ่งที่ฉันต้องการที่จะแสดง
แต่ฉันไม่รู้สึกดีกับความคิดนี้ ดูเหมือนว่าจะเป็นคลาสพื้นฐานป่องที่มีฟังก์ชั่นมากเกินไป

ฉันใช้ภาษา C # เป็นภาษาโปรแกรม ดังนั้นการสืบทอดหลายอย่างไม่ใช่ตัวเลือกที่นี่ หมายความว่า: การมีคลาสพื้นฐานพิเศษอย่างFisherman : Farmer, Attackerจะไม่ทำงาน


3
ฉันกำลังดูตัวอย่างนี้ซึ่งดูเหมือนเป็นวิธีแก้ปัญหานี้: en.wikipedia.org/wiki/Composition_over_inheritance
Shawn Mclean

4
คำถามนี้จะพอดีดีกว่ามากในgamedev.stackexchange.com
ฟิลิปป์

คำตอบ:


14

องค์ประกอบและส่วนต่อประสานมรดกเป็นทางเลือกปกติท

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

public class Worker: ICanBuild, ICanFarm
{
    #region ICanBuild Implementation
    // Build
    #endregion

    #region ICanFarm Implementation
    // Farm
    #endregion
}

คุณสามารถส่งผ่านสิ่งปลูกสร้างและการทำฟาร์ม "ทั่วไป" ผ่านตัวสร้างคลาสของคุณ นั่นคือสิ่งที่องค์ประกอบมา


แค่คิดดัง ๆ : 'ความสัมพันธ์' จะ (ภายใน) ได้แทนที่จะเป็น (คนงานมีตัวสร้างแทนที่จะเป็นคนงานคือตัวสร้าง) ตัวอย่าง / รูปแบบที่คุณอธิบายมีความหมายและฟังดูเหมือนเป็นวิธีที่ดีในการแก้ปัญหาของฉัน แต่ฉันมีเวลายากที่จะได้รับความสัมพันธ์นี้
Brettetete

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

1
ICanBuildคนงานยังคงเป็นผู้สร้างเพราะคุณรับมาจาก การที่คุณมีเครื่องมือสำหรับการสร้างนั้นมีความกังวลรองในลำดับชั้นวัตถุ
Robert Harvey

มีเหตุผล. ฉันจะลองเจ้านี้ดู ขอบคุณสำหรับคำตอบที่เป็นมิตรและเป็นคำอธิบายของคุณ ฉันซาบซึ้งจริงๆ ฉันจะให้เปิดคำถามนี้บางครั้งแม้ว่า :)
Brettetete

4
@Betetete มันอาจช่วยให้คิดว่าการใช้งาน Build, Farm, Attack, Hunt และอื่น ๆ เป็นทักษะที่ตัวละครของคุณมี BuildSkill, FarmSkill, AttackSkill เป็นต้นจากนั้นองค์ประกอบจะสมเหตุสมผลมากขึ้น
Eric King

4

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

class component // Some generic base component structure
{
  void update() {} // With an empty update function
} 

ในกรณีนี้ขยายฟังก์ชั่นการอัพเดทเพื่อใช้งานฟังก์ชั่นที่ NPC ควรมี

class build : component
{
  override void update()
  { 
    // Check if the right object is selected, resources, etc
  }
}

การวนซ้ำคอนเทนเนอร์คอมโพเนนต์และการอัพเดตแต่ละคอมโพเนนต์อนุญาตให้ใช้ฟังก์ชันการทำงานเฉพาะของแต่ละคอมโพเนนต์

class npc
{
  void update()
  {
    foreach(component c in components)
      c.update();
  }

  list<component> components;
}

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

npc worker;
worker.add_component<build>();
worker.add_component<walk>();
worker.add_component<attack>();

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


3

หากคุณกำหนดอินเทอร์เฟซเช่นICanBuildนั้นระบบเกมของคุณจะต้องตรวจสอบทุก NPC ตามประเภทซึ่งโดยทั่วไปแล้วจะเป็นขมวดคิ้วใน OOP ตัวอย่างเช่นif (npc is ICanBuild).

คุณจะดีกว่าด้วย:

interface INPC
{
    bool CanBuild { get; }
    void Build(...);
    bool CanFarm { get; }
    void Farm(...);
    ... etc.
}

แล้ว:

class Worker : INPC
{
    private readonly IBuildStrategy buildStrategy;
    public Worker(IBuildStrategy buildStrategy)
    {
        this.buildStrategy = buildStrategy;
    }

    public bool CanBuild { get { return true; } }
    public void Build(...)
    {
        this.buildStrategy.Build(...);
    }
}

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

var workerType = NPC.Builder
    .Builds(new WorkerBuildBehavior())
    .Farms(new WorkerFarmBehavior())
    .Build();

var workerFactory = new NPCFactory(workerType);

var worker = workerFactory.Build();

ผู้สร้าง NPC สามารถใช้สิ่งDefaultWalkBehaviorที่สามารถแทนที่ได้ในกรณีของ NPC ที่บินได้ แต่ไม่สามารถเดินได้เป็นต้น


ดีเพียงแค่ความคิดดังอีกครั้ง: มีจริงไม่แตกต่างกันมากระหว่างเป็นและif(npc is ICanBuild) if(npc.CanBuild)แม้ว่าฉันจะชอบnpc.CanBuildวิธีการจากทัศนคติปัจจุบันของฉัน
Brettetete

3
@Betetete - คุณสามารถเห็นในคำถามนี้ทำไมโปรแกรมเมอร์บางคนไม่ชอบการตรวจสอบประเภท
Scott Whitlock

0

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

กำหนดประเภทหน่วยทั้งหมดในไฟล์กำหนดค่าหรือฐานข้อมูลแทนที่จะเข้ารหัสยาก

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

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