Tạo một hệ thống vật phẩm mạnh mẽ


12

Mục đích của tôi là tạo ra một hệ thống vật phẩm mô đun / chung chung nhất có thể để xử lý những việc như:

  • Vật phẩm có thể nâng cấp (+6 Katana)
  • Công cụ điều chỉnh Stat (+15 khéo léo)
  • Công cụ sửa đổi vật phẩm (% X cơ hội gây sát thương Y, cơ hội đóng băng)
  • Vật phẩm có thể sạc lại (Nhân viên ma thuật với 30 lần sử dụng)
  • Đặt Mục (Trang bị 4 phần X được đặt để kích hoạt tính năng Y)
  • Hiếm khi (phổ biến, độc đáo, huyền thoại)
  • Không thể tháo rời (đột nhập vào một số vật liệu chế tạo)
  • Craftable (có thể được chế tạo bằng vật liệu nhất định)
  • Tiêu hao (5 phút% sức tấn công X, hồi máu +15 hp)

* Tôi đã có thể giải quyết các tính năng được in đậm trong thiết lập sau.

Bây giờ tôi đã cố gắng thêm nhiều tùy chọn để phản ánh những gì tôi có trong đầu. Tôi không có kế hoạch thêm tất cả các tính năng cần thiết này, nhưng tôi muốn có thể triển khai chúng khi tôi thấy phù hợp. Chúng cũng phải tương thích với hệ thống kiểm kê và tuần tự hóa dữ liệu.

Tôi dự định hoàn toàn không sử dụng kế thừa mà thay vào đó là cách tiếp cận dựa trên dữ liệu thành phần / thực thể. Ban đầu tôi nghĩ về một hệ thống có:

  • BaseStat: một lớp chung chứa các số liệu thống kê khi đang di chuyển (cũng có thể được sử dụng cho các mục và thống kê nhân vật)
  • Item: một lớp chứa dữ liệu như danh sách, tên, itemtype và những thứ có liên quan đến ui, actionName, mô tả, v.v.
  • IWeapon: giao diện cho vũ khí. Mỗi vũ khí sẽ có lớp riêng với IWeapon được triển khai. Điều này sẽ có Tấn công và tham chiếu đến chỉ số nhân vật. Khi vũ khí được trang bị, dữ liệu của nó (chỉ số của lớp vật phẩm) sẽ được đưa vào chỉ số nhân vật (bất kể BaseStat nào, nó sẽ được thêm vào lớp nhân vật dưới dạng phần thưởng Stat) Vì vậy, ví dụ, chúng tôi muốn tạo ra một thanh kiếm (nghĩ đến tạo các lớp vật phẩm với json) vì vậy thanh kiếm sẽ thêm 5 đòn tấn công vào chỉ số nhân vật. Vì vậy, chúng tôi có BaseStat là ("Tấn công", 5) (chúng tôi cũng có thể sử dụng enum). Chỉ số này sẽ được thêm vào chỉ số "Tấn công" của nhân vật dưới dạng BonusStat (sẽ là một lớp khác) khi trang bị nó. Vì vậy, một lớp có tên Sword thực hiện IWeapon sẽ được tạo khi nó 'Lớp vật phẩm được tạo ra. Vì vậy, chúng ta có thể tiêm các chỉ số nhân vật vào thanh kiếm này và khi tấn công, nó có thể lấy tổng chỉ số Tấn công từ chỉ số nhân vật và gây sát thương trong phương thức Tấn công.
  • BonusStat: là cách thêm số liệu thống kê dưới dạng tiền thưởng mà không cần chạm vào BaseStat.
  • IConsumable: Logic tương tự như với IWeapon. Thêm chỉ số trực tiếp khá dễ dàng (+15 hp) nhưng tôi không chắc chắn về việc thêm vũ khí tạm thời với thiết lập này (% x để tấn công trong 5 phút).
  • IUpgradizable: Điều này có thể được thực hiện với thiết lập này. Tôi đang nghĩ Nâng cấp như một chỉ số cơ bản, được tăng lên khi nâng cấp vũ khí. Khi được nâng cấp, chúng ta có thể tính lại BaseStat của vũ khí để phù hợp với cấp độ Nâng cấp của nó.

