Creating Save Functionality for Embedded Objects

Hello All,

I am working on a “Save” function for my current program. After doing a little research it seems the method is to have any meaningful data from the program save to some external source like a text document or a spreadsheet. That includes any variables or objects that may be changing as the user interacts with the program. Then have some way for the user to “Load” those variables into the program during use.

Where I am having a small problem is some of the objects in my program have other objects as part of their attributes and I’m a little unsure of how to proceed. Here is the format of two of the objects from my design that I would need to save.

class Tile {
  Tile(float, float, int, String) {
  }

class Spot {
  Spot(float, float, float, Tile, Tile, Tile, String) {
  }

As you can see, my Spot objects all have three Tile objects as part of their attributes. I’m having some trouble visualizing the logic of how the save functionality would work in this case. So how would I effectively save the data from a Spot object so the user could save progress and come back later?

Thank you as always for the help.

You could keep track of your tiles using an arraylist, give them an id based on index in array, and use that to inform the class, which object to save.

Interesting. I haven’t used ArrayLists just yet in this program but I have come across them before.

It does sound like I have some of what you are suggesting. The code initializes 19 Tile objects in the setup() in an Array of Tiles. As the user uses the program, those tiles will have the int and the String information changed. That change in information gets updated for each item in the Array of Tiles at the end of the draw().

I thought what I would need to do to write a save function would be to save the Tiles and the Spots in some external file. I understand how to do that with the Tile objects because those are just number and String variables that change as the user interacts with the program. The Spots, which have tile objects in them I don’t understand as well.

How do I add a reference in a file outside of the processing program to each tile object that exists in a Spot object? My thought was to have the Tiles all save in a spreadsheet and then have some reference in each Spot for the corresponding location in the spreadsheet. I’m not sure if that is the best method for this project, or frankly if that would work.

Ultimately its up to you what type of file you decide to save your data in. These are some common types, .txt, .csv, .xls.

For simplicity sake, let’s use the txt format. This format can be read on pc using loadStrings() and we can use a filewriter to save data to the file.

In the txt file you could save all your objects in separate lines and separate the data for the objects using commas or something else.

When we read the file back, we then use loadStrings() . This will give us an array of Strings with each index corresponding to the objects, and the index of the string will correspond to the index of the object in the program.

This way recreating the data required by the program just requires us to loop through the txt file, and for every index we can create a new instance of the object and pass the necessary parameters.

The main thing is to keep a track of which variables are saved in each line and which order they are in.
Now this might get a bit unwieldy if you are saving lots of data for each object, but if you don’t have too many variables to store/read then this should be fine.

Hope that makes sense.

It does, and I have done this before. I understand that process, which makes me think my question is not clear.

Let’s say we use a text document for example. My tile array would look something like this:

Tile1, .5, .5, 0, “empty”
Tile2, .8, .8, 0, “empty”

Then when I use loadStrings() it would create and Array with each line from the text file being an element of the Array. I would have some code that would break each line up and recognize that each comma separated element corresponded to part of a Tile Object.

If I attempt to do this with my Spot Array I am confused as to what this needs to look like because I don’t think I can write it like this:

Spot 1, 1.5, 1.5, 1.5, Tile1, Tile2, Tile3, “empty”

In the spots where I have Tile1, Tile2, and Tile3 there needs to be some kind of reference to the Tile object that the Spot is referencing. What actually goes here? Does this need to be a reference to the file where the tile information exists? Do I have some kind of reference point in the code so when I use something like loadStrings() it knows to check the Tile objects that are already loaded into the program? (Obviously in this case I would need to make sure the Tile objects all get loaded first).

Which is why i recommended you assign each object an index id, which matches its location in the array, then instead of saving tile1, tile2, tile3 you save the index of the tiles.

You could do this once in setup. Note that if these change for any reason, then you will need a method to update them.

Then when loading the data we load the tiles first which populates the array, with each index matching the corresponding tile.

Now we can load the rest of the data, and use the array id to retrieve the tile in the corresponding index of the array.

1 Like

Gotcha, thank you for the recommendations.

2 Likes

also heres a little class I wrote a while back to write files please note it is using a very old version of my fileWriter class as I encountered a bug on my new one.

In any case just copy it to your sketch file in a new tab, and paste the code if you wish to use it.

Summary
class fileOutput {
  PrintWriter output;
  boolean save, onMouseUp, mdown, debug, append, appendFile, match, append2, overWrite;
  int counter, counter2;
  File file;
  String location, filePath, folderPath = "";

