การสร้างระบบรายการที่แข็งแกร่ง


12

จุดประสงค์ของฉันคือการสร้างระบบไอเท็มแบบโมดูลาร์ / แบบทั่วไปที่สามารถจัดการสิ่งต่าง ๆ เช่น:

  • รายการที่อัปเกรดได้ (+6 Katana)
  • ตัวดัดแปลงทางสถิติ (ความชำนาญ +15)
  • ตัวดัดแปลงไอเท็ม (โอกาส X% ที่จะทำความเสียหาย Y, โอกาสที่จะถูกแช่แข็ง)
  • รายการที่ชาร์จใหม่ได้ (พนักงาน Magic กับประเพณี 30)
  • Set Items (ติดตั้ง X 4 ชุดเพื่อเปิดใช้งานคุณสมบัติ Y)
  • หายาก (ทั่วไป, ไม่ซ้ำใคร, ตำนาน)
  • ทำให้ไม่ลงรอยกัน (แบ่งเป็นวัสดุหัตถกรรมบางอย่าง)
  • Craftable (สามารถสร้างขึ้นด้วยวัสดุบางอย่าง)
  • ใช้งานได้ (5 นาที% พลังโจมตี X, รักษา +15 hp)

* ฉันสามารถแก้ไขคุณสมบัติที่เป็นตัวหนาในการตั้งค่าต่อไปนี้

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

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

  • BaseStat: คลาสทั่วไปที่เก็บสถานะ on-the-go (สามารถใช้สำหรับไอเท็มและสถิติของตัวละครได้เช่นกัน)
  • รายการ: คลาสที่เก็บข้อมูลเช่นรายการชื่อรายการประเภทและสิ่งต่าง ๆ ที่เกี่ยวข้องกับ ui, actionName คำอธิบาย ฯลฯ
  • IWeapon: ส่วนต่อประสานสำหรับอาวุธ อาวุธทุกชนิดจะมีคลาสของตัวเองโดยมีการใช้ IWeapon สิ่งนี้จะมีการโจมตีและการอ้างอิงถึงสถิติของตัวละคร เมื่อติดตั้งอาวุธมันคือข้อมูล (Item class 'stat) จะถูกฉีดเข้าไปในตัวละคร stat (ไม่ว่า BaseStat มันมีอะไรมันจะถูกเพิ่มเข้าไปในคลาสของตัวละครเป็นโบนัส Stat) ตัวอย่างเช่นเราต้องการสร้างดาบ สร้างคลาสไอเท็มด้วย json) ดังนั้น Sword จะเพิ่มการโจมตี 5 ครั้งให้กับสถิติตัวละคร ดังนั้นเราจึงมีBaseStatเป็น ("Attack", 5) (เราสามารถใช้ enum ได้เช่นกัน) สถิตินี้จะถูกเพิ่มลงในสถานะ "Attack" ของตัวละครเป็นBonusStat (ซึ่งจะเป็นคลาสที่แตกต่างกัน) เมื่อทำการติดตั้ง ดังนั้นคลาสที่ชื่อว่าSword จะใช้IWeaponจะถูกสร้างขึ้นเมื่อ 'สถานะที่ถูกสร้างขึ้นตัวละครคลาสไอเท็มเข้าสู่ดาบนี้และเมื่อโจมตีมันสามารถดึงสถิติการโจมตีทั้งหมดจากสถิติตัวละครและดาเมจความเสียหายในวิธีการโจมตี
  • BonusStat: เป็นวิธีการเพิ่มสถิติเป็นโบนัสโดยไม่ต้องสัมผัส BaseStat
  • IConsumable: ตรรกะเช่นเดียวกับ IWeapon การเพิ่มสถิติโดยตรงนั้นค่อนข้างง่าย (+15 hp) แต่ฉันไม่แน่ใจเกี่ยวกับการเพิ่มอาวุธชั่วคราวด้วยการตั้งค่านี้ (% x เพื่อโจมตีเป็นเวลา 5 นาที)
  • IUpgradeable: สามารถดำเนินการได้ด้วยการตั้งค่านี้ ฉันคิดว่าUpgradeLevelเป็นสถิติพื้นฐานซึ่งเพิ่มขึ้นเมื่ออัพเกรดอาวุธ เมื่ออัปเกรดเราสามารถคำนวณBaseStatของอาวุธอีกครั้งเพื่อให้ตรงกับระดับการอัพเกรด

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

เพื่อให้คุณมีส่วนร่วมได้ง่ายนี่คือคำถามที่คุณอาจช่วยได้:

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

รู้สึกอิสระที่จะตอบคำถามเดียวเพราะฉันเก็บคำถามไว้อย่างกว้าง ๆ เพื่อที่ฉันจะได้รับความรู้จากด้านต่าง ๆ / ผู้คน


แก้ไข


ทำตามคำตอบของ @ jjimenezg93 ฉันได้สร้างระบบพื้นฐานใน C # สำหรับการทดสอบมันใช้งานได้! ดูว่าคุณสามารถเพิ่มอะไรลงไปได้ไหม:

public interface IItem
{
    List<IAttribute> Components { get; set; }

    void ReceiveMessage<T>(T message);
}

public interface IAttribute
{
    IItem source { get; set; }
    void ReceiveMessage<T>(T message);
}

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


public class TestItem : IItem
{
    private List<IAttribute> _components = new List<IAttribute>();
    public List<IAttribute> Components
    {
        get
        {
            return _components;
        }

        set
        {
            _components = value;
        }
    }

    public void ReceiveMessage<T>(T message)
    {
        foreach (IAttribute attribute in Components)
        {
            attribute.ReceiveMessage(message);
        }
    }
}

public class TestAttribute : IAttribute
{
    string _infoRequiredFromMessage;

    public TestAttribute(IItem source)
    {
        _source = source;
    }

    private IItem _source;
    public IItem source
    {
        get
        {
            return _source;
        }

        set
        {
            _source = value;
        }
    }

    public void ReceiveMessage<T>(T message)
    {
        TestMessage convertedMessage = message as TestMessage;
        if (convertedMessage != null)
        {
            convertedMessage.Execute();
            _infoRequiredFromMessage = convertedMessage._particularInformationThatNeedsToBePassed;
            Debug.Log("Message passed : " + _infoRequiredFromMessage);

        }
    }
} 

public class TestMessage
{
    private string _messageString;
    private int _messageInt;
    public string _particularInformationThatNeedsToBePassed;
    public TestMessage(string messageString, int messageInt, string particularInformationThatNeedsToBePassed)
    {
        _messageString = messageString;
        _messageInt = messageInt;
        _particularInformationThatNeedsToBePassed = particularInformationThatNeedsToBePassed;
    }
    //messages should not have methods, so this is here for fun and testing.
    public void Execute()
    {
        Debug.Log("Desired Execution Method: \nThis is test message : " + _messageString + "\nThis is test int : " + _messageInt);
    }
} 

นี่คือการตั้งค่าที่จำเป็น ตอนนี้เราสามารถใช้ระบบได้ (การติดตามสำหรับ Unity)

public class TestManager : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        TestItem testItem = new TestItem();
        TestAttribute testAttribute = new TestAttribute(testItem);
        testItem.Components.Add(testAttribute);
        TestMessage testMessage = new TestMessage("my test message", 1, "VERYIMPORTANTINFO");
        testItem.ReceiveMessage(testMessage);
    }

}

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


