【问题标题】:OO design for my (basic) Pacman game我的(基本)吃豆人游戏的面向对象设计
【发布时间】:2011-06-30 20:27:30
【问题描述】:

我正在尝试用 C++ 创建一个基本的 Pacman 游戏(我将在这个问题中使用 Java 语法,因为这更容易演示),但我找不到一个好的设计选项。

到目前为止,我有 4 节课:
- 怪物:可以针对特定于怪物的行为进行子类化,并包含怪物的所有逻辑
- 播放器:包含播放器逻辑
- 地图:包含表示地图的二维数组。此数组指定哪些位置是墙壁或吃豆人食物
- 游戏:包含玩家、地图和怪物列表。

为了简单起见:

public class Game {
  Player player;
  Map map;
  ArrayList<Monster> monsters;

  public Game() {
    player = new Player();
    map = new Map();
    monsters = new ArrayList<Monster();
    monsters.add(new ScaryMonster());
    monsters.add(new DumpMonster());
  }

  public void update() {
    player.update();
    map.update();

    for (Monster monster: monsters) {
      monster.update();
    }

  public void draw() {
    map.draw();
    player.draw();

    for (Monster monster: monsters) {
      monster.draw();
  }
}

所以我现在要做的就是创建一个 Game 对象并每次调用 update() 和 draw() 。很简单。但它不起作用。

假设我在玩家对象上调用 update() 并且玩家(当然是吃豆人)击中食物。在这种情况下,应该通知地图对象(和位置)以从二维数组中移除食物。假设玩家杀死了一个怪物,怪物的位置应该改变(怪物类有一个“位置字段”)。你可以想象更多这样的情况。

一种选择是将地图和怪物对象作为参数传递给玩家对象的 update() 和 draw() 方法。并在地图的方法调用中将玩家和怪物对象作为参数传递。但这听起来肯定不是一个好的 OO 设计。

有什么好的 OO 方法可以解决这个问题?我正在考虑使用观察者模式(所以游戏是主题,玩家,地图和怪物是观察者),但这没有任何意义:这样观察者将不得不让主题知道任何变化,即显然不是正确的使用方式。

非常欢迎任何提示。

非常感谢:)

【问题讨论】:

    标签: oop design-patterns


    【解决方案1】:

    您为什么不尝试映射操作?

    每一个动作都有一个反应。所以让我们说吃豆子击中食物。这是一个动作,“击中食物”,它反过来会产生一个反应(通知地图,或食物,或任何你喜欢的东西)食物不再存在。

    现在想象一下吃豆人击中了一个怪物,那是另一个动作……对此会有什么反应?好吧,它可能会导致怪物死亡(调用 BeDeath 方法 :P)或者它可能导致吃豆人死亡......无论它是什么,它都允许您将动作链接到游戏中的反应。

    这意味着游戏的逻辑,规则将在游戏类中,此外,游戏类已经知道所需的所有元素并且可以与每个元素进行通信。

    编辑:一个简单的例子(非常简单,随着游戏变得越来越复杂,您需要更好地考虑动作和反应结构)

    public void IGameInfo
    {
      List<Monster> Monsters {get;}
      Pacman Pacman {get;}
      Map Map {get;}
    }
    
    public void ComputeReactions()
    {
      foreach (actionChecker in Actions)
      {
        actionChecker.Check(gameInfo);
      }
    }
    
    public void ComputeDotEaten(IGameInfo gameInfo)
    {
      foreach (dot in gameInfo.Map.Dots)
        if (pacman.location == dot.location)
          dot.MarkEaten();
    }
    
    public void ComputeMonsterEaten(IGameInfo gameInfo)
    {
      foreach (Monster in gameInfo.monsters)
        if (gameInfo.pacman.location == gameInfo.monster.location &&
            gameInfo.pacman.Invulnerable)
          monster.MarkDeath();
        else
          Game.EndGame();
    }
    

    或者,如果您愿意,您也可以映射反应

    public void ComputeDotEaten(IGameInfo gameInfo)
    {
      foreach (dot in gameInfo.Map.Dots)
        if (pacman.location == dot.location)
          Reactions["DotEaten"].Execute(dot);
    }
    

    请注意,要使其正常工作,您的所有反应都必须共享一个共同的签名(即,获取一个转换为预期参数的对象数组)

    【讨论】:

    • 非常感谢您的回复,这听起来是个好主意。但是你将如何实施这样的系统?让 update() 方法返回操作?但是,如果你在播放器上使用 update() 方法,播放器应该能够检查是否有怪物/食物被击中,所以它需要这些位置作为参数,仍然会造成真正的混乱......
    • 不,更新、计算、绘制。首先调用更新,然后计算对游戏类中发生的事情的反应,最后,一旦所有内容都调整好,就绘制场景。
    • 所以把第一步分成两步(计算方法)?或者调用 update() 然后计算 Game 类中的动作?但是在实践中如何检查例如怪物是否被击中?两个对象的 getPosition() 方法?还是我想错了?您能否发布一个非常简单的示例来说明您的意思? Tyvm
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 2015-09-06
    • 2012-11-13
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多