需要C#OOP建议:什么是适当的数据结构?

时间:2011-06-23 22:17:46

标签: c# oop data-structures

好的,我需要一些可靠的OOP建议。这是设置:我有一个播放器对象,其代码可以处理移动,寻路逻辑等等。现在我需要为每个播放器分配一个变量和设置的大表。也就是说,每个玩家可以执行大约40种不同的武器动作,每种武器都有6-8个与之相关的变量。我的问题是,哪种数据结构最合适。注意,我不想为每种武器创建一个类,因为武器永远不会在我的游戏中实例化。

以下是具体内容:每位玩家都有40种不同的武器需要按名称拉出。例如:

Weapon #1: Pistol
Weapon #2: Shotgun
Weapon #3: Rifle
Weapon #4: Bow
Weapon #5: RocketLauncher

然后每个武器都分配了相同的变量集。例如:

Pistol.HasBeenUnlocked (true/false)
Pistol.Priority (1-40)
Pistol.AmmoQuantityMax (0-10)
Pistol.AmmoQuantityCurrent (0-10)
Pistol.UIButtonState ("Hidden", "Selected", "Deselected", "CoolOff")
Pistol.CoolOffTimerMax (0.0-5.0)
Pistol.CoolOffOn (true/false)

现在我想要某种数据结构来组织所有这些变量并使它们易于访问。例如,我希望能够循环遍历所有40种武器并根据它们是否被特定玩家解锁而为它们生成按钮。这就是我想要做的事情:

for (int i = 0; i < Player.Weapons.NumberOfWeaponsTotal(); i++) {
     if (Player.Weapons.Weapon[i].UIButtonState == "Hidden"){
          // don't create a UI button to fire weapon
     }
     else {
          // create a UI button to fire weapon
     }
}

不同武器的变量名称将相同。所以Weapons.Pistol.UIButtonState将存在,Weapons.Rifle.UIButtonState也将存在。

那么组织这个的最佳方法是什么? 2D阵列并不漂亮。结构似乎不够健壮。一堂课看起来很奇怪。我从来没有用过这类东西。我想确保这与玩家有某种联系,所以当他被摧毁时,这个“对象”也是如此。

3 个答案:

答案 0 :(得分:4)

我认为课程对于这种情况来说是理想的。创建一个IWeapon接口,该接口具有公共属性和方法,如NameGetAmmoCount()IsEnabled等。然后为具有所有特定属性的每种类型的武器创建类。 (但是如果你有8种不同的手枪具有相同的方法和属性,你可以创建一个通用的Pistol类,并相应地设置变量。)

Player对象中,拥有List<IWeapon>来持有玩家拥有的所有武器。

Player被销毁时,只需调用列表中的.Empty()方法即可将其全部删除。

持有的武器将是Player类的另一个成员:IWeapon ActiveWeapon

您可以执行以下操作:

if(ActiveWeapon.GetAmmoCount() > 0) ActiveWeapon.Fire());

或者:

this.ActiveWeapon = new Pistol("LaserPistol");
this.Weapons.Add(this.ActiveWeapon);

this.ActiveWeapon = new AssaultRifle();

这里的优点是你的Player不需要确切知道它们在运行之前可能具有哪种类型的武器,你可以轻松地将它们换成其他武器。

修改:我找到了一个使用Strategy design pattern提供此示例的页面:http://hnaser.blogspot.com/2009/07/abc-of-design-patterns.html

答案 1 :(得分:1)

定义一个结构或类类型,其中包含所有不同武器具有的所有属性。创建此武器类型的数组[40]并将其存储为播放器类型的属性。定义一个int的枚举,用作武器数组中的命名索引。 Pistol = 0,Shotgun = 1等。使用这些枚举名称索引代码中的武器数组。

在极少数情况下,当您需要通过字符串名称查找武器时,您可以直接扫描武器阵列,直到找到名称与您要查找的武器相匹配的武器。捕获该武器的索引并丢弃该字符串名称,以便所有进一步的操作都通过索引数组。通过40个项目进行线性扫描的成本不会很明显。

如果你真的必须避免扫描列表中的匹配名称,你可以构造一个Dictionary来提供按名称快速查找。您希望字典条目返回特定播放器的武器,因此您需要为每个播放器使用不同的字典实例,并将其存储在播放器对象的字段中。如果简单扫描就足够了(几乎可以肯定),请不要理会字典方法。

无论您是将武器类型定义为结构还是类,它都是结构化类型,并且每次使用它时都会创建此类型的实例。每位玩家40个实例。类和结构体之间的主要区别在于分配内存的位置和方式以及赋值是否复制值。如果你小心从不将数组中的武器分配到本地武器变量中,并且你不使用上面的字典或者做任何其他需要共享对相同武器数据的引用的东西,那么struct应该没问题。如有疑问,请使用class。

当玩家离开时你不必担心武器会被处理掉 - 只要武器阵列是唯一一个长期参考武器的东西,那么它们都将在玩家被处置。适当的封装和最小化不同对象之间的交叉引用将有助于此。

如果您确定您所选择的武器将始终具有相同的变量和相同的动作(方法),那么您可以使用单一武器类型来获取所有武器,并且该类型可以是结构或者类。

如果你开始遇到这种行为对于那种武器的行为不同的特殊情况,或者这种武器具有其他任何武器都不需要的额外数据,那么你应该考虑定义一个基础武器类以及专门化的后代。需要的。