เพื่ออธิบายสิ่งต่าง ๆ : ทุกรายการในเกมจะใช้อินเทอร์เฟซ IItem และทุกแอตทริบิวต์ (ชื่อไม่ควรทำให้คุณสับสนนั่นหมายถึงคุณลักษณะของไอเท็ม / ระบบเช่นอัปเกรดหรือไม่แยแส) จะใช้ IAttribute จากนั้นเรามีวิธีการประมวลผลข้อความ (ทำไมเราต้องการข้อความจะอธิบายในตัวอย่างเพิ่มเติม) ดังนั้นในบริบทคุณสามารถแนบแอททริบิวไปยังไอเท็มและปล่อยให้ส่วนที่เหลือทำเพื่อคุณ มีความยืดหยุ่นมากเพราะคุณสามารถเพิ่ม / ลบคุณสมบัติได้อย่างง่ายดาย ดังนั้นตัวอย่างที่หลอกจะไม่ลงรอยกัน เราจะมีคลาสที่เรียกว่า Disenchantable (IAttribute) และใน Disenchant method มันจะถามหา:

  • รายการส่วนผสม (เมื่อไอเทมไม่แยแสไอเท็มใดควรมอบให้ผู้เล่น) หมายเหตุ: IItem ควรขยายให้มี ItemType, ItemID เป็นต้น
  • int resultModifier (หากคุณใช้คุณสมบัติเพิ่มความสามารถในการลดระดับคุณสามารถส่งค่า int ที่นี่เพื่อเพิ่มส่วนผสมที่ได้รับเมื่อไม่ได้ใช้งาน)
  • int failChance (หากกระบวนการที่ทำให้หมดกำลังใจมีโอกาสล้มเหลว)

เป็นต้น

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


1
คุณอาจพบแรงบันดาลใจที่มีประโยชน์ในระบบปรับเปลี่ยนที่ใช้ในเพื่อเป็นเกียรติแก่
DMGregory

