保存类的实例,然后重新创建实例

我有很多types的实体,每种types都有很多, if instanceof Spider (例如)然后在游戏代码中执行此操作…现在我到了要保存每个types的部分。

他们都是基于相同的类,实体,但实施一些自己的方法。 当我拯救他们,让我们现在说,我只是想要他们的位置和他们的健康。

所以我写的位置写健康,与所有的实体文件。

现在是这个问题,当我想加载它,我怎么知道这是一个蜘蛛,然后创建一个蜘蛛实例?

一个简单的解决办法是创建toString方法,然后写在位置和健康之前。 但是,这个问题是,当我加载时,我将不得不为每个types都有这个大的switch语句,我将不得不手动添加一个我创建的新types的情况。 这不是很友善。

什么样的方式来读取一个类的名称,然后创建一个该类的实例,而没有一个大开关的情况下?

我会避免使用像这样的reflection,并使用语言不可知的标记blob格式,或类似的东西(只是一种可能的方法实体序列化):

有一个ISaveable接口的方法,产生一个Entity给予大量的保存数据,并产生一个Entity的保存数据块。

 public interface ISaveable { public SaveHunk save(Entity e); public Entity load(SaveHunk sh); public GUID getClassID(); } 

为每个实体types(即SpiderPlayerBox等)创建ISaveable匿名实例,并让这些实例知道如何使用大量的保存数据(原始string,XML,JSON等)并创建适当的实体,如何将该实体转换回保存数据的大块。

 ISaveable spiderSaveable = new ISaveable() { @Override public SaveHunk save(Entity e) { if (e == null) return null; if (!(e instanceof Spider)) { return null; } SaveHunk hunk = new SaveHunk(getClassID(), e.getEntityID()); Spider spider = (Spider)e; hunk.put("pos", spider.getPosition().toString()); hunk.put("vel", spider.getVelocity().toString()); // and so on return hunk; } @Override public Entity load(SaveHunk hunk) { if (hunk== null) return null; if (!hunk.getClassID().equals(getClassID())) { // The GUIDs don't match, may want to handle this in the manager return null; } Spider spider = new Spider(hunk.getEntityID()); spider.setPosition(Vector3.parse(hunk.get("pos"))); spider.setVelocity(Vector3.parse(hunk.get("vel"))); // and so on return spider; } @Override public GUID getClassID() { return Spider.UNIQUE_CLASS_ID; } }; 

有一个管理员类可以有一个ISaveable实例附加到一个代表一个类的全局唯一ID(例如, Player总是1, Spider总是2,总是总是3,等等)。

 saveManager.addSaveable(spiderSaveable); 

当你保存的时候,把世界上所有的实体都传递给管理者,获取与他们的类ID相对应的ISaveable ,并把它们传递给ISaveableSaveHunk save(Entity e)方法,该方法将实体向下转换并将其重要值转换为保存数据的大块,然后将该大块写入类ID后面的文件。

 for (Entity e : world.getEntities()) { ISaveable saveable = saveManager.getSaveable(e.getClassID()); if (saveable != null) { saveFileWriter.writeHunk(saveable.save(e)); } else { // Log a warning } } 

加载时,从类ID开始读取文件,获取该类的相应ISaveable ,然后将文件中的保存数据块传递给ISaveableEntity load(SaveHunk sh)方法。

 for (SaveHunk hunk : saveHunksFromFile) { ISaveable saveable = saveManager.getSaveable(hunk.getClassID()); if (saveable != null) { world.loadEntity(saveable.load(hunk)); } else { // Log a warning } } 

限制

请注意,这只是一个单一的方法,并有其((激烈))的局限性。 ISaveable每个类types的所有ISaveable实例是讨厌的,并且需要时间遍历所有实体类。 另外,在创造世界之前,必须在某个时刻完成,否则你将无法将资料加载到所说的世界中。 另外,如果你改变一个class级的ID(比如说你想移除一个敌人types),那么如果他们有这个敌人types(这可能是可取的或不可取的,取决于你有多好处理,我的代码在这种情况下没有什么特别的)。

这也限制了你如何使用实体的基类和子类; 例如,如果你将Spider分解成CaveSpiderFieldSpider ,那么你使用哪个ISaveable ? 这实际上意味着你不能/不应该在你的世界中实例化分支类,或者如果你想要一个Spider ,那么它必须有自己的叶类,如CommonSpider ,而不是使用更通用的Spider ,否则你最终会使你的装载机复杂化,并在它们之间复制代码。 你可以有一个多步的ISaveable层次结构,其中的东西把剩下的大量的保存数据ISaveables给基类的更一般的ISaveables ,但是它大大提高了系统的复杂性,因为实体现在必须知道他们自己的全部基类。

优点

采用这种方法或类似的方法,与使用直线reflection相比,有许多优点。

  • 通过使用工厂样式的加载和保存方法,您可以避免对所有实体类进行标准初始化过程,因为类的创建和初始化知识由该类的ISaveable实例决定。
  • 您可以使用GUID并避免在保存文件中使用完全限定的类名,从而允许混淆并减less保存文件的大小,更不用说在语言之间移植保存文件(如果需要外部开发工具)。
  • 实体类(只应该关注游戏本身)和保存/加载系统(应该只关心保存和加载数据)之间存在着分离的关注点。
  • 这种方法很容易适应其他体系结构(即组件 – 实体 – 系统模型),只需稍作修改即可。
  • 通过使用一个统一的构造来表示保存数据(即SaveHunk ),您可以轻松切换实际保存文件的格式,并保留相同的代码库。
  • 避免使用开关箱来支持地图查找。

toString()部分本身并不是很糟糕。 我认为处理重新加载对象的一种方法是通过使用Reflection。

 // an example of a forName argument: "java.util.Map" Class loadedObjectClass = Class.forName(xml.getElementValue("clazzName")); Object loadedObject = loadedObjectClass.newInstance(); 

我也会注意到一些人会担心过度使用types检查, 这最终会导致你想要避免的大规模交换语句。 例子:与其有一个玩家碰撞方法来检查某物是否是蜘蛛,并且损坏了玩家,可以尝试为每个物体设置一个onTouch()方法,这对大多数物体不起作用,并且会损害Spider类中的触摸器。 (然后,如果触摸器不能被破坏,你可以使他们的damage()方法什么也不做)

最后,如果明智的用户在明文保存文件中看到类名,就要小心游戏。 没有必要强行让单人黑客成为不可能的事情,但是你可能只是想混淆一下保存文件,使之变得困难。

首先Java有很好的OO让它闪耀

每次你用instanceof创建一个新的实例方法并在子类中重新实现

你也可以使用getter方法返回标准方法中使用的基本运动速度等参数

做同样的保存和加载(就像@shotgunninja一样,但每个实体是可以的)

和加载时有hashmap(或只是一个数组,如果你可以确保顺序将保持不变;))包含实体工厂创建一个空白的实体,你可以调用load(SaveChunk) ,将configuration它(作为奖金这样可以在GC暂停时变得太过分了)