堅牢なアイテムシステムの作成


12

私の目的は、次のようなものを処理できる、可能な限り一般的なアイテムシステムを作成することです。

  • アップグレード可能なアイテム(+6カタナ)
  • ステータス修飾子(+15器用さ)
  • アイテムモディファイア(Yダメージを与える確率%X、フリーズする確率)
  • 充電アイテム(30回使用の魔法使い)
  • セットアイテム(4セットのXセットを装備してY機能を有効化)
  • レアリティ(共通、ユニーク、伝説)
  • 劣化(一部のクラフト材料に分割)
  • 製作可能(特定の材料で製作可能)
  • 消耗品(5%%X攻撃力、回復+15 hp)

※以下の設定で太字になっている機能は解決できました。

今、私は私が考えていることを反映するために多くのオプションを追加しようとしました。必要な機能をすべて追加するつもりはありませんが、必要に応じて実装できるようにしたいと考えています。これらは、在庫システムおよびデータのシリアル化とも互換性がある必要があります。

私は継承をまったく使用、エンティティコンポーネント/データ主導のアプローチを使用することを計画しています。最初は、次のようなシステムについて考えました。

  • BaseStat:外出先で統計を保持する汎用クラス(アイテムとキャラクターの統計にも使用できます)
  • 項目:リスト、名前、項目タイプ、およびui、actionName、説明などに関連するものなどのデータを保持するクラス。
  • IWeapon:武器のインターフェース。すべての武器には、IWeaponが実装された独自のクラスがあります。これには、Attackとキャラクター統計への参照があります。武器が装備されている場合、そのデータ(アイテムクラスのstat)がキャラクターstatに挿入されます(BaseStatが何であれ、それはStatボーナスとしてキャラクタークラスに追加されます)したがって、たとえば、剣( jsonを使用してアイテムクラスを作成する)ため、剣はキャラクターのステータスに5攻撃を追加します したがって、BaseStatは( "Attack"、5)になります(enumも使用できます)。このステータスは、装備するとBonusStat(別のクラスになる)としてキャラクターの "攻撃"ステータスに追加されます。したがって、SwordIWeaponを実装するという名前のクラスが作成されます。アイテムクラスが作成されます。そのため、この剣にキャラクターのステータスを挿入し、攻撃するときに、キャラクターのステータスから合計の攻撃ステータスを取得し、攻撃方法でダメージを与えることができます。
  • BonusStat:BaseStatに触れずに統計をボーナスとして追加する方法です。
  • IConsumable:IWeaponと同じロジック。直接統計の追加はかなり簡単(+15 hp)ですが、この設定で一時的な武器を追加するかどうかはわかりません(%xで5分間攻撃する)。
  • IUpgradeable:これは、このセットアップで実装できます。私は武器をアップグレードするときに増加する基本ステータスとしてUpgradeLevelを考えています。アップグレードすると、武器のBaseStatを再計算して、アップグレードレベルに一致させることができます。

この時点まで、システムはかなり良いと思います。しかし、他の機能については、他に何かが必要だと思います。たとえば、BaseStatはこの機能を処理できず、これが行き詰まったため、Craftable機能をこれに実装できないからです。すべての材料をスタットとして追加できますが、それは意味がありません。

これを簡単に投稿できるようにするために、次の質問を参考にしてください。

  • このセットアップを続行して他の機能を実装する必要がありますか?継承なしでそれは可能でしょうか?
  • 継承なしでこれらの機能をすべて実装するために、あなたが考えることができる方法はありますか?
  • アイテムモディファイアについて、どうすればそれを達成できますか?それはその性質上非常に一般的だからです。
  • この種のアーキテクチャを構築するプロセスを容易にするために何ができるか、推奨事項はありますか?
  • この問題に関連する掘り出し物はありますか?
  • 私は本当に継承を避けようとしていますが、継承をかなり維持できるように維持しながら、継承によって簡単に解決/達成できると思いますか?

さまざまな側面や人から知識を得ることができるように質問を非常に広くしているので、1つの質問に自由に答えてください。


編集


@ 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メソッドでは、次のように要求します。

  • 材料の一覧表示(アイテムが劣化している場合、どのアイテムをプレーヤーに与える必要があるか)注:IItemは、ItemType、ItemIDなどを持つように拡張する必要があります。
  • int resultModifier(あなたが一種のブースト劣化機能を実装する場合、ここにintを渡して劣化時に受け取る成分を増やすことができます)
  • int failureChance(劣化プロセスに失敗の可能性がある場合)

