简体   繁体   English

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

[英]Need C# OOP Advice: What is the Proper Data-Structure for This?

Okay, I need some solid OOP advice. 好的,我需要一些可靠的OOP建议。 Here's the setup: I have a player object with code that handles things like movement, pathfinding logic, etc. Now I need to assign each player a large table of variables and settings. 这是设置:我有一个播放器对象,其代码可以处理移动,寻路逻辑等等。现在我需要为每个播放器分配一个变量和设置的大表。 Namely, each player has about 40 different weapon actions he can perform, and each weapon has 6-8 variables associated with it. 也就是说,每个玩家可以执行大约40种不同的武器动作,每种武器都有6-8个与之相关的变量。 My question is, which data-structure is most appropriate. 我的问题是,哪种数据结构最合适。 Note, I don't want to create a class for each weapon because weapons are never instantiated in my game. 注意,我不想为每种武器创建一个类,因为武器永远不会在我的游戏中实例化。

Here are the specifics: Each player has 40 different weapons that need to be pulled by name. 以下是具体内容:每位玩家都有40种不同的武器需要按名称拉出。 For example: 例如:

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

Each weapon then has the same set of variables assigned to it. 然后每个武器都分配了相同的变量集。 For example: 例如:

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)

Now I want some sort of data-structure that organizes all these variables and makes them easy to access. 现在我想要某种数据结构来组织所有这些变量并使它们易于访问。 For example, I want to be able to loop through all 40 weapons and generate buttons for them based on whether they were unlocked by a particular player. 例如,我希望能够循环遍历所有40种武器并根据它们是否被特定玩家解锁而为它们生成按钮。 Here's what I want to do: 这就是我想要做的事情:

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

Variable names across the different weapons will be the same. 不同武器的变量名称是相同的。 So Weapons.Pistol.UIButtonState will exist, as will Weapons.Rifle.UIButtonState. 所以Weapons.Pistol.UIButtonState将存在,Weapons.Rifle.UIButtonState也将存在。

So what's the best way to organize this? 那么组织这个的最好方法是什么? A 2D array isn't pretty. 2D阵列并不漂亮。 A structure doesn't seem robust enough. 结构似乎不够健壮。 A class sees strange. 一堂课看起来很奇怪。 I've never used classes for this sort of thing. 我从来没有用过这类东西。 I want to make sure this is somehow linked to the player, so when he's destroyed, so is this "object". 我想确保这与玩家有某种联系,所以当他被摧毁时,这个“对象”也是如此。

I think classes would be ideal for this scenario. 我认为课程对于这种情况是理想的。 Create an IWeapon interface that has the common properties and methods like Name , GetAmmoCount() , IsEnabled , etc. Then create classes for each type of weapon with all the specific properties. 创建一个IWeapon接口,它具有公共属性和方法,如NameGetAmmoCount()IsEnabled等。然后为每种类型的武器创建具有所有特定属性的类。 (But if you have 8 different pistols with the same methods and properties, you could create a generic Pistol class and just set the variables accordingly). (但是如果你有8种不同的手枪具有相同的方法和属性,你可以创建一个通用的Pistol类,并相应地设置变量)。

In the Player object, have a List<IWeapon> to hold all the weapons the player has. Player对象中,有一个List<IWeapon>来保存玩家拥有的所有武器。

When the Player is destroyed, just call the .Empty() method on the List to remove them all. Player被销毁后,只需调用List上的.Empty()方法即可将其全部删除。

The weapon being held would be an additional member of the Player class: IWeapon ActiveWeapon . 持有的武器将是Player类的另一个成员: IWeapon ActiveWeapon

You can do things like: 你可以这样做:

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

Or: 要么:

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

this.ActiveWeapon = new AssaultRifle();

The advantage here is that your Player doesn't have to know exactly which types of weapons they might have until runtime, and you can easily swap them for other ones. 这里的优点是你的Player在运行之前不必确切知道他们可能拥有哪种类型的武器,你可以轻松地将它们换成其他武器。

