游戏开发中面向对象的应用程序问题

时间:2009-12-25 22:17:16

标签: oop object

我会尽可能地直接解决这个问题,因为在结构化编程背景下肯定会有一些我完全遗漏的东西。

说我有一个Player类。这个Player类可以改变它在游戏世界中的位置。我将此方法称为warp(),它将Position类实例作为参数来修改Player的内部位置。这在OO术语中对我来说是完全合理的,因为我要求玩家“做”某事。

当我除了修改球员位置之外还需要做其他事情时,问题就出现了。例如,假设我需要将该扭曲事件发送给在线游戏中的其他玩家。该代码是否也应该在Player的warp()方法中?如果没有,那么我会想象在服务器类中声明某种辅助方法,比如warpPlayer(播放器,位置)。这样做似乎可以减少玩家作为一系列吸气者和制定者所做的一切,或者我在这里错了?这是完全正常的吗?我已经无数次地读过一个暴露一系列getter / setter的类表示一个非常糟糕的抽象(用作数据结构而不是类)。

当您需要保留数据并将其保存到文件时,会出现同样的问题。由于将播放器“保存”到文件与Player类处于不同的抽象级别,因此在播放器类中使用save()方法是否有意义?如果没有,就像savePlayer(播放器)一样在外部声明它意味着savePlayer方法需要一种方法来从Player类中获取所需的每一段数据,最终暴露出类的整个私有实现。

因为OOP是今天最常用的设计方法(我假设?),所以我不得不对这些问题感到遗憾。我和我的同行一起讨论了它,他们也做了轻微的开发,他们也有与OOP完全相同的问题。也许只是结构化编程背景使我们无法理解OOP的全部好处,而不仅仅是提供设置和获取私有数据的方法,以便从一个地方更改和检索它。

先谢谢,希望我听起来不像白痴。对于那些真正需要了解这种设计所涉及的语言的人来说,它是服务器端的Java和客户端的ActionScript 3。

7 个答案:

答案 0 :(得分:4)

我建议你不要害怕这样一个事实,那个玩家将是一类吸气者和制定者。无论如何,对象是什么?它是属性和行为的汇编。事实上,您的课程越简单,您在开发过程中获得的OOP的好处就越多。

我会将您的任务/功能分解为类:

播放器:

  • 有点数属性
  • 有位置属性
  • 可以走路(位置),发射“走路”事件
  • 可以healUp(生命值)
  • 可以使用灾难(生命值),解雇“isHurt”事件
  • 可以检查是否仍然存在,例如isAlive()方法

战斗机扩展玩家(你应该能够在需要时将玩家施放到战斗机上):

  • 有力量和其他战斗参数来计算伤害
  • 可以攻击()发起“攻击”事件

世界跟踪所有玩家:

  • 听“走路”事件(并防止非法移动)
  • 聆听“isHurt”事件(并检查它们是否还活着)

战斗处理两名战士之间的战斗:

  • 构造函数,有两个战士作为参数(你只想构建真正相互战斗的玩家之间的战斗)
  • 听取两名玩家的“攻击”事件,计算伤害,并执行防守球员的takeDamage方法

PlayerPersister扩展AbstractPersister:

  • 将玩家的状态保存在数据库中
  • 从数据库中恢复玩家的状态

当然,你游戏的崩溃将会复杂得多,但我希望这能帮助你以“更多OOP”方式开始思考问题:)

答案 1 :(得分:1)

不要过分担心Player类是一群不知所措。 Player类是一个模型类,模型类往往是这样的。重要的是您的模型类小而干净,因为它们将在整个程序中重用。

我认为你应该使用你建议的warpPlayer(player, position)方法。它使Player类保持清洁。如果您不想将播放器传递给函数,也许您可​​以拥有包含PlayerController对象和Player方法的warp(Position p)类。这样,您可以向控制器添加事件发布,并将其保留在模型之外。

至于保存播放器,我是通过让Player实现某种序列化接口来实现的。播放器类负责序列化和反序列化自身,而其他一些类则负责将序列化数据写入文件或从文件中编写。