Cho đến thời điểm này, tôi có thể thấy hệ thống đó khá tốt. Nhưng đối với các tính năng khác, tôi nghĩ rằng chúng tôi cần một cái gì đó khác, vì ví dụ tôi không thể triển khai tính năng Craftable vào điều này vì BaseStat của tôi sẽ không thể xử lý tính năng này và đây là nơi tôi gặp khó khăn. Tôi có thể thêm tất cả các thành phần dưới dạng Stat nhưng điều đó sẽ không có ý nghĩa.

Để giúp bạn dễ dàng đóng góp điều này, đây là một số câu hỏi mà bạn có thể giúp đỡ:

  • Tôi có nên tiếp tục với thiết lập này để thực hiện các tính năng khác? Nó sẽ có thể mà không cần thừa kế?
  • Có cách nào bạn có thể nghĩ ra, để thực hiện tất cả các tính năng này mà không cần kế thừa không?
  • Về sửa đổi vật phẩm, làm thế nào người ta có thể đạt được điều đó? Bởi vì nó rất chung chung trong bản chất của nó.
  • Có thể làm gì để giảm bớt quá trình xây dựng loại kiến ​​trúc này, có khuyến nghị nào không?
  • Có nguồn nào tôi có thể đào có liên quan đến vấn đề này không?
  • Tôi thực sự cố gắng tránh thừa kế, nhưng bạn có nghĩ rằng những điều này sẽ được giải quyết / đạt được với sự kế thừa một cách dễ dàng trong khi vẫn giữ nó khá bền?

Hãy trả lời chỉ một câu hỏi vì tôi giữ câu hỏi rất rộng để tôi có thể nhận được kiến ​​thức từ các khía cạnh / người khác nhau.


BIÊN TẬP


Theo câu trả lời của @ jjimenezg93, tôi đã tạo ra một hệ thống rất cơ bản trong C # để thử nghiệm, nó hoạt động! Xem nếu bạn có thể thêm bất cứ điều gì vào nó:

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);
}

Cho đến nay, IItem và IAttribution là các giao diện cơ bản. Không có nhu cầu (mà tôi có thể nghĩ ra) để có một giao diện / thuộc tính cơ bản cho tin nhắn, vì vậy chúng tôi sẽ trực tiếp tạo một lớp thông báo thử nghiệm. Bây giờ cho các lớp kiểm tra:


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);
    }
} 

Đây là những thiết lập cần thiết. Bây giờ chúng tôi có thể sử dụng hệ thống (Sau đây là cho 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);
    }

}

Đính kèm tập lệnh TestManager này vào một thành phần trong cảnh và bạn có thể thấy trong gỡ lỗi thông báo đó được truyền thành công.


Để giải thích mọi thứ: Mọi vật phẩm trong trò chơi sẽ triển khai giao diện IItem và mọi Thuộc tính (tên không nên làm bạn bối rối, điều đó có nghĩa là tính năng / hệ thống vật phẩm. Giống như Nâng cấp hoặc không thể gỡ bỏ) sẽ triển khai IAttribution. Sau đó, chúng tôi có một phương pháp để xử lý tin nhắn (tại sao chúng tôi cần tin nhắn sẽ được giải thích trong ví dụ tiếp theo). Vì vậy, trong ngữ cảnh, bạn có thể đính kèm các thuộc tính cho một mục và để phần còn lại làm cho bạn. Điều này rất linh hoạt, bởi vì bạn có thể thêm / xóa các thuộc tính một cách dễ dàng. Vì vậy, một ví dụ giả sẽ không thể chấp nhận được. Chúng ta sẽ có một lớp gọi là Disenchantable (IAttribution) và nó theo phương thức Disenchant, nó sẽ yêu cầu:

  • Liệt kê các thành phần (khi vật phẩm bị loại bỏ, vật phẩm nào nên được cung cấp cho người chơi) lưu ý: IItem nên được mở rộng để có ItemType, ItemID, v.v.
  • int resultModifier (nếu bạn triển khai một loại tăng tính năng khử mùi, bạn có thể chuyển một int ở đây để tăng các thành phần nhận được khi bị loại bỏ)
  • int failChance (nếu quá trình không hài lòng có cơ hội thất bại)