  fileOutput() {
    appendFile = true;
  };

  fileOutput(boolean a) {
    overWrite = a;
    appendFile = a;
  };

  fileOutput(String location) {
    //appendFile = true;
    checkLocation(location);
    open();
  };

  fileOutput(String location, boolean m) {
    appendFile = m;
    checkLocation(location);
    open();
    //file = dataFile(location);
    //filePath = file.getPath();
  };

  void checkLocation(String location) {
    int count = 0;
    for (int i=location.length()-1; i>-1; i--) {
      char c = location.charAt(i);

      if (c=='\\') {
        folderPath = location.substring(0, i);
        this.location = location.substring(i, location.length());
        count ++;
        break;
      }
    }
    if (count==0)this.location = location;

    String s1 = folderPath.replace("\\", "");
    String s2 = this.location.replace("\\", "");



    println("checkLocation: " + s1 + "\\" + counter + "\\" + s2);
  };

  void update(String folder, String file, int counter ) {
    //filePath = folder + "\\" + file;
    this.folderPath = folder +"\\";
    this.location = file;
    this.counter = counter;
    appendFile = false;
    overWrite = true;
  };

  void saveData() {
    if (mdown()) {
      checkFile( location, append);
    }
    if (mdown)
      output.println(mouseX + ",+ " + mouseY);
    close();
  };

  void open() {
    checkFile(location, append);
  };

  void write(String s) {
    // use this one to append the file created
    if (output!=null) {
      output.println(s);
      println("successful write", s);
      output.flush(); // Writes the remaining data to the file
      //output.close(); // Finishes the file
    } else print("Create Save File");
  };

  void write(float f) {
    output.println(str(f));
  };

  void write_(String s) {
    //use this one to overwrite file
    //output.write(s);
    output.println(s);
    if (output!=null) {
      println("successful overwrite", s);
      output.flush(); // Writes the remaining data to the file
      output.close(); // Finishes the file
    } else println("failed", location, folderPath);
  };

  void write(String []s) {
    String s1 = "";
    for (int i=0; i<s.length; i++) {
      s1 += s[i];
    }
    if (s1!=null&&output!=null)output.println(s1);
  };

  void close() {
    if (output!=null) {
      output.flush(); // Writes the remaining data to the file
      output.close(); // Finishes the file
    } else println(location, folderPath);
  };

  boolean onMouseUp() {
    boolean k = false;
    if (pos()&&!mousePressed&&onMouseUp) {
      onMouseUp = false;
    } else if (pos()&&mousePressed&&!onMouseUp) {
      output.println(counter);
      onMouseUp = true;
      k = false;
    } else if (onMouseUp&&!mousePressed) {
      k = true;
      onMouseUp = false;
      counter ++;
    }

    return k;
  };

  boolean mdown() {
    boolean k= false;
    if (mdown)k = false;
    if (mousePressed&&!mdown) {
      mdown = true;
      k = true;
    }
    if (!mousePressed)mdown = false;    
    return k;
  };

  boolean pos() {
    return mouseX>0&&mouseX<width&&mouseY>0&&mouseY<height;
  };

  void checkFile(String location, boolean append) {
    if (appendFile) {

      file = dataFile(folderPath + "/" + location);
      filePath = file.getPath();
      filePath = filePath.replace(location, "");
      String[] list = null;
      if (debug)println("checkFile");
      if (listNames(filePath)!=null&&!match) {
        println(filePath);

        list = listNames(filePath);
        boolean b = false;
        for (int j=maxFolderSize; j>-1; j--) {
          //println(j);
          if (b)break;
          counter = j;
          for (int i=list.length-1; i>-1; i--) {

            int n = int(list[i]);
            if (j == n) {
              println(j + ", " + list[i]);
              counter = j;
              b = true;
              break;
            }
          }
        }
        match = true;
        if (!b)counter = -1;
        if (counter>=0)counter = counter + 1;
        else counter = 0;

        //println(counter);
      }
      //file = dataFile(folderPath + "/" + counter +"/"+ location);
      file = dataFile( location);

      filePath = file.getPath();
      filePath = filePath.replace(location, "");
    }
    println(" append",appendFile);
    //overWrite||
    if (output == null) {
      if (appendFile) {
        if (file==null)file = dataFile(folderPath + "/" + counter + "/" + location);
        output = createWriter("/data/" + folderPath + "/" + counter + "/"+ location);
      println(" append");
    } else {
        if (file==null)file = dataFile(folderPath + "/" + location);
        output = createWriter("/data/" + folderPath + "/"+ location);
        println("not append");
      }
      //println("new folderpath " + folderPath + counter + "\\" + location);
      //
      filePath = file.getPath();
      filePath = filePath.replace(location, "");
    }
    if (debug) println(filePath);
    try {

      FileWriter fw = new FileWriter(file, false);///true = append
      BufferedWriter bw = new BufferedWriter(fw);
      output = new PrintWriter(bw);
    }
    catch(IOException ioe) {
      System.out.println("Exception ");
      ioe.printStackTrace();
      println(filePath);
    }
  };
};

String[] listNames(String dir) {

  if (dir==null)return null;
  File file  = new File(dir);
  if (file.isDirectory()) {
    String names[] = file.list();
    return names;
  } else {
    // If it's not a directory
    return null;
  }
};

int totalFiles(String dir) {
  File file  = new File(dir);
  if (file.isDirectory()) {
    String names[] = file.list();
    return names.length;
  } else {
    // If it's not a directory
    return -1;
  }
};

String getFileExtension(File file) {
  String fileName = file.getName();
  if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0)
    return fileName.substring(fileName.lastIndexOf(".")+1);
  else return null;
};

and here is the sketch code.

import java.io.BufferedWriter;
import java.io.FileWriter;
int maxFolderSize = 1000;
fileOutput output;
boolean mdown = false;
String loc = "positions.txt";
ArrayList<Obj> objects = new ArrayList<Obj>();
int totalObjects = 100;
String[] data;
void setup() {
  output = new fileOutput(loc,false);
  for(int i=0;i<totalObjects;i++){
    float x = random(width);
    float y = random(height);
    float z = random(200);
    Obj o = new Obj(x,y,z,i);
    output.write(x+","+y+","+z+","+o.id);
    objects.add(o);
  }
  
  data = loadStrings("positions.txt");
  for(int i=0;i<10;i++){
    String s = data[0];
    //split the lines to retrieve the variables
    //remember the order is x,y,z,id
    // so index[0] = x .....
    String []vars = s.split(",");
    
    // this bit is not necessary but just shows you interactions you could add
    Obj tile = objects.get(parseInt(vars[3]));
    println("tile: ",tile.x,tile.y,tile.z);
    
    //find the data
    float x = parseFloat(vars[0]);
    float y = parseFloat(vars[1]);
    float z = parseFloat(vars[2]);
    int id = parseInt(vars[3]);
    
    //create new blank obj
    Obj tempTile = new Obj();
    //populate obj data
    tempTile.id = id;
    tempTile.x = x;
    tempTile.y = y;
    tempTile.z = z;
    //pass our object into the constructor
    Obj1 o = new Obj1(i,tempTile);
    // this is the data for the new objects weve created using the info stored in the text file
    println("obj1: ",o.tile1.x,o.tile1.y,o.tile1.z);
  }
  
};

void draw() {
  point(mouseX, mouseY);
  //add condition here before calling
  //click anywhere on the canvas to save
  //if(mousePressed&&!mdown){
    
  //  output.write("hello");
  //  mdown = true;
  //}
  
  if(!mousePressed)mdown = false;
  
  //use output.saveData("parameter i " + "parameter i++".....)
}

class Obj{
  int id;
  float x,y, z;
  
  Obj(){
    
  };
  
  Obj(float xx,float yy,float zz,int id_){
    x = xx;
    y = yy;
    z = zz;
    id = id_;
  };
};

class Obj1{
  int id;
  float x,y, z;
  Obj tile1,tile2,tile3;
  
  Obj1(){
    
  };
  
  Obj1(int id_,Obj Tile1){
    id = id_;
    tile1 = Tile1;
  };
  
  Obj1(int id_,Obj Tile1,Obj Tile2,Obj Tile3){
    id = id_;
    tile1 = Tile1;
    tile2 = Tile2;
    tile3 = Tile3;
  };
};