答案 2 :(得分:0)

我可能会考虑使用一个跟踪玩家对象的Game对象。所以你可以做类似game.WarpPlayerTo(WarpLocations.Forest);如果有多个玩家,可以传递一个玩家对象或guid。我觉得你仍然可以保持OO,而游戏对象可以解决我认为的大多数问题。

答案 3 :(得分:0)

您所描述的问题不仅仅属于游戏设计,而是属于软件架构。常见的方法是使用依赖注入(DI)控制反转(IoC)机制。简而言之,您要实现的是能够从您的对象访问各种本地 Service ,以便例如传播某些事件(例如warp),日志等。

控制反转意味着,不是直接创建对象,而是告诉某些服务为您创建它们,该服务依次使用依赖注入来向对象通知它们所依赖的服务。

答案 4 :(得分:0)

如果您在多台PC的不同PC之间共享数据,则该程序的核心功能是保持并同步PC之间的这种状态。如果将这些值分散在许多不同的类中,则很难同步。

在这种情况下,我建议您设计需要在所有客户端之间同步的数据,并将其存储在单个类中(例如GameState)。此对象将处理不同PC之间的所有同步,并允许本地代码请求更改数据。然后它将从自己的状态“驱动”游戏对象(Player,EnemyTank等)。 [编辑:原因是保持这种状态尽可能小并在客户端之间有效地传输它将是您设计的关键部分。通过将它们保存在一个地方,它可以更容易地做到这一点,并鼓励您只将绝对必需品放在该类中,以便您的通信不会因不必要的数据而变得臃肿]

如果您没有进行多人游戏,并且您发现更改玩家的位置需要更新多个对象(例如,您希望相机知道玩家已移动以便可以跟随他),那么一个好的方法是让玩家对自己的位置负责,但提升其他对象可以订阅/收听的事件/消息,以便知道玩家的位置何时发生变化。所以你移动玩家,然后相机得到一个回调,告诉它玩家的位置已经更新。

另一种方法是,相机只是每帧读取玩家的位置以便更新自己 - 但这并不像使用事件那样松散耦合和灵活。

答案 5 :(得分:0)

有时,OOP的技巧是理解什么是对象,什么是对象的功能。我认为我们通常很容易在概念上将Player,Monster,Item等对象锁定为系统中的“对象”,然后我们需要创建对象,如Environment,Transporter等将这些对象链接在一起,它可能会失控,具体取决于概念如何协同工作,以及我们需要完成的任务。

我过去曾与之合作的优秀工程师有一种将系统视为对象集合的方法。有时在一个系统中,它们将是业务对象(如项目,发票等),有时它们将是封装处理逻辑(DyeInjectionProcessor,PersistanceManager)的对象,这些逻辑跨越系统中的多个操作和“对象”。在这两种情况下,隐喻都适用于该特定系统,并使整个过程更容易实现,描述和维护。

OOP的真正强大之处在于能够在大型复杂系统中更轻松地表达和管理。这些是目标的OOP原则,并不用担心它是否适合严格的对象层次结构。

我还没有参与游戏设计工作,所以也许这个建议不会有效,在我开展工作的系统中开发它是一个非常有益的改变,在简化和封装方面考虑OOP而不是真正的世界对象为1 OOP类。

答案 6 :(得分:0)

我想扩展GrayWizardx的最后一段,说明并非所有对象都需要具有相同的复杂程度。它可能非常适合您的设计,使对象具有get / set属性的简单集合。另一方面,重要的是要记住对象可以表示任务的任务或集合而不是现实世界的实体。

例如,玩家对象可能不负责移动玩家,而是代表其位置和当前状态。 PlayerMovement对象可能包含用于更改玩家在屏幕上或游戏世界中的位置的逻辑。

在我开始简单地重复已经说过的内容之前,我将指向OOP设计的SOLID principles(Aviad P.已经提到过其中两个)。他们可能会为游戏创建一个好的对象模型提供一些高级指导。