Vân vân.

Những thông tin này sẽ được cung cấp bởi một lớp có tên DisenchantManager, nó sẽ nhận được vật phẩm và hình thành thông báo này theo vật phẩm (thành phần của vật phẩm khi bị loại bỏ) và tiến trình của người chơi (resultModifier và failChance). Để truyền thông điệp này, chúng tôi sẽ tạo một lớp DisenchantMessage, lớp này sẽ hoạt động như một cơ thể cho thông điệp này. Vì vậy, DisenchantManager sẽ đưa vào DisenchantMessage và gửi nó đến vật phẩm. Mục sẽ nhận được thông báo và chuyển nó đến tất cả các Thuộc tính được đính kèm. Vì phương thức receiveMessage của lớp Disenchantable sẽ tìm kiếm DisenchantMessage, nên chỉ có thuộc tính Disenchantable mới nhận được thông báo này và hành động theo nó. Hy vọng điều này sẽ xóa mọi thứ nhiều như nó đã làm cho tôi :).


1
Bạn có thể tìm thấy một số nguồn cảm hứng hữu ích trong các hệ thống sửa đổi được sử dụng trong For Honor
DMGregory

@DMGregory Này! Cảm ơn các liên kết. Mặc dù có vẻ rất tháo vát, nhưng thật không may, tôi cần nói chuyện thực tế để nắm bắt khái niệm. Và tôi cố gắng tìm hiểu cuộc nói chuyện thực tế, không may là nội dung chỉ dành cho thành viên của GDCVault (495 đô la trong một năm là điên rồ!). (Bạn có thể tìm thấy cuộc nói chuyện ở đây nếu bạn có tư cách thành viên GDCVault -, -
Vandarthul

Chính xác thì, khái niệm "BaseStat" của bạn sẽ loại trừ vũ khí có thể chế tạo như thế nào?
Attackfarm

Nó không thực sự ngăn cản nhưng không thực sự phù hợp với bối cảnh trong tâm trí của tôi. Có thể thêm "Gỗ", 2 và "Sắt", 5 làm BaseStat vào công thức chế tạo, sẽ mang lại thanh kiếm. Tôi nghĩ việc thay đổi tên của BaseStat thành BaseAttribution sẽ phục vụ tốt hơn trong bối cảnh này. Tuy nhiên, hệ thống sẽ không thể phục vụ mục đích của nó. Hãy suy nghĩ về vật phẩm tiêu hao với 5 phút -% 50 sức tấn công. Làm thế nào tôi có thể vượt qua nó như là một BaseStat? "Perc_AttackPower", 50 điều này cần được giải quyết là "nếu là Perc, hãy coi số nguyên là phần trăm" và thiếu thông tin về phút. O hy vọng bạn hiểu ý tôi.
Vandarthul

@Attackfarm với ý nghĩ thứ hai, khái niệm "BaseStat" này có thể được mở rộng với việc có một danh sách các int thay vì chỉ một int. vì vậy, đối với buff tiêu hao, tôi có thể cung cấp "Tấn công", 50, 5, 1 và IConsumable sẽ tìm kiếm 3 số nguyên, 1. - giá trị, 2. - phút, 3. - nếu đó là tỷ lệ phần trăm hay không. Nhưng nó cảm thấy bất lực khi các hệ thống khác xâm nhập và buộc phải giải thích chính họ chỉ trong int.
Vandarthul

Câu trả lời:


6

Tôi nghĩ bạn có thể đạt được những gì bạn muốn về khả năng mở rộng và khả năng bảo trì bằng cách sử dụng Hệ thống thành phần thực thể với sự kế thừa cơ bản và hệ thống nhắn tin. Tất nhiên, hãy nhớ rằng hệ thống này là mô-đun / tùy biến / có thể mở rộng nhất mà tôi có thể nghĩ đến, nhưng nó có thể sẽ hoạt động kém hơn so với giải pháp hiện tại của bạn.

Tôi sẽ giải thích thêm:

Trước hết, bạn tạo một giao diện IItemvà một giao diện IComponent. Bất kỳ mục nào bạn muốn lưu trữ phải được kế thừa từ IItemvà bất kỳ thành phần nào bạn muốn ảnh hưởng đến các mục của bạn đều phải kế thừa từ đó IComponent.

IItemsẽ có một loạt các thành phần và một phương thức để xử lý IMessage. Phương thức xử lý này chỉ đơn giản là gửi bất kỳ tin nhắn nhận được đến tất cả các thành phần được lưu trữ. Sau đó, các thành phần quan tâm đến thông điệp đã cho sẽ hành động tương ứng và các thành phần khác sẽ bỏ qua nó.

Ví dụ, một tin nhắn là loại sát thương, và nó thông báo cho cả kẻ tấn công và kẻ tấn công, vì vậy bạn biết bạn đã đánh bao nhiêu và có thể tính phí thanh giận dữ của mình dựa trên thiệt hại đó. Hoặc AI của kẻ thù có thể quyết định chạy nếu nó tấn công bạn và gây sát thương dưới 2HP. Đây là những ví dụ ngớ ngẩn nhưng sử dụng một hệ thống tương tự như tôi đang đề cập, bạn sẽ không cần phải làm gì hơn là tạo một thông điệp và các tay cầm phù hợp để thêm hầu hết các loại cơ học này.

Tôi có một triển khai cho một ECS với nhắn tin ở đây , nhưng nó được sử dụng cho các thực thể thay vì các mục và nó sử dụng C ++. Dù sao, tôi nghĩ rằng nó có thể giúp đỡ nếu bạn nhìn vào component.h, entity.hmessages.h. Có rất nhiều thứ cần phải cải thiện nhưng nó đã làm việc cho tôi trong công việc đại học đơn giản đó.

Hy vọng nó giúp.


Hey @ jjimenezg93, cảm ơn bạn đã trả lời. Vì vậy, để giải thích với một ví dụ đơn giản về những gì bạn đã giải thích: Chúng tôi muốn một thanh kiếm đó là: - Không hài lòng [Thành phần] - Công cụ sửa đổi Stat [Thành phần] - Nâng cấp [Thành phần] Tôi có một danh sách các hành động có chứa tất cả mọi thứ như - DISENCHANT - MODIFY_STAT - NÂNG CẤP Bất cứ khi nào mục nhận được thông báo này, đi qua tất cả các thành phần của nó và gửi tin nhắn này, mỗi thành phần sẽ biết phải làm gì với thông báo đã cho. Về lý thuyết, điều này có vẻ tuyệt vời! Tôi đã không kiểm tra ví dụ của bạn nhưng tôi sẽ, cảm ơn rất nhiều!
Vandarthul

@Vandarthul Vâng, về cơ bản là ý tưởng. Bằng cách này, vật phẩm sẽ không biết gì về các thành phần của nó vì vậy không có khớp nối nào cả, đồng thời, nó sẽ có tất cả các chức năng mà bạn mong muốn, cũng có thể được chia sẻ giữa các loại vật phẩm khác nhau. Tôi hy vọng nó phù hợp với nhu cầu của bạn!
jjimenezg93
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.