类层次结构 - RPG 游戏中的数据设计 (VB.Net) [英] Class Hierarchy - Data design in an RPG Game (VB.Net)

查看:29
本文介绍了类层次结构 - RPG 游戏中的数据设计 (VB.Net)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在如何组织我正在制作的 RPG 项目中的类时遇到了麻烦.我曾尝试实施战斗系统,但我对最初的结果并不满意.

I'm having troubles wrapping my head around how to organize the classes within my RPG project that I'm making. I've tried to implement a battle system but I am not comfortable with my initial results.

这是我目前为我的课程设置的基本布局.

This is the basic layout I currently have for my classes.

clsCharacter > clsTeam > clsBattle

clsCharacter > clsTeam > clsBattle

clsCharacter 包含与一个角色有关的统计数据,包括该角色可以使用的魔法.还包括该角色装备的武器.

clsCharacter contains statistics pertaining to one character, including magic available to that character to use. Also includes what weapons that character has equipped.

clsTeam 包含多个 clsCharacter,以及团队中每个 clsCharacter 可以使用的项目列表.

clsTeam contains multiple clsCharacter, as well as a list of items that each clsCharacter in the team can use.

clsBattle 包含两个 clsTeams.一支球队是球员,另一支球队是电脑.

clsBattle contains two clsTeams. One team is the player and the other is the computer.

现在,这是组织数据的绝妙方法.但是,我看到了这种方法的局限性.例如,要将数据从 Battle 传递到 Character,它必须通过 team 类,反之亦然.另外,如果我使用属性,它会将数据作为 ByVal 而不是 ByRef 传输,因此我无法保证我正在编辑原始对象而不是传递对象 (IIRC) 的副本

Now, this is a wonderful way to organize data. However, I see limitations with this approach. For instance, to pass data from Battle to Character, it has to pass through the team class and vice-versa. Plus, if I use Properties, it transfers data as ByVal instead of ByRef, so I cannot guarentee that I'm editing the original and not a copy of the passed object (IIRC)

此外,我觉得在 clsCharacter 类中包含调用此方法的方法很麻烦:MyTeam.MyBattle.DoAction().另外,当时 clsCharacter 甚至可能不在战斗中 - 当我还需要担心在地图上移动、保存/加载时,我不会陷入那个具有专用于战斗的代码的类中数据等

In addition, I feel it is just messy code to include methods within the clsCharacter class that invoke this: MyTeam.MyBattle.DoAction(). Plus, the clsCharacter might not even be in the battle at the time - I don't won't to bog down that class featuring code that is exclusive for battling when I also need to be concerned about moving around the map, saving/loading data, etc.

那么,有什么建议吗?现在,我已经没有想法了.到目前为止,我的一个想法是为 clsCharacter 包含一个函数,该函数导出角色可以进行的所有可能移动的列表,如果角色是 CPU,则选择最佳的一个,如果是 Human,则将其包装在一些漂亮的 GUI 中这样他们就可以选择要采取的行动.但与此同时,我如何在战斗中使用这些信息?

So, any suggestions? Right now, I'm burnt out of ideas. One idea I have so far is to include a function for the clsCharacter that exports a list of all possible moves the character could make, and if the character is CPU choose the most optimal one, and if Human than wrap it up in some nifty GUI so they can choose what action to take. But at the same time, how do I use that information within the context of the battle?

推荐答案

注意:这个答案最终比我预期的要长得多,但我专门针对您的 RPG 情况进行了调整,因此希望您觉得它有帮助.将代码实际复制到控制台应用程序中并使用它以了解其实际工作方式可能很有用.

您是否考虑过在设计中使用接口?我认为他们真的会很有帮助.我问一周前的一个问题,并得到了一些关于将接口与依赖注入一起使用的很好的建议.

Have you considered using interfaces in your design? I think they would really help a lot. I asked a question a week ago and got some great suggestions about using interfaces with Dependency Injection.

使用接口,您可以避免思考执行此操作需要哪些数据容器?"而是想我需要什么行动?"下面是一个简单的示例,说明如何对代码进行建模以提取战斗中的攻击方法:

Using interfaces, you can avoid thinking "what containers of data do I need to perform this action?" and instead think "what action do I need?" Here's a simple example of how you might model your code to pull out the method of attacking in a battle:

Public Interface IAttacker
    Function GetAttackDamage() As Double 
    Property Name As String
End Interface 

Public Class Character
    Implements IAttacker

    Public Property Name As String Implements IAttacker.Name
    Public Property Weapons As List(Of IWeapon)
    Public Property SpecialTeamAttackMove As IWeapon

    Public Function GetAttackDamage() As Double _
      Implements IAttacker.GetAttackDamage()
        'Get the sum of the damage in the weapons list'
        Return Weapons.Select(Function(iw) iw.Damage).Sum()
    End Function
End Class