答案 2 :(得分:1)

<强>声明

您可以使用这样的模型来满足您的需求。请记住,与任何设计一样,它可能包含缺陷。它旨在帮助您解决问题,而不是解决方案。

<强>武器

此类代表您应用中的武器。 请注意重写方法,以便正确实现比较/搜索/索引。

public class Weapon
{
    public Weapon(WeaponName name)
    {
        Name = name;
    }

    public WeaponName Name { get; set; }
    public int Priority { get; set; }
    public int MaxAmmoQuantity { get; set; }
    public int CurrentAmmoQuantity { get; set; }

    public override string ToString()
    {
        return Enum.GetName(typeof(WeaponName), Name);
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;

        var other = obj as Weapon;
        if (other == null) return false;

        return Name == other.Name;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

<强> WeaponName

武器名称的固定选项(假设,如你所说,它们的选项有限)

public enum WeaponName
{
    Pistol,
    Shotgun,
    Rifle,
    Bow,
    RocketLauncher
}

<强>播放器

请注意:

  • 在内部,您可以将武器存储在您喜欢的结构中,但对于外部世界,只发布IEnumerable实例
  • 它表示玩家可以抓取武器或更改活动武器(此处可以添加所需的任何动作)。通过事件,玩家可以向外界传达他的行为。
  • 您正在为对象方法提供语义。而不是player.Weapons.Add你明确了玩家的行为。

    公共类播放器 {     private readonly IList _ownedWeapons = new List();     受保护的武器ActiveWeapon {get;私人集; }

    public IEnumerable<WeaponName> OwnedWeapons { get; set; }
    
    public event EventHandler<WeaponEventArgs> WeaponGrabbed;
    public event EventHandler<WeaponEventArgs> ActiveWeaponChanged;
    
    public void InvokeActiveWeaponChanged()
    {
        var handler = ActiveWeaponChanged;
        if (handler != null) handler(this, new WeaponEventArgs(ActiveWeapon));
    }
    public void InvokeWeaponGrabbed(Weapon weapon)
    {
        var handler = WeaponGrabbed;
        if (handler != null) handler(this, new WeaponEventArgs(weapon));
    }
    
    public void SwitchWeapon(WeaponName weaponName)
    {
        ActiveWeapon = _ownedWeapons.Where(weapon => weapon.Name == weaponName).First();
        InvokeActiveWeaponChanged();
    }
    public void Grab(Weapon weapon)
    {
        _ownedWeapons.Add(weapon);
        InvokeWeaponGrabbed(weapon);
    }
    

    }

    公共类WeaponEventArgs:EventArgs     {         public WeaponEventArgs(武器武器)         {             武器=武器;         }

        public Weapon Weapon { get; set; }
    }
    

<强> WeaponsRepository

  • 集中所有关于武器玩家的知识可以在您的应用中使用,在构造函数中注册可用的武器名称及其创建。
  • 此外,它还提供了创建和查询可用武器的方法。
  • 您可以使用WeaponsNotOwnedBy方法判断玩家尚未拥有的武器。

    公共类WeaponRepository {     readonly Dictionary&gt; _availableWeapons =新词典&gt;();

    public WeaponRepository()
    {
        _availableWeapons.Add(WeaponName.Pistol, () => new Weapon(WeaponName.Pistol) );
        _availableWeapons.Add(WeaponName.Shotgun, () => new Weapon(WeaponName.Shotgun) );
    }
    
    public Weapon Create(WeaponName name)
    {
        return _availableWeapons[name]();
    }
    
    public IEnumerable<WeaponName> AvailableWeapons()
    {
        return Enum.GetValues(typeof (WeaponName)).Cast<WeaponName>();
    }
    
    public IEnumerable<WeaponName> WeaponsNotOwnedBy(Player player)
    {
        return AvailableWeapons().Where(weaponName => !player.OwnedWeapons.Contains(weaponName));
    }
    

    }

<强>用法

  • 你可以帮助一个班级负责建立和更新玩家的工具栏。
  • 使用BuildToolbar方法中WeaponRepository中的AvailableWeapons方法为每个武器创建一个按钮。
  • 在此课程中,订阅玩家的活动。通过这种方式,您可以根据需要启用/禁用/突出显示活动武器。

注意:我为了简洁而省略了WeaponButton

public class WeaponToolbarBuilder
{
    private readonly Player _player;
    private readonly WeaponRepository _weaponRepository;
    private List<WeaponButton> _buttons = new List<WeaponButton>();

    public WeaponToolbarBuilder(Player player, WeaponRepository weaponRepository)
    {
        _player = player;
        _weaponRepository = weaponRepository;

        _player.ActiveWeaponChanged += _player_ActiveWeaponChanged;
        _player.WeaponGrabbed += _player_WeaponGrabbed;
    }

    void _player_WeaponGrabbed(object sender, WeaponEventArgs e)
    {
        var weaponButton = _buttons.Where(button => button.WeaponName == e.Weapon.Name).First();
        weaponButton.Enable();
    }

    void _player_ActiveWeaponChanged(object sender, WeaponEventArgs e)
    {
        var currentActiveButton = _buttons.Where(button => button.Highlighted).First();
        currentActiveButton.Highlight(false);

        var newActiveButton = _buttons.Where(button => button.WeaponName == e.Weapon.Name);
        newActiveButton.Highlight(true);
    }

    public void BuildToolbar() { ... }
}