これらの情報はDisenchantManagerと呼ばれるクラスによって提供され、アイテムを受け取り、アイテム(劣化したときのアイテムの成分)とプレーヤーの進行状況(resultModifierとfailureChance)に従ってこのメッセージを形成します。このメッセージを渡すために、このメッセージの本文として機能するDisenchantMessageクラスを作成します。したがって、DisenchantManagerはDisenchantMessageを入力し、それをアイテムに送信します。アイテムはメッセージを受信し、添付されているすべての属性に渡します。DisenchantableクラスのReceiveMessageメソッドはDisenchantMessageを探すため、Disenchantable属性のみがこのメッセージを受信して​​処理します。これで私と同じように問題が解決することを願っています:)


1
あなたには、いくつかの有用なインスピレーションを見つけることのために名誉で使用される修飾システム
DMGregory

@DMGregoryこんにちは!リンクをありがとう。とても機知に富んでいるようですが、残念ながらコンセプトを理解するためには実際の話が必要です。そして、私は実際の話を見つけようとしますが、それは残念ながらGDCVaultのメンバー限定コンテンツです(年間495ドルは非常識です!)。(GDCVaultメンバーシップをお持ちの場合、ここで講演を見つけることができます---
Vandarthul

正確には、「BaseStat」のコンセプトはクラフト可能な武器をどのように排除するのでしょうか?
Attackfarm

それは本当に排除するものではありませんが、私の心の中の状況に実際には適合しません。「Wood」、2と「Iron」、5をBaseStatとしてクラフトレシピに追加すると、剣が生成されます。BaseStatの名前をBaseAttributeに変更すると、このコンテキストでより効果的になると思います。しかし、それでもシステムはその目的を果たすことができません。5分-50%の攻撃力を持つ消耗品について考えてください。どのようにしてBaseStatとして渡しますか?「Perc_AttackPower」、50これは「Percの場合は整数をパーセントとして扱う」として解決する必要があり、分の情報がありません。意味がわかればいいのに
Vandarthul 2017

@Attackfarmは考え直しましたが、この「BaseStat」の概念は、1つのintではなくintのリストを持つことで拡張できます。したがって、消費可能バフの場合、「攻撃」を提供できます。50、5、1、およびIConsumableは、3つの整数、1。-値、2。-分、3。-パーセンテージかどうかを検索します。しかし、他のシステムが入り込み、intでのみ自分自身を説明することを強いられるので、それは衝動的に感じます。
Vandarthul 2017

回答:


6

基本的な継承を備えたエンティティコンポーネントシステムとメッセージングシステムを使用することで、スケーラビリティとメンテナンス性の観点から望むものを実現できると思います。もちろん、このシステムは私が考えることができる最もモジュール式/カスタマイズ可能/スケーラブルであることを心に留めておいてください。しかし、それはおそらく現在のソリューションよりもパフォーマンスが悪いでしょう。

さらに説明します。

まず、インターフェースIItemとインターフェースを作成しますIComponent。保存IItemするアイテムはから継承する必要があり、アイテムに影響を与えるコンポーネントはから継承する必要がありますIComponent

IItemコンポーネントの配列とを処理するメソッドがありIMessageます。この処理メソッドは、受信したメッセージを保存されているすべてのコンポーネントに送信するだけです。次に、その特定のメッセージに関心のあるコンポーネントがそれに応じて動作し、他のコンポーネントはそれを無視します。

たとえば、1つのメッセージは損傷タイプであり、攻撃者と攻撃者の両方に通知されるため、どれだけの打撃をしたかがわかり、その損傷に基づいてフューリーバーを充電することができます。または、敵のAIがヒットして2HP未満のダメージを与えると、実行を決定することができます。これらはばかげた例ですが、私が言及しているのと同じようなシステムを使用すると、この種のメカニズムのほとんどを追加するためにメッセージと適切な処理を作成する以外に何もする必要はありません。

ここにメッセージング機能付きのECSの実装がありますが、これはアイテムではなくエンティティに使用され、C ++を使用しています。とにかく、私はあなたが見てみるならば、それは助けることができると思うcomponent.hentity.hmessages.h。改善すべき点はたくさんありますが、その単純な大学の仕事ではうまくいきました。

それが役に立てば幸い。


@ jjimenezg93様、回答ありがとうございます。だから、あなたが説明したことの簡単な例で詳しく説明するために、次のような剣が必要です:-Disenchantable [Component]-Stat Modifier [Component]-Upgradeable [Component]私はすべてのようなものを含むアクションのリストを持っています-DISENCHANT-MODIFY_STAT-UPGRADEアイテムがこのメッセージを受信し、そのすべてのコンポーネントを通過してこのメ​​ッセージを送信するたびに、各コンポーネントは指定されたメッセージの処理方法を認識します。理論的には、これは素晴らしいようです!私はあなたの例をチェックしませんでしたが、本当に感謝します!
Vandarthul 2017

@Vandarthulはい、それは基本的にアイデアです。このように、アイテムはそのコンポーネントについて何も認識しないため、結合はまったくありません。同時に、必要なすべての機能を備え、さまざまなタイプのアイテム間で共有することもできます。あなたのニーズに合うことを願っています!
jjimenezg93 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.