我正在解决如何组织我正在制作的RPG项目中的类的麻烦。我试图实施一个战斗系统,但我对初步结果不满意。
这是我目前为课程设置的基本布局。
clsCharacter> clsTeam> clsBattle
clsCharacter 包含与一个角色相关的统计信息,包括可供该角色使用的魔法。还包括角色配备的武器。
clsTeam 包含多个clsCharacter,以及团队中每个clsCharacter可以使用的项目列表。
clsBattle 包含两个clsTeams。一个团队是玩家,另一个团队是计算机。
现在,这是组织数据的绝佳方式。但是,我看到这种方法的局限性。例如,要将数据从Battle传递给Character,它必须通过团队类,反之亦然。另外,如果我使用属性,它会将数据传输为ByVal而不是ByRef,因此我无法保证我正在编辑原始文件而不是传递对象的副本(IIRC)
此外,我觉得在clsCharacter类中包含调用此方法的方法只是凌乱的代码:MyTeam.MyBattle.DoAction()。另外,clsCharacter当时甚至可能都不在战斗中 - 当我还需要关心在地图上移动,保存/加载时,我不会陷入那种专门用于战斗的代码。数据等
那么,有什么建议吗?现在,我被烧掉了。我到目前为止的一个想法是为clsCharacter包含一个函数,该函数导出一个字符可以进行的所有可能移动的列表,如果字符是CPU,则选择最优的一个,如果是Human,则将它包装在一些漂亮的GUI中所以他们可以选择采取什么行动。但与此同时,我如何在战斗的背景下使用这些信息呢?
答案 0 :(得分:3)
注意:这个答案的结束时间超过了我的预期,但我特意将它归结为你的RPG情况,所以希望你觉得它很有帮助。将代码实际复制到控制台应用程序中并使用它来理解它的实际工作方式可能很有用。
您是否考虑过在设计中使用接口?我认为他们真的会有很多帮助。我一周前asked a question并得到了一些关于使用依赖注入接口的好建议。
使用接口,您可以避免考虑“执行此操作需要哪些容器数据?”而是想“我需要采取什么行动?”这是一个简单的例子,说明如何对代码进行建模,以便在战斗中采用攻击方法:
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
属性也被抽象到接口中。请参阅以下程序示例,了解其如何工作:
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
希望您能从这个(可悲的冗长)示例中看到使用接口来帮助封装特定功能的优势。这允许您将类期望的功能与类本身分离,并允许使用该类的对象直接通过接口调用所需的功能。
在我展示的示例中,Battle
并不关心它是否有角色。它需要知道的是它有可能攻击的东西,即实现IAttacker
接口的东西。因为我们这样写,所以我们并没有将我们的战斗局限于Character
。我们也只给Battle
对象提供战斗所必需的东西。 Battle
不一定需要知道与攻击者相关的所有经验点,物品,宠物等。它唯一关心的是当它要求攻击者可以提供的总损害时,攻击者会提供该信息。每个IAttacker
可能会以不同方式实现它,但只要Battle
对象获取所需信息,就可以了。
如果在设计中将接口与基类混合在一起,那么在构建类层次结构时,您将有一个良好的开端。
答案 1 :(得分:0)
将你的角色作为一个集合属性暴露在团队中没有任何“错误”,假设一场战斗将总是有两个团队(即使团队只有一个玩家)
然后,您可以在战斗中对团队成员执行某种方法,例如
var dwarfs = _teamA.Players.Where(p => p.CharacterType == Character.Dwarf);
foreach(var dwarf in dwarfs)
AddHealth(dwarf);
代码是C#,但希望你明白了。基本上你是对向字符类添加方法的问题是正确的。这些方法应该在管理游戏部分的类中定义
答案 2 :(得分:0)
这实际上来自Civilization 5数据库。它是用XML编写的,而且很容易理解。我的回答主要是为了支持Ben的答案,但我强烈建议你在其他游戏中制作一个或两个mod,让他们的代码暴露在外,看看体验,或者至少是修改工具来暴露他们的代码。相信我,它会有很多帮助,
这个清楚地显示了如何描述单元对象,注意它们连接到抽象类UNITCLASS_LONGSWORDSMAN,UNITCOMBAT_MELEE,UNITAI_ATTACK
<?xml version="1.0" encoding="utf-8"?>
<!-- edited with XMLSPY v2004 rel. 2 U (http://www.xmlspy.com) by MBeach (Firaxis Games) -->
<GameData>
<!-- Table data -->
<Units>
<Row>
<Class>UNITCLASS_LONGSWORDSMAN</Class>
<Type>UNIT_DANISH_BERSERKER</Type>
<PrereqTech>TECH_STEEL</PrereqTech>
<Combat>16</Combat>
<Cost>120</Cost>
<Moves>3</Moves>
<HurryCostModifier>20</HurryCostModifier>
<CombatClass>UNITCOMBAT_MELEE</CombatClass>
<Domain>DOMAIN_LAND</Domain>
<DefaultUnitAI>UNITAI_ATTACK</DefaultUnitAI>
<Description>TXT_KEY_UNIT_DANISH_BERSERKER</Description>
<Civilopedia>TXT_KEY_CIV5_DENMARK_BERSERKER_TEXT</Civilopedia>
<Strategy>TXT_KEY_CIV5_DENMARK_BERSERKER_STRATEGY</Strategy>
<Help>TXT_KEY_CIV5_DENMARK_BERSERKER_HELP</Help>
<MilitarySupport>true</MilitarySupport>
<MilitaryProduction>true</MilitaryProduction>
<Pillage>true</Pillage>
<ObsoleteTech>TECH_RIFLING</ObsoleteTech>
<GoodyHutUpgradeUnitClass>UNITCLASS_RIFLEMAN</GoodyHutUpgradeUnitClass>
<AdvancedStartCost>25</AdvancedStartCost>
<XPValueAttack>3</XPValueAttack>
<XPValueDefense>3</XPValueDefense>
<Conscription>3</Conscription>
<UnitArtInfo>ART_DEF_UNIT_U_DANISH_BERSERKER</UnitArtInfo>
<IconAtlas>HARALD_UNIT_ATLAS</IconAtlas>
<UnitFlagAtlas>HARALD_UNIT_FLAG_ATLAS</UnitFlagAtlas>
<UnitFlagIconOffset>0</UnitFlagIconOffset>
<PortraitIndex>0</PortraitIndex>
<MoveRate>HEAVY_BIPED</MoveRate>
</Row>
<Row>
<Class>UNITCLASS_RIFLEMAN</Class>
<Type>UNIT_DANISH_SKI_INFANTRY</Type>
<PrereqTech>TECH_RIFLING</PrereqTech>
<Combat>25</Combat>
<Cost>225</Cost>
<Moves>2</Moves>
<CombatClass>UNITCOMBAT_GUN</CombatClass>
<Domain>DOMAIN_LAND</Domain>
<DefaultUnitAI>UNITAI_DEFENSE</DefaultUnitAI>
<Description>TXT_KEY_UNIT_DANISH_SKI_INFANTRY</Description>
<Civilopedia>TXT_KEY_CIV5_DENMARK_SKI_INFANTRY_TEXT</Civilopedia>
<Strategy>TXT_KEY_CIV5_DENMARK_SKI_INFANTRY_STRATEGY</Strategy>
<Help>TXT_KEY_CIV5_DENMARK_SKI_INFANTRY_HELP</Help>
<MilitarySupport>true</MilitarySupport>
<MilitaryProduction>true</MilitaryProduction>
<Pillage>true</Pillage>
<IgnoreBuildingDefense>true</IgnoreBuildingDefense>
<ObsoleteTech>TECH_REPLACEABLE_PARTS</ObsoleteTech>
<GoodyHutUpgradeUnitClass>UNITCLASS_INFANTRY</GoodyHutUpgradeUnitClass>
<AdvancedStartCost>30</AdvancedStartCost>
<XPValueAttack>3</XPValueAttack>
<XPValueDefense>3</XPValueDefense>
<Conscription>5</Conscription>
<UnitArtInfo>ART_DEF_UNIT_U_DANISH_SKI_INFANTRY</UnitArtInfo>
<IconAtlas>HARALD_UNIT_ATLAS</IconAtlas>
<UnitFlagAtlas>HARALD_UNIT_FLAG_ATLAS</UnitFlagAtlas>
<UnitFlagIconOffset>1</UnitFlagIconOffset>
<PortraitIndex>1</PortraitIndex>
</Row>
</Units>
在这里,您可以看到设备如何连接到AI
<Unit_AITypes>
<Row>
<UnitType>UNIT_DANISH_BERSERKER</UnitType>
<UnitAIType>UNITAI_ATTACK</UnitAIType>
</Row>
<Row>
<UnitType>UNIT_DANISH_BERSERKER</UnitType>
<UnitAIType>UNITAI_DEFENSE</UnitAIType>
</Row>
<Row>
<UnitType>UNIT_DANISH_SKI_INFANTRY</UnitType>
<UnitAIType>UNITAI_ATTACK</UnitAIType>
</Row>
<Row>
<UnitType>UNIT_DANISH_SKI_INFANTRY</UnitType>
<UnitAIType>UNITAI_DEFENSE</UnitAIType>
</Row>
<Row>
<UnitType>UNIT_DANISH_SKI_INFANTRY</UnitType>
<UnitAIType>UNITAI_EXPLORE</UnitAIType>
</Row>
</Unit_AITypes>
在这里你可以看到单位如何升级,在civ5中它实际上获得了水平并成为别的东西,你可以提供更多的权力等
<Unit_ClassUpgrades>
<Row>
<UnitType>UNIT_DANISH_BERSERKER</UnitType>
<UnitClassType>UNITCLASS_RIFLEMAN</UnitClassType>
</Row>
<Row>
<UnitType>UNIT_DANISH_SKI_INFANTRY</UnitType>
<UnitClassType>UNITCLASS_INFANTRY</UnitClassType>
</Row>
</Unit_ClassUpgrades>
<Unit_FreePromotions>
<Row>
<UnitType>UNIT_DANISH_BERSERKER</UnitType>
<PromotionType>PROMOTION_AMPHIBIOUS</PromotionType>
</Row>
<Row>
<UnitType>UNIT_DANISH_SKI_INFANTRY</UnitType>
<PromotionType>PROMOTION_SKI_INFANTRY</PromotionType>
</Row>
</Unit_FreePromotions>
<Unit_ResourceQuantityRequirements>
<Row>
<UnitType>UNIT_DANISH_BERSERKER</UnitType>
<ResourceType>RESOURCE_IRON</ResourceType>
</Row>
</Unit_ResourceQuantityRequirements>
可能会有更多有用的示例,但我真的建议您使用modd,如果您选择的游戏更接近您喜欢的moded社区,那就更好了。
干杯,祝你好运