我想为我正在进行的游戏实现一个检查点系统,为此我需要复制一个级别中的所有可变对象,以便创建一个级别的独立副本。在一个简化的例子中,我的类看起来像这样:
public class GameObject
{
...
private Level level;
...
}
public class Level
{
...
private List<GameObject> gameObjects;
...
}
但是有一个问题:当我想要复制一个级别及其对象时,引用变得不一致。例如,我可以深度复制我的Level-instance并深度复制所有gameObjects。但是当我这样做时,GameObject中的Level-reference不再指向“正确”(新)级别。在这种情况下,我可以为每个对象调用一个方法并重置其级别。但随着越来越多类型的对象被包含,这将变得越来越复杂,因为对象可能或可能没有自己创建的对象集,然后这些引用也必须更新等等。
对于这种情况,有什么类似设计模式的东西,或者特定于Java的东西会使这个问题变得更容易吗?
修改
我刚才有一个想法:如何为每个GameObject创建一些包装类,如下所示:
public class GameObjectWrapper
{
...
private long gameObjectID;
private Level level;
...
}
这将作为对所有需要引用的对象的引用。
每个GameObject都会得到一个唯一的id,level会有一个地图将每个id链接到一个实际的GameObject(类似GameObject Level.getObject(long id)
)。虽然这可能会奏效,但我仍然觉得必须有更好的解决方案。
编辑:进一步澄清
似乎我没有把我的问题弄清楚,所以我会简化并概括一点:
我有两个示例对象(objectA
和objectB
):objectA
包含对objectB
的引用,objectB
包含对objectA
的引用}。
这样的事情:
public class MyObject
{
private MyObject partner;
...
public MyObject shallowCopy()
{
return new MyObject(partner);
}
}
我想复制两者。通过浅拷贝,我得到两个新对象:newObjectA
和newObjectB
。 newObjectA
存储的引用(应该指向newObjectB
)仍然指向原始objectB
,反之亦然。
MyObject objectA = new MyObject(), objectB = new MyObject();
objectA.setPartner(objectB);
objectB.setPartner(objectA);
MyObject newObjectA = objectA.copy(); //newObjectA's partner now is objectB (instead of newObjectB)
MyObject newObjectB = objectB.copy(); //newObjectB's partner now is objectA (instead of newObjectA)
现在,我可以“只是”遍历我的所有对象并将旧对象映射到新对象,但这对我来说似乎很慢而且太复杂了。解决此问题的最简单,最有效的方法是什么?
注意:我考虑将此发布到gamedev.stackexchange.com,但我发现这更像是一个编程和设计特定问题,而不是游戏开发特定问题。
答案 0 :(得分:3)
如果您只复制Level
对象并希望其列表中的引用指向新副本,则无法在复制方法中循环它们。
这样的事情:
class Level {
List<GameObject> gameObjects;
Level copy() {
Level level = new Level();
level.gameObjects = new ArrayList<>(gameObjects.size());
for (GameObject gameObject : gameObjects) {
level.gameObjects.add(gameObject.copyWithReference(level));
}
return level;
}
}
class GameObject {
Level level;
GameObject copyWithReference(Level level) {
GameObject gameObject = new GameObject();
gameObject.level = level;
return gameObject;
}
}
编辑:
您可以拥有gameObjects
的容器:
class GameObjectSet {
Level level;
List<GameObject> gameObjects;
GameObjectSet copy(Level newLevel) {
GameObjectSet gameObjectSet = new GameObjectSet();
gameObjectSet.level = newLevel;
// copy all gameObjects and make them point to new gameObjectSet
return gameObjectSet;
}
}
现在,您可以在GameObjectSet
课程和Level
课程中同时参考GameObject
。
答案 1 :(得分:2)
检查点的经典解决方案是Memento pattern。
也就是说,如果您想要持久检查点,Java序列化可能就是您所寻找的,因为除了对象状态(在同一个流中)之外它还保留了对象标识。这将是:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(level);
}
byte[] checkpoint = baos.getBytes();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(checkpoint)) {
Level level = (Level) ois.readObject();
assert level == level.getGameObjects().get(0).getLevel();
}