私の目的は、次のようなものを処理できる、可能な限り一般的なアイテムシステムを作成することです。
- アップグレード可能なアイテム(+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(別のクラスになる)としてキャラクターの "攻撃"ステータスに追加されます。したがって、SwordはIWeaponを実装するという名前のクラスが作成されます。アイテムクラスが作成されます。そのため、この剣にキャラクターのステータスを挿入し、攻撃するときに、キャラクターのステータスから合計の攻撃ステータスを取得し、攻撃方法でダメージを与えることができます。
- 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属性のみがこのメッセージを受信して処理します。これで私と同じように問題が解決することを願っています:)