如何 efficiently/properly 为等距游戏存储 类 (单个超类的所有子类型)的地图?
How to efficiently/properly store a map of varying classes (all subtypes of a single superclass) for an isometric game?
在过去的几周里,我一直在尝试在业余时间开发一个等距的、基于图块的大亨风格的制作游戏(实际上只是为了看看我是否有能力)并且我 运行 面对我正在努力应对的设计挑战。我已经使用 Tile class(保存在 Map class 中)成功地实现了地砖并绘制它们等等,但我现在希望能够在地砖上添加对象。
我已经能够使用相当笨拙的调试 class 创建和渲染对象(如 table),包含在地图 class 中作为 std::map<int, Object>
并为对象提供 X/Y 坐标,以便地图可以将它们呈现在正确的位置。所有这一切,虽然可能不是最好的方法,但在我尝试 subclass 对象以赋予它们不同的功能之前一直有效。
我曾(错误地)假设我能够为地图上的所有对象提供某种异构容器,我可以在游戏期间循环遍历它和 运行 对象类型特定的逻辑环形。我还没有完全确定游戏的范围,所以目前我只是给不同的对象不同的成员变量——比如一个有容量的容器对象,一个有能量等级的加热器对象,等等。我的第一个测试是能够将项目放置在地图上,然后能够查询容器对象的所有容量的总和 - 只是为了确保它在继续下一步之前有效.
我一直在尝试使用 OpenTTD 和一些 Habbo Hotel 模拟器等开源项目来解决所有这些问题,但无济于事。我很想听听其他人认为解决这个问题的最佳方法是什么。
我已经尝试 dynamic_cast
在我的地图中使用指针,但从我看到的其他答案来看,这似乎不是一个特别好的方法。我是否应该创建一个相同类型的对象池,然后在游戏循环期间遍历每个池以使事情变得更简单?
std::map<int, std::unique_ptr<Object>>
可能就是您所需要的。如果你的 Object
API 是干净的,你不应该需要 dynamic_cast
s。例如你应该可以使用:
for(auto x : objects)
{
x->draw();
x->simulate();
}
标准解决方案是将它们存储为指向具体对象的基 class 的指针。
如果你有
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_ptr
、std::weak_ptr
或 std::reference_wrapper
的对象。
还有一件事。 std::vector
是默认容器。如果 std::map
没有充分的理由,请使用 std::vector
.
你有几个选择。
第一个是std::any
/boost::any
(before 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
当然,您需要 delete
obj
当您以这种方式完成后:
delete obj;
需要注意的是,如果你delete
时它仍然设置为obj
,这将使myMap[0]
失效。这是我不一定推荐这样做的原因之一(std::any
如果你可以使用它会更好),但如果你想使用它就在那里。
当然,你总是可以使用智能指针代替,如@.
在过去的几周里,我一直在尝试在业余时间开发一个等距的、基于图块的大亨风格的制作游戏(实际上只是为了看看我是否有能力)并且我 运行 面对我正在努力应对的设计挑战。我已经使用 Tile class(保存在 Map class 中)成功地实现了地砖并绘制它们等等,但我现在希望能够在地砖上添加对象。
我已经能够使用相当笨拙的调试 class 创建和渲染对象(如 table),包含在地图 class 中作为 std::map<int, Object>
并为对象提供 X/Y 坐标,以便地图可以将它们呈现在正确的位置。所有这一切,虽然可能不是最好的方法,但在我尝试 subclass 对象以赋予它们不同的功能之前一直有效。
我曾(错误地)假设我能够为地图上的所有对象提供某种异构容器,我可以在游戏期间循环遍历它和 运行 对象类型特定的逻辑环形。我还没有完全确定游戏的范围,所以目前我只是给不同的对象不同的成员变量——比如一个有容量的容器对象,一个有能量等级的加热器对象,等等。我的第一个测试是能够将项目放置在地图上,然后能够查询容器对象的所有容量的总和 - 只是为了确保它在继续下一步之前有效.
我一直在尝试使用 OpenTTD 和一些 Habbo Hotel 模拟器等开源项目来解决所有这些问题,但无济于事。我很想听听其他人认为解决这个问题的最佳方法是什么。
我已经尝试 dynamic_cast
在我的地图中使用指针,但从我看到的其他答案来看,这似乎不是一个特别好的方法。我是否应该创建一个相同类型的对象池,然后在游戏循环期间遍历每个池以使事情变得更简单?
std::map<int, std::unique_ptr<Object>>
可能就是您所需要的。如果你的 Object
API 是干净的,你不应该需要 dynamic_cast
s。例如你应该可以使用:
for(auto x : objects)
{
x->draw();
x->simulate();
}
标准解决方案是将它们存储为指向具体对象的基 class 的指针。
如果你有
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_ptr
、std::weak_ptr
或 std::reference_wrapper
的对象。
还有一件事。 std::vector
是默认容器。如果 std::map
没有充分的理由,请使用 std::vector
.
你有几个选择。
第一个是std::any
/boost::any
(before 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
当然,您需要 delete
obj
当您以这种方式完成后:
delete obj;
需要注意的是,如果你delete
时它仍然设置为obj
,这将使myMap[0]
失效。这是我不一定推荐这样做的原因之一(std::any
如果你可以使用它会更好),但如果你想使用它就在那里。
当然,你总是可以使用智能指针代替,如@