@DMGregory เฮ้! ขอบคุณสำหรับลิงค์ ในขณะที่มันดูมั่งคั่งมาก แต่น่าเสียดายที่ฉันต้องการพูดคุยจริงเพื่อเข้าใจแนวคิด และฉันพยายามที่จะค้นหาการพูดคุยจริงซึ่งน่าเสียดายที่เนื้อหาของสมาชิกเท่านั้น GDCVault (495 $ ต่อปีนั้นบ้า!) (คุณสามารถค้นหาคำพูดได้ที่นี่หากคุณเป็นสมาชิก GDCVault -, -
Vandarthul

อย่างไรแนวคิด "BaseStat" ของคุณจะตัดอาวุธที่สร้างขึ้นมาได้หรือไม่?
Attackfarm

มันไม่ได้ดักคอจริงๆ แต่ไม่เหมาะสมกับบริบทในใจของฉัน มันเป็นไปได้ที่จะเพิ่ม "ไม้", 2 และ "เหล็ก", 5 เป็น BaseStat สูตรการประดิษฐ์ซึ่งจะให้ดาบ ฉันคิดว่าการเปลี่ยนชื่อ BaseStat เป็น BaseAttribute จะให้บริการที่ดีขึ้นในบริบทนี้ แต่ถึงกระนั้นระบบจะไม่สามารถให้บริการตามวัตถุประสงค์ได้ นึกถึงไอเท็มสิ้นเปลืองด้วยพลังโจมตี 5 นาที -% 50 ฉันจะส่งผ่านมันเป็น BaseStat ได้อย่างไร "Perc_AttackPower", 50 นี้จำเป็นต้องแก้ไขเป็น "ถ้าเป็น Perc ให้ใช้จำนวนเต็มเป็นเปอร์เซ็นต์" และไม่มีข้อมูลในนาที หวังว่าคุณจะได้รับในสิ่งที่ฉันหมายถึง
Vandarthul

@Attackfarm จากความคิดที่สองแนวคิด "BaseStat" นี้สามารถขยายได้โดยมีรายการของ ints แทนที่จะเป็นเพียงหนึ่ง int ดังนั้นสำหรับบัฟสิ้นเปลืองฉันสามารถจัดหา "Attack", 50, 5, 1 และ IConsumable จะค้นหาจำนวนเต็ม 3 จำนวน, 1 - ค่า, 2 - นาที, 3 - ถ้าเป็นเปอร์เซ็นต์หรือไม่ แต่มันก็รู้สึกเป็นแรงผลักดันเมื่อระบบอื่น ๆ เข้ามาและถูกบังคับให้อธิบายตัวเองเป็นอย่างดี
Vandarthul

คำตอบ:


6

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

ฉันจะอธิบายเพิ่มเติม:

ครั้งแรกของทั้งหมดที่คุณสร้างอินเตอร์เฟซและอินเตอร์เฟซIItem IComponentรายการใด ๆ ที่คุณต้องการจัดเก็บจะต้องรับช่วงจากIItemและส่วนประกอบใด ๆ ที่คุณต้องการให้มีผลกับรายการของคุณจะต้องรับช่วงจากIComponentที่คุณต้องการที่จะส่งผลกระทบต่อสินค้าของคุณจะต้องสืบทอดมาจาก

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

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

ฉันมีการนำไปใช้กับ ECS ด้วยการส่งข้อความที่นี่แต่สิ่งนี้ใช้สำหรับเอนทิตีแทนที่จะเป็นรายการและใช้ C ++ อย่างไรก็ตามผมคิดว่ามันสามารถช่วยถ้าคุณดูที่component.h, entity.hและmessages.hและมีหลายสิ่งหลายอย่างที่ต้องปรับปรุง แต่มันก็ใช้ได้กับฉันในงานมหาวิทยาลัยที่เรียบง่าย

หวังว่ามันจะช่วย


สวัสดี @ jjimenezg93 ขอบคุณสำหรับคำตอบของคุณ ดังนั้นเพื่ออธิบายรายละเอียดด้วยตัวอย่างง่ายๆของสิ่งที่คุณอธิบาย: เราต้องการดาบนั่นคือ: - แยกส่วน [ส่วนประกอบ] - ตัวดัดแปลงสถิติ [ส่วนประกอบ] - อัปเกรด [ส่วนประกอบ] ฉันมีรายการการกระทำที่มีทุกสิ่งเช่น - DISENCHANT - MODIFY_STAT - อัปเกรดเมื่อใดก็ตามที่รายการได้รับข้อความนี้ให้ทำตามส่วนประกอบทั้งหมดแล้วส่งข้อความนี้ส่วนประกอบแต่ละส่วนจะรู้ว่าจะทำอย่างไรกับข้อความที่กำหนด ในทางทฤษฎีสิ่งนี้ดูยอดเยี่ยม! ฉันไม่ได้ตรวจสอบตัวอย่างของคุณ แต่ฉันจะขอบคุณมาก!
Vandarthul

@Vandarthul ใช่นั่นเป็นความคิดพื้นฐาน วิธีนี้ไอเท็มจะไม่รู้อะไรเลยเกี่ยวกับส่วนประกอบของมันดังนั้นจึงไม่มีการเชื่อมต่อเลยและในเวลาเดียวกันมันก็จะมีฟังก์ชั่นทั้งหมดที่คุณต้องการซึ่งยังสามารถแชร์ในรายการประเภทต่าง ๆ ได้ ฉันหวังว่ามันจะเหมาะกับความต้องการของคุณ!
jjimenezg93
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.