Edit: I found a page that gives an example of this using the Strategy design pattern : http://hnaser.blogspot.com/2009/07/abc-of-design-patterns.html 编辑:我找到了一个页面,使用策略设计模式给出了一个示例: http//hnaser.blogspot.com/2009/07/abc-of-design-patterns.html

Define a struct or a class type that contains all the properties that all the different weapons have. 定义包含所有不同武器具有的所有属性的结构或类类型。 Create an array[40] of this weapon type and store it as a property on the player type. 创建此武器类型的数组[40]并将其存储为播放器类型的属性。 Define an enum of ints to use as named indices into the weapons array. 定义一个int的枚举,用作武器数组中的命名索引。 Pistol = 0, Shotgun = 1, etc. Use these enum names to index into the weapons array in your code. Pistol = 0,Shotgun = 1等。使用这些枚举名称索引代码中的武器数组。

On the rare occasion when you do need to look up a weapon by string name, you can just scan through the weapons array until you find the weapon whose name matches what you're looking for. 在极少数情况下,当您需要通过字符串名称查找武器时,您可以直接扫描武器阵列,直到找到名称与您要查找的武器相匹配的武器。 Capture the index of that weapon and throw away the string name, so that all further operations are by indexed array. 捕获该武器的索引并丢弃该字符串名称,以便所有进一步的操作都通过索引数组。 The cost of a linear scan through 40 items will not be noticeable. 通过40个项目进行线性扫描的成本不会很明显。

If you really do absolutely have to avoid scanning the list for a matching name, you could construct a Dictionary to provide fast lookup by name. 如果您确实必须避免扫描列表中的匹配名称,则可以构造一个Dictionary以按名称提供快速查找。 You'll want the dictionary entries to return the weapons of a particular player, so you'll need a different dictionary instance for each player, and store it in a field of the player object. 您希望字典条目返回特定播放器的武器,因此您需要为每个播放器使用不同的字典实例,并将其存储在播放器对象的字段中。 If the simple scan is sufficient (it almost certainly is), don't bother with the dictionary approach. 如果简单扫描就足够了(几乎可以肯定),请不要理会字典方法。

Regardless of whether you define the weapon type as a struct or a class, it is a structured type and you are creating instances of this type every time you use it. 无论您是将武器类型定义为结构还是类,它都是结构化类型,并且每次使用它时都会创建此类型的实例。 40 instances per player. 每位玩家40个实例。 The main difference between class and struct is where/how the memory is allocated and whether assignment copies the values or not. 类和结构体之间的主要区别在于分配内存的位置和方式以及赋值是否复制值。 If you are careful to never assign a weapon from the array into a local weapon variable, and you don't use the dictionary thing above or do anything else that needs to share references to the same weapon data, then struct should be fine. 如果你小心从不将数组中的武器分配到本地武器变量中,并且你不使用上面的字典或者做任何其他需要共享对相同武器数据的引用的东西,那么struct应该没问题。 If in doubt, use class. 如有疑问,请使用课程。

You don't need to worry about the weapons getting disposed of when the player goes away - as long as the weapons array is the only thing that keeps a long term reference to the weapons, then they will all be disposed when the player is disposed. 当玩家离开时你不必担心武器会被处理掉 - 只要武器阵列是唯一可以长期参考武器的东西,那么当玩家被处置时它们都会被处理掉。 Proper encapsulation and minimizing cross-references between different objects will help here. 适当的封装和最小化不同对象之间的交叉引用将有助于此。

If you are certain that your selection of weapons will always have the same variables and the same actions (methods), then you can get by with a single weapon type for all the weapons, and that type can be a struct or a class. 如果您确定您选择的武器将始终具有相同的变量和相同的动作(方法),那么您可以使用单一武器类型来获取所有武器,并且该类型可以是结构或类。

If you start getting into special cases where this action behaves differently for that weapon, or this weapon has additional data that none of the other weapons need, then you should look at defining a base weapon class and descendents of that for specialization as needed. 如果你开始遇到这种行为对于那种武器的行为不同的特殊情况,或者这种武器具有其他任何武器都不需要的额外数据,那么你应该根据需要选择定义一个基础武器类及其专属化的后代。

Disclaimer 放弃

You could use a model like this to archieve what you need. 您可以使用这样的模型来满足您的需求。 Please bear in mind that, as any design, it may contains flaws. 请记住,与任何设计一样,它可能包含缺陷。 It is intended to help you to solve your problem, not as a solution. 它旨在帮助您解决问题,而不是解决方案。

Weapon 武器

This class represents a Weapon in your app. 此类代表您应用中的武器。 Note the overriding methods in order to implement comparison/search/indexing properly. 请注意重写方法,以便正确实现比较/搜索/索引。

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 WeaponName

Fixed options for the weapon names (assuming, as you said, there will be limited options for them) 固定武器名称的选项(假设,正如你所说,它们的选项有限)

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

Player 播放机

Note that: 注意:

  • Internally, you can store weapons in the structure you like, but for the outside world only an IEnumerable instance is published 在内部,您可以在您喜欢的结构中存储武器,但对于外部世界,只发布IEnumerable实例
  • It represents the fact that a player can grab a weapon or change the active one (here you can add any actions needed). 它代表了一个事实,即玩家可以抓住武器或更改活动武器(在这里你可以添加所需的任何动作)。 Via events, the player communicates the outside world about his actions. 通过事件,玩家向外界传达他的行为。
  • You are giving the object methods with semantics. 您正在为对象方法提供语义。 Rather than player.Weapons.Add you are making explicit the player's behavior. 而不是player.Weapons.Add你明确了玩家的行为。

    public class Player { private readonly IList _ownedWeapons = new List(); public class Player {private readonly IList _ownedWeapons = new List(); protected Weapon ActiveWeapon { get; 受保护的武器ActiveWeapon {get; private set; 私人集; } }

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

    } }

    public class WeaponEventArgs : EventArgs { public WeaponEventArgs(Weapon weapon) { Weapon = weapon; public class WeaponEventArgs:EventArgs {public WeaponEventArgs(Weapon weapon){Weapon = weapon; } }

      public Weapon Weapon { get; set; } } 

WeaponsRepository WeaponsRepository

  • Centralized all the knowledge about weapons player can use in your app, registering available weapon names and their creation in the constructor. 集中所有关于武器玩家的知识可以在你的应用程序中使用,在构造函数中注册可用的武器名称及其创建。
  • Also, it provides methods to create and query available weapons. 此外,它还提供了创建和查询可用武器的方法。
  • You can tell what weapons a player do not have yet by using the WeaponsNotOwnedBy method. 你可以通过使用WeaponsNotOwnedBy方法告诉玩家还没有什么武器。

    public class WeaponRepository { readonly Dictionary> _availableWeapons = new Dictionary>(); public class WeaponRepository {readonly Dictionary> _availableWeapons = new Dictionary>();

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

    } }

Usage 用法

  • You could assing a class the responsiblity to build and update the player's toolbar. 你可以帮助一个班级负责建立和更新玩家的工具栏。
  • Create one button per weapon using the method AvailableWeapons in WeaponRepository in the BuildToolbar method. 使用BuildToolbar方法中WeaponRepository中的AvailableWeapons方法为每个武器创建一个按钮。
  • In this class, subscribe to the player's event. 在此课程中,订阅玩家的活动。 In this way, you can enable/disable/highlight active weapon as needed. 这样,您可以根据需要启用/禁用/突出显示活动武器。

Note: Im ommiting WeaponButton for brevity 注意:为简洁起见,我省略了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() { ... }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM