Java中游戏的面向对象设计:如何处理NPC团队?

3
我正在用Java制作一个非常简单的2D RPG游戏。我的目标是尽可能使用简单的代码实现。目前,我的类结构如下:
- 物理对象具有x和y维度。 - 漫游对象是可以移动()的物理对象。 - 人形对象是漫游对象,具有GameItems库存。 - 玩家是单例人形对象,最多可以雇佣4个NPC人形对象加入他或她的队伍,并执行其他操作,例如与非人形对象战斗。 - NPC人形对象可以被玩家对象雇佣加入他或她的队伍,一旦被雇佣就可以为玩家战斗。
到目前为止,我已经给玩家类添加了一个“party” NPC人形对象的ArrayList,并给NPC人形对象类添加了一个“hired”布尔值。
然而,我的战斗方法很笨重,需要使用if语句来检查队伍的大小,然后才能实现战斗,例如:
public class Player extends Humanoids {
   private ArrayList<Humanoids> party;
   // GETTERS AND SETTERS for party here
   //...

   public void fightEnemy(Enemy eneObj) {
      if (this.getParty().size() == 0)
        // Do combat without party issues
      else if (this.getParty().size() == 1)
        // Do combat with party of 1
      else if (this.getParty().size() == 2)
        // Do combat with party of 2
      // etc. 

我的问题是,在面向对象设计的思路下,我这样做是否可以用尽可能简单的代码实现?是否有更好的方法?


我建议你按照这种方式编写代码,但在拥有一个可工作的版本之后,请重新审视代码并进行重构或重写。 - Ron
@Chris,谢谢,我已经加入了“==”。@Ron,感谢您的建议,我会采纳。我应该指出,我更喜欢一开始就把它做对,而不是回来处理一团糟。 - Arvanem
如果只涉及整数,使用switch...case语句怎么样? - Chuk Lee
@Chuk Lee:想法不错。使用switch或if语句,但问题在于它很笨重,不是良好的OO设计。Chris Cooper的答案从OO设计角度来看很好。 - Arvanem
我认为玩家不应该拥有派对;玩家属于一个派对,就像NPC一样。 - Duncan
4个回答

7
"我的问题是,在面向对象设计方面,我是否在尽可能简单的代码中正确地完成了这项工作?"
不是的,您的描述使用了描述如何构建过多继承的基本动词。
物理对象具有x和y维度(位置)。 [漫游对象是一个具有可变位置的物理对象] 人形对象是具有库存的漫游对象。 玩家是一个单例的人形对象,可以拥有一支队伍 [一个队伍一个玩家,并且]最多可以有4个NPC人形角色
组合虽然在面向对象设计中很少强调,但在代码中有一定的作用。这就是为什么has-a/is-a区别在分析中经常被使用的原因。
通过将玩家声明为单例,您增加了类型复杂性,并可能限制了设计。如果将来想要两个或更多的玩家怎么办?这不是一个不合理的扩展,但需要打破使用的单例反模式。如果只想要一个玩家,请只实例化一个;在类中编码单例假设是不必要的限制。请记住,编码人员必须积极调用构造函数,不必担心玩家会自动出现。
对象有位置,很好:通过组合给它一个位置。位置可以改变,因此定义position::move()。玩家可能有人类控制器,这使其与NPC区分开来,但是-根据定义-非玩家角色确实是一个角色,其控制不来自玩家。您是否想让NPC玩家控制?许多游戏都这样做,但是如果您已经在类中编码了玩家角色依赖关系,则NPC将始终是NPC。
此外,您对于派对人数为4(或5)是否合适有多大的把握?零、一、无穷大原则认为,如果允许多个,则应该允许任意数量。如果您不将“五”的硬编码到设计中,则会限制灵活性。
我通常建议设计师将继承视为最后的方法,因为过度使用的历史。一个设计可以是面向对象的,而没有任何继承。多态很酷,但封装和抽象也同样重要,甚至更重要。

1
感谢您的评论。这就是我来到Stack Overflow的原因。它关乎从一开始就正确编码,而不是回来处理混乱的代码,而在我的情况下,这涉及太多的继承。 - Arvanem
虽然这不是我的问题,但我很想知道你会建议什么样的类设计? - Arnab Datta

2

好的,忽略总体设计,从基本编程角度来看,你应该有一个以参加聚会人数为参数的方法,而不是使用那个if语句结构。这样,你只需要传递this.getParty().size(),就可以摆脱那些if语句了。

例如:

  combatManager.fight(this.getParty().size(), eneObj);

combatManager是一个对象(或类,如果您需要静态版本),它知道如何让事物进行战斗。

但正如我所说,这不是您设计的解决方案,仅仅是一种更好的避免使用if语句的方式。

Player类不应该负责让事物进行战斗,因此您可以将fightEnemy方法更改为engageEnemy或其他名称,并简单地将其传递正确的参数给combatManager


@Chris Cooper:Java类需要大写字母开头,因此不清楚您是建议使用带有静态fight()方法的辅助类还是类的实例。无论如何,您只是将问题转移到了fight方法上:类不会知道如何神奇地让“事物战斗”。特别是如果OP的方法已经被称为fightEnnemy,那么另一个方法fight是否真的有帮助并不完全清楚。 - SyntaxT3rr0r
@WizardOfOdds:我同意你所说的。我把它变成了大写,但在原帖中,我认为它真的不重要是什么。我并没有建议他如何布置架构,只是建议他如何处理那个特定的问题。我也同意另一种战斗方法可能不会有所帮助,但实际上,Player类不应该负责知道如何让事物战斗。 - Chris Cooper
@ Chris 和 Wizard,感谢你们的讨论。很明显我需要重新开始规划这个RPG,使其从一开始就更加流畅。 - Arvanem

0
也许这是使用策略设计模式的好机会。这样,您可以更改添加或删除团队成员时将使用的战斗策略,而不是每次战斗都更改。

感谢您提供的有关模式的链接,它清晰地阐述了“策略设计模式”,这让我以前难以理解的内容变得更加清晰。 - Arvanem

0
玩家是队伍的一部分。NPC也是队伍的一部分。为什么玩家要处理NPC的战斗?我认为你应该将这个逻辑添加到NPC类(扩展自Humanoid),并加入一些人工智能,使得当NPC们在一起时,它们可以与玩家和其他NPC互动。

@Tom:感谢您的回复。顺便说一下,我已经实现了一个总体的 Characters 类来实现一个 Hireable 接口,该接口具有 hire(Characters hiree) 方法。当被调用时,hire 将 hiree 添加到调用 hire 方法的对象的小队中。目前,我通过调用 hire(player) 来初始化玩家为 1 的团队,但这是一个笨拙的解决方案。您有什么建议吗? - Arvanem
@Tom,不用担心回答。这很难理解我在做什么,你需要看代码。如果我无法解决问题,我会稍后在StackOverflow上发布它作为一个问题。 - Arvanem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接