Public Class Team
    Implements IAttacker

    Public Property Characters As List(Of Character) = _
        New List(Of Character)()
    Public Property Name As String Implements IAttacker.Name

    Public Function GetAttackDamage() As Double _
      Implements IAttacker.GetAttackDamage()
        'Get the sum of the damage of the SpecialTeamAttackMove'
        'of each character on the team'
        Return Characters.Select(Function(c) _
            c.SpecialTeamAttackMove.Damage).Sum())
    End Function
End Public

Public Class Battle
    Public Property AttackerA As IAttacker
    Public Property AttackerB As IAttacker

    Public Sub New(attackerA As IAttacker, attackerB As IAttacker)
        Me.AttackerA = attackerA
        Me.AttackerB = attackerB
    End Sub

    'This function returns the "winner" of the fight, unless it is a tie'
    Public Function Fight() As IAttacker
        If Me.AttackerA.GetAttackDamage() = Me.AttackerB.GetAttackDamage() Then
            Return Nothing
        ElseIf Me.AttackerA.GetAttackDamage() > _
               Me.AttackerB.GetAttackDamage() Then
            Return Me.AttackerA
        Else
            Return Me.AttackerB
        End If
    End Function

 End Class

在上面的代码中,Battle 类只知道它需要一个实现IAttacker 的对象.它不关心该对象如何实现 GetAttackDamage(),只关心它可以在需要时调用该方法.Name 属性也被抽象到接口中.请参阅以下程序示例,了解其工作原理:

In the above code, the Battle class only knows that it needs an object that implements IAttacker. It doesn't care how that object implements GetAttackDamage(), only that it can call the method when it needs to. The Name property is also abstracted into the interface. See the following procedural examples of how this might work:

Public Sub BattleBetweenTwoCharacters()
  'In this example, two characters are instantiated. Assume the "..." is where'
  'you add weapons and other properties. The characters fight and the winning'
  'IAttacker is returned'
    Dim charA As Character = New Character() With {...}
    Dim charB As Character = New Character() With {...}

    Dim winningChar As IAttacker = New Battle(charA, charB).Fight()

    If winningChar Is Nothing Then
        Console.WriteLine("It was a tie.")
    Else
        Console.WriteLine("{0} was the winner",winningChar.Name)
    End If
End Sub


Public Sub BattleBetweenTwoCharacters()
  'In this example, several characters and a team are instantiated. '
  'Assume the "..." is where you add weapons and other properties. A '
  'fight takes place and the winner is returned.'
    Dim charA As Character = New Character() With {...}
    Dim charB As Character = New Character() With {...}
    Dim charC As Character = New Character() With {...}

    Dim teamAB As New Team()
    teamAB.Characters.Add(charA)
    teamAB.Characters.Add(charB)

    'Here, a team fights a character. The Battle.Fight method will call '
    'GetAttackDamage() on each object, even though a team implements'
    'it differently than a single character.'
    Dim winner As IAttacker = New Battle(teamAB, charB).Fight()

    If winningChar Is Nothing Then
        Console.WriteLine("It was a tie.")
    Else
        Console.WriteLine("{0} was the winner",winningChar.Name)
    End If
End Sub    

希望您能从这个(可悲的冗长)示例中看到使用接口来帮助封装特定功能的优势.这允许您将您期望从类中获得的功能与类本身分离,并允许使用该类的对象直接通过接口调用它需要的功能.

Hopefully you can see from this (woefully lengthy) example the advantage of using interfaces to help encapsulate specific functionality. This allows you to decouple the functionality you expect from a class from the class itself and allow the objects that consume that class to call the functionality it needs directly through the interface.

在我展示的例子中,Battle 并不关心它是否有字符.它需要知道的只是它具有可以攻击的东西,即实现了 IAttacker 接口的东西.因为我们是这样写的,所以我们并没有将我们的战斗限制在 Character 上.我们也只给 Battle 对象提供战斗所需的东西.Battle 不一定需要了解与攻击者相关的所有经验值、物品、宠物等.它唯一关心的是,当它要求攻击者可以提供的总伤害时,攻击者会提供该信息.每个 IAttacker 可能会以不同的方式实现它,但没关系,只要 Battle 对象获得它需要的信息.

In the example I showed, the Battle didn't care if it had a character. All it need to know about was that it had things that could attack, that is, things which implemented the IAttacker interface. Because we wrote it this way, we weren't limiting our battles to just Characters. We are also giving the Battle object only the things necessary to fight. The Battle doesn't necessarily need to know about all of the experience points, items, pets, etc. associated with an attacker. The only thing it cares about is that when it asks for the total damage that an attacker can give, the attacker provides that information. Each IAttacker might implement it differently, but that's OK, as long as the Battle object gets the information it needs.

如果您在设计中混合使用接口和基类,那么您将在构建类层次结构方面有一个很好的开端.

If you mix interfaces with base classes in your design, you'll be off to a very good start in structuring your class hierarchy.

这篇关于类层次结构 - RPG 游戏中的数据设计 (VB.Net)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