如何高效/正确地存储等距游戏的不同类(单个超类的所有子类型)的地图?



在过去的几周里,我一直在尝试在业余时间开发一款等距、基于瓷砖的大亨风格的制作游戏(实际上只是为了看看我是否有能力),我遇到了一个我正在努力的设计挑战。我已经成功地实现了地面瓷砖并使用 Tile 类(保存在 Map 类中)绘制它们等,但我希望现在能够在瓷砖上添加对象。

我已经能够使用一个相当笨拙的调试类创建和渲染对象(如表),该类作为std::map<int, Object>包含在 Map 类中,并为对象提供 X/Y 坐标,以便 Map 可以在正确的位置渲染它们。所有这些,虽然可能不是最好的方法,但都有效,直到我尝试对对象进行子类化以赋予它们不同的功能。

我(错误地)认为我能够为地图上的所有对象提供某种异构容器,我可以在游戏循环期间循环并运行特定于对象类型的逻辑。我还没有完全确定游戏的范围,所以目前我只是给不同的对象不同的成员变量 - 例如具有容量的容器对象,具有能量等级的加热器对象等等。我的第一个测试是能够在地图上放置项目,然后能够查询容器对象的所有容量的总和 - 只是为了确保它在继续下一步之前有效。

我一直在尝试使用OpenTTD和一些Habbo Hotel模拟器等开源项目来解决这个问题,但无济于事。我很想听听其他人认为解决这个问题的最佳方法是什么。

我已经尝试过在地图中dynamic_cast和使用指针,但从我看到的其他答案来看,这似乎不是一个特别好的方法。我是否应该创建一个相同类型的对象池,然后在游戏循环期间遍历每个池以使事情更简单?

标准解决方案是将它们存储为基类指向具体对象的指针。

如果你有

class IObject
{
public:
virtual void render() const = 0;
virtual ~IObject(){}
};

你可以这样做

int main()
{
using IObjectUP = std::unique_ptr<IObject>;
std::map<int, IObjectUP> m;
// some concrete objects get created
for (const auto& [_, o] : m)
o->render();
}

std::unique_ptr的使用将获得所有权。如果其他人拥有某个对象,你可以使用std::shared_ptrstd::weak_ptrstd::reference_wrapper

还有一件事。std::vector是默认容器。如果没有充分的理由std::map,请使用std::vector

std::map<int, std::unique_ptr<Object>>

可能就是你所需要的。如果您的ObjectAPI 是干净的,则不需要dynamic_casts。例如,您应该能够使用:

for(auto x : objects)
{
x->draw();
x->simulate();
}

你有几个选择。

第一个是std::any/boost::any(C++17之前)。如果您的对象完全没有共同点,这将非常有用。

另一种选择是使用通用接口:

class Object {
public:
virtual void func() {};
};

并从该接口继承:

class A : public Object {
public:
void func() {/*my overload*/}
};

这样,您可以将它们存储在如下所示的容器中:

Object* obj = new A;
std::map<int, Object*> myMap;
myMap[0] = obj;

并像这样得到它:

myMap[0]->func(); // get the overloaded version

当然,当你以这种方式完成它时,你需要deleteobj

delete obj;

应该注意的是,如果在delete它时仍设置为obj,这将使myMap[0]无效。这是我不一定建议这样做的原因之一(如果你能使用它,std::any会好得多),但如果你想使用它,它就在那里。

当然,正如@Jeffrey所建议的那样,您始终可以使用智能指针。

相关内容

  • 没有找到相关文章

最新更新