存储和管理(正确反序列化对象以及反序列化后如何使用它们

Store and manage (de)serialized objects properly and how to work with them after deserialisation

我想使用来自 class 的数据将多个对象加载并渲染到游戏世界,并使用(反)序列化实现 save/load 功能,但我不知道如何正确地将它们重新引入到游戏世界程序,因为它们包含 render/display 它们所必需的瞬态对象。

我第一次 运行 我的程序,它试图从一个名为 'Data.txt' 的 txt 文件加载数据,该文件不存在。该程序创建一个新的 Data.txt 文件,向其中添加一个名为“campfire”的 Campfire 对象并保存。只要程序 运行s 它就会被渲染到屏幕上(这部分工作正常)。关闭程序并重新启动它后,它应该检查文件,找到它并加载 ArrayList 'data' 以及之前的 saved/serialized campfire 对象。然后它向它添加另一个相同类型的对象并渲染它们。这部分在我的 Campfire class.

的渲染方法中抛出 NullPointerException

这是主要的 class:

public class MainClass extends ApplicationAdapter{
public static SpriteBatch batch;    

private Data data;
private Campfire campfire;

@Override
public void create () {
    batch = new SpriteBatch();

    data = new Data();
    data.load();

    campfire = new Campfire();
    data.addCampfire(campfire);

    System.out.println(data.getSize());
    data.save();
}

@Override
public void render () {
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.begin();
        data.render(batch);
    batch.end();
}

加载所有文件的class:

public class Data {
private String filename;
private ArrayList<Campfire> data;

 public Data() {
    data = new ArrayList<Campfire>(); 
    filename = "Data.txt"; 
 }

 public void save(){
     try {  
         FileOutputStream file = new FileOutputStream (filename); 
         ObjectOutputStream out = new ObjectOutputStream (file);       

         out.writeObject(data); 
         out.close(); 
         file.close();      
     }catch (IOException e) {e.printStackTrace();} 
 }

public void load() {
     try {                                  
         FileInputStream file = new FileInputStream (filename); 
         ObjectInputStream in = new ObjectInputStream(file); 

         data = (ArrayList<Campfire>)in.readObject();  
         in.close(); 
         file.close();  
     } 
     catch (IOException io) {System.out.println("File not found.");} 
     catch (ClassNotFoundException ex) {System.out.println("Cant load from file.");}
 } 

 public void addCampfire(Campfire object) {
     data.add(object);
 }
 public void render(SpriteBatch batch) {
     for(int i = 0;i < data.size(); i++) {
         Campfire camp = data.get(i);
         camp.render(batch);
        //data.get(i).render(batch);
     }
 }

 public Campfire get(int index) {
    return data.get(index);
 }

 public void set(int index, Campfire object) {
     data.set(index, object);
 }

 public int getSize() {
     return data.size();
 }

这是在 render() 中抛出 NullPointerException 的 class:

public class Campfire implements Serializable{
private transient Texture img;
private int id;

public Campfire() {             
    img = new Texture("campfire.png");
}

public void render(SpriteBatch batch) {
    batch.draw(img, 200,200, 2000,2000);
}

原因是纹理 'img' 不存在,因为我需要对其使用 transient 关键字,因此它不会是 saved/loaded to/from 数据文件。

我需要一种不同的方法来根据我从中加载的数组列表创建对象。我无法使用 arrayList 中的对象,因为它们无法呈现(transient 关键字必须分配给 Texture() 对象——它不可序列化)。我看过很多教程并阅读了大量有关该主题的文章,但我找不到提到如何重新引入该对象的文章,以便我可以利用其依赖于不可(反)序列化部分的功能。

所以这是我的问题: 有没有比我的尝试更好的方法(再次创建对象,然后将反序列化的值分配给它?我不知道如何在反序列化过程后正确加载和使用 'data' ArrayList,我感谢每一个关于如何将他们重新引入计划的建议。

提前致谢, M

我建议不要为此使用 java 序列化。它在序列化格式的大小方面具有优势,但根据我的经验,使用起来有点麻烦。您遇到过这种痛苦,因为您无法序列化那个 Texture 对象,因为它不是 Serializable。您通过将其标记为瞬态来避免该问题,这是一个错误。可能有处理不属于您的对象的解决方法 - 注册一些处理程序,但我不知道细节,因为我避免了它。

相反,选择一种友好的格式,例如 json。这意味着您的序列化数据文件会更大一些,但是,您可以打开它并阅读它,看看您保存了什么。

并且... libgdx 附带了一些工具。查看他们的 wiki 页面使用 here

还有其他选择 - 就像 googles gson 库一样,但为什么不坚持使用 libgdx 的工具 - 我发现它们非常可靠。

只是一个额外的警告 - 如果您在 Android 中使用某种代码 obfuscation/minimization 工具,您需要将其配置为不重命名您序列化的任何对象,因为它确实依赖于在包和对象名称上进行反序列化。如果您保存游戏状态,也需要注意 - 然后更改对象的 names/packages,然后尝试加载该游戏状态。