建模游戏对象层次结构的最优雅,最有效的方法是什么?(设计困扰)

zyn*_*dor 9 c++ oop

我有以下代码,想想c ++中的简单射击游戏:

// world.hpp
//----------
class Enemy;
class Bullet;
class Player;

struct World
{
  // has-a collision map
  // has-a list of Enemies
  // has-a list of Bullets
  // has-a pointer to a player
};

// object.hpp
//-----------
#include "world.hpp"

struct Object
{
  virtual ~Object();
  virtual void Update() =0;
  virtual void Render() const =0;

  Float xPos, yPos, xVel, yVel, radius;  // etc.
};

struct Enemy: public Object
{
  virtual ~Enemy();
  virtual void Update();
  virtual void Render() const;
};

// Bullet and Player are similar (they render and update differently per se,
/// but the behavior exposed to World is similar) 

// world.cpp
//----------
#include "object.hpp"

// object.cpp
//-----------
#include "object.hpp"
Run Code Online (Sandbox Code Playgroud)

这有两个问题:

1,游戏对象了解其他游戏对象.

必须有一个了解所有物体的设施.它可能会或可能不必暴露所有对象,它必须公开其中的一些,具体取决于询问者的参数(位置,一个).这个设施应该是世界.

对象必须知道它们所处的世界,以查询碰撞信息和其他对象.

这引入了一个依赖项,其中Objects'和World的实现都必须访问对象的头,因此World不会直接包含它自己的头,而不是包含object.hpp(后者又包含world.hpp).这让我感到不舒服 - 我不觉得world.cpp应该在我对object.hpp进行更改后重新编译.World不觉得它应该与Object一起使用.感觉就像糟糕的设计 - 如何解决?

2,多态性 - 可以而且应该用于除了代码重用和游戏实体的逻辑分组之外的任何事情(以及如何)?

Enemies,Bullets和Player将以不同的方式更新和呈现,因此虚拟的Update()和Render()功能 - 相同的界面.它们仍然保存在单独的列表中,其原因在于它们的相互作用取决于两个相互作用对象的列表 - 两个敌人互相反弹,子弹和敌人相互摧毁等.

这是超出Enemy和Bullet实施的功能; 这不是我关心的问题.我觉得除了相同的界面之外还有一个将敌人,子弹和玩家分开的因素,这可以而且应该以其他方式表达,允许我为所有人创建一个列表 - 因为他们的界面是相同的!

问题是如何将对象类别从另一个对象类别中包含在同一列表中.Typeid或其他形式的类型识别是不可能的 - 但那么还有什么方法可以做到这一点?或者这种方法可能没有错?

Dun*_*ers 6

我想你想把一个对世界的引用交给游戏对象.这是依赖注入的一个非常基本的原则.对于冲突,World可以使用策略模式来解决特定类型的对象如何交互.

这将不同对象类型的知识保留在主要对象之外,并将其封装在具有特定于交互的知识的对象中.


cop*_*pro 5

这可能是我在设计类似程序时遇到的最大问题.我已经确定的方法是认识到一个对象真的不关心它在绝对意义上的位置.所有它关心的是它周围的东西.因此,World对象(我更喜欢单个对象的方法,出于很多原因,您可以在Internet上搜索)维护所有对象的位置,并且他们可以向世界询问附近有哪些对象,其他对象在哪里与它有关,等World不应该关心的内容Object; 它几乎可以容纳任何东西,而Object它们自己将定义它们如何相互作用.事件也是处理对象交互的好方法,因为它们提供了一种方法World来告知Object正在发生的事情而不关心它Object是什么.

从隐藏信息Object有关的所有对象是一个好主意,不应该与隐藏有关信息混淆的任何 Object.以人为本思考 - 了解并保留有关许多不同人的信息是合理的,尽管您只能通过遇到这些信息或让别人告诉您这些信息来找到这些信息.

再次编辑:

行.我很清楚你真正想要的是什么,那就是多次调度 - 能够多样化地处理函数调用的许多参数类型的情况,而不仅仅是一个.遗憾的是,C++本身不支持多个调度.

这里有两个选择.您可以尝试使用双重调度或访客模式重现多个调度,或者您可以使用dynamic_cast.您想要使用哪种情况取决于具体情况.如果你有很多不同的类型可以使用它,dynamic_cast可能是更好的方法.如果您只有少数,双重调度或访客模式可能更合适.