3

私にとってかなりトリッキーなモジュールのサウンドデザインを考え出すのに苦労しています。実際のビジネス ロジックは私をひどく混乱させたので、よりわかりやすい形に言い換えました。実際の設計は言語に依存しないと思いますが、私は C# でコーディングしています。

シンプルなファンタジー ロールプレイング ゲームをコーディングしているとします。いくつかの基本的な属性を持つプレーヤーがいます。プレイヤーはいくつかのアイテムを装備することができ、それは何らかの形で彼の属性に影響を与える可能性があります. 項目には、変更する属性を定義する 1 つ以上のプロパティがあります。

public Character
{
    public string charName;
    public int charStrength;
    public int charHitPoints;
    ...
    public List<Item> equippedItems;
}

public Item
{
    public string itemName;
    public List<ItemProperty> itemProperties;   
}

public ItemProperty
{
    public int propertyValue;
    public virtual void applyPropertyModification(Character charStatsToBeModified){}
}

文字データは XML 形式です。

<Character>
    <Name>John Doe</Name>
    <BaseStrength>10</BaseStrength>
    <BaseHitPoints>100</BaseHitPoints>
    <Items>
        <Item>
            <Name>Short Sword</Name>
            <Properties>
                <Strength>10</Strength>
                <HitPoints>200</HitPoints>
            </Properties>
        </Item>
    </Items>
<Character>

XML から文字データをロードする方法を探しています。これにより、X の一般的な新しい文字属性 (50 程度を想像してください) および/または Y の一般的な項目のプロパティ (100 ~ 150 程度を想像してください) を簡単に追加できます。

私の設計思想:

可能な各文字属性は、新しいプロパティとして Character クラスに追加されます。属性の数が大きくなると扱いにくくなる可能性があります。applyPropertyModification可能な各項目プロパティは、適切なメソッドを使用して、独自の ItemProperty サブクラスとして追加されます。

public Strength : ItemProperty
{
    public override void applyPropertyModification(Character charStatsToBeModified)
    {
        charStatsToBeModified.charStrength += this.propertyValue
    }
}

これは大量の余分なコードのように見えます。特に、オーバーライドされたメソッドは、どの文字属性を変更する必要があるかを判断するだけであるためです。

ItemProperty は、適切な ItemProperty サブクラスに対応するエレメント XML タグを使用して、リフレクションを使用して作成されます (別の方法としては、100 条件スイッチになると思います)。

私の一般的な設計は正しいですか、それとも私がこれにアプローチするのは間違った方向ですか? 私は自分でこれを理解するのに十分な知識はありませんが、これを行うためのより良い方法があると確信しています. また、この質問がまったく不明な場合、またはとりとめのない場合は、お知らせください。明確にする/言い換えるようにします。ありがとう。

4

3 に答える 3

2

ここにいる数人は正しい道を進んでいました... しかし、誰も頭に釘を打たなかったので、ここに行きます. 正しい道を歩み始めたようですが、もう少し先に進む必要があります。コードにコメントを付けてこれを書き、少し説明を加えました。

ここに行きます:

//So you have a character...
public class Character
{
    //You know you ALWAYS need Raw Attribs in a game
    private Dictionary<string, Attrib> _rawAttributes;
    //You know you will always need a record of modified attribs
    private Dictionary<string, Attrib> _modifiedAttributes;
    //And while your at it, take a light optimization to not have to recalculate everytime
    private bool _areModifiedAttribsCurrent { get; set; }
    //A character has gear! This is what makes the world go around
    public List<Equipment> Equipment { get; private set; }

    //You don't want to give public access to setting gear, this should be controlled.
    //You'll want to do more as far as remove / change... but this'll get you started
    public void AddEquipment(Equipment e)
    {
        Equipment.Add(e);
        _areModifiedAttribsCurrent = false;
    }

    //And a way to add attribs and set base values..
    //once again, you will want more but this will get you started
    public void AddAttribute(Attrib x)
    {
        _rawAttributes.Add(x.Name, x);
    }

    //Finally you want a way to fetch the modified attribs
    //Keep in mind you need to do the copy dance in the  apply to not upset your 
    //base stats.
    public Dictionary<string, Attrib> FetchModifiedAttributes()
    {
        if (!_areModifiedAttribsCurrent)
        {
            var traceAttribs = _rawAttributes;
            foreach (var e in Equipment.OrderBy(x => x.ApplyOrder))
            {
                traceAttribs = e.ApplyModifiers(traceAttribs);
            }
            _modifiedAttributes = traceAttribs;
        }

        return _modifiedAttributes;
    }
}

//Attrib, pretty simple.. Could go away but we all know attribs have effects so don't even start down that path
//You WILL need this class later on... but right now it looks pretty meaningless.
public class Attrib
{
    public string Name { get; set; }
    public decimal Value { get; set; }
}

//GEAR... yes, this is what all RPG lovers love... 
//Grind for that awesome gear!
public class Equipment
{
    //Ok so I put in some stuff unrelated to your problem but you need a name right?!
    public string Name { get; set; }
    //What order does gear effect stats... this is important if you do more than flat modifiers.
    public int ApplyOrder { get; set; }
    //What modifiers does this gear have?!
    public List<Modifier> ItemModifiers { get; set; }
    //Aha.... let the gear apply its modifiers to an attrib dictionary... I knew I loved OO for some reason
    public Dictionary<string, Attrib> ApplyModifiers(Dictionary<string, Attrib> inParams)
    {
        //Copy / Clone... Whatever you want to call it this is important as to not 
        //unintentionally screw up yoru base collection.
        var response = new Dictionary<string, Attrib>();
        foreach (var m in ItemModifiers)
        {
            //If we have this attrib, keep going
            if (inParams.ContainsKey(m.TargetName))
            {
                //If this is the first time the response ran into it, add it
                if (!response.ContainsKey(m.TargetName))
                {
                    response.Add(m.TargetName, inParams[m.TargetName]);
                }

                //And wait what's this... let the Modifier apply it!?  
                //yes... pass it down again... you'll see why in a second.
                m.Apply(response[m.TargetName]);
            }
        }

        return response;
    }
}

//A modifier... what the!?
//Yep... abstraction is key to maintainable and expandable code
public class Modifier
{
    public string TargetName { get; set; }
    public decimal ModifierValue { get; set; }
    //The other stuff is kind of pointless... but this is where the magic happens... All in a modifier type.
    public ModifierType ModifierType { get; set; }
    //Let the modifier apply it's own values... off the type... yea
    //I did that on purpose ;-)
    public void Apply(Attrib a)
    {
        a.Value = ModifierType.ApplyModifier(this, a.Value);
    }
}

//Decoration... Wonderful
//This base class gives you a interface to work with... Hell, it could be an interface but I decided
//to type abstract today.
public abstract class ModifierType
{
    public abstract string ModifierName { get; }
    public abstract decimal ApplyModifier(Modifier m, decimal InitialValue);
}

//A concrete type of ModifierType... This is what determines how the modifier value is applied.
//This gives you more flexibility than hard coding modifier types.  If you really wanted to you could
//serialize these and store lambda expressions in the DB so you not only have type driven logic, you could have
//data driven behavior.
public class FlatModifier : ModifierType
{
    //The names can be really handy if you want to expose calculations to players.
    public override string ModifierName { get { return "Flat Effect"; } }
    //And finally... let the calculation happen!  Time to bubble back up!
    public override decimal ApplyModifier(Modifier m, decimal InitialValue)
    {
        return InitialValue + m.ModifierValue;
    }
}

オブジェクトに作業を任せてください。XML から来ていることは知っていますが、それらをオブジェクトに逆シリアル化し、いくつかの選択メソッドを呼び出して処理します。これにより、C# is by reference などの煩わしい問題のいくつかを回避できます。また、冗長な処理パスがオブジェクト グラフをジャッキアップするのを防ぎます。

ご不明な点がございましたらお知らせください... 今夜は退屈です。

于 2012-06-11T23:35:11.407 に答える
1

Character何百ものプロパティ/フィールドを入力し、XML 要素名に基づいてこれらのフィールドの値を設定したいですか?

最も簡単な解決策は、単純に XML 要素を読み取り、Dictionary<string, string>値を含む を作成することです。次に、ディクショナリの各要素について、Character型がディクショナリ キーから一致するプロパティ/フィールドを公開している場合は、その値でリフレクションを介してプロパティの値を設定しようとします。別名:

Character someCharacter = // Create your character
Dictionary<string, string> myDictionary = // Load XML and transform
var properties = someCharacter.GetType().GetProperties();

foreach(KeyValuePair<string, string> kvp in myDictionary)
{
    var matchingProperty = properties.FirstOrDefault(x => x.PropertyName.ToLower() == kvp.Key.ToLower());

    if(matchingProperty != null)
    {
        matchingProperty.SetValue(character, kvp.Value, null); // make sure to type convert if necesary here, since kvp.Value is a string, etc
    }
}

一度コードを記述すれば、XML / オブジェクトにプロパティを追加して、必要に応じてオーバーライドできます。

于 2012-06-11T21:53:20.563 に答える
1

反射を使用する代わりに。このようなものに一致するようにxmlを変更します

<Items>
  <Item name="Short Sword">
    <Properties>
      <Property name="Strength" type="int">10<Property>               
      <Propery name="HitPoints" type="int">200</HitPoints>
    </Properties>
  </Item>
</Items>

属性に基づいて解析しDictionary<String,Dynamic>、インデクサーを実装します

例えば

private Dictionary<String, Dynamic> _properties = new Dictionary<String,Dynamic>();

public dynamic this[Strng argIndex]
{
  get {return _properties[argIndex];}
}

これをキャラクターなどに持ち込むと、次のような楽しいことができます

characters["Fred"].Hitpoints = characters["Fred"].Items["Short Sword"].Hitpoints * characters["Fred"].Experience["Blades"]

Dictionary<String,Dynamic>プロパティバッグ クラス ( thingy)を定義したら、プロパティをプロパティバッグにすることができます。

基本的にこれは .net のバージョンの Ducktyping を使用しており、遅延バインディングを使用しているため、少し遅くなりますが、実行時に代償を払いたい場合は、非常に柔軟です。

于 2012-06-11T22:17:03.433 に答える