• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
0
Question by KieveKRS · Jul 03, 2016 at 07:59 PM · serializationsaveloadsave-to-filecollectible

Storing the state of a collectible in save-file?

With apologies, my Google-fu has failed me. I've found dozens of topics on collectibles and not one has been able to address my actual question.

What is the ideal method for saving a "Collectible" game object's state in a save-file?

What I'm trying to do right now is modify the "RollerBall" tutorial with a pause menu that allows you to save/load the level, preserving the player's position, score ("count") and the state of the various collectible cubes. I managed to get the pause menu in place and have code skeletons in place for saving & loading, but I have no idea how to go about preserving the Active/Inactive state of each specific collectible object.

Since I'm using t$$anonymous$$s as a primer and test-bed to master more complex concepts down the road, I'm really looking for a method or structure that could handle multiple different types of collectibles (say a player picks up two objects worth 100pts and a t$$anonymous$$rd worth 500pts. Score-save is easy, knowing w$$anonymous$$ch collectibles were picked up and disabling them on reload is my stumbling block).

To be clear, I'm using serialization and have no intention of calling PlayerPrefs if I can help it.

EDIT: So t$$anonymous$$s is the code for my Save/Load class, w$$anonymous$$ch gets called from "MenuEsc" by method. Works beautifully for saving player position, but I'm still having a miserable time trying to wrap my brain around the problem of saving collectibles' "picked-up" status.

 using UnityEngine;
 using System;
 using System.Collections;
 using System.Runtime.Serialization.Formatters.Binary;
 using System.IO;
 
 public class MenuSaveLoad : MonoBehaviour
 {
 
     public static void SaveGame()
     {
         BinaryFormatter bf = new BinaryFormatter();
         FileStream file = File.Create(Application.persistentDataPath + "/savegame.sav");
 
         LevelData data = new LevelData();
         
         //Write game data into save structure here
           
         data.PositionX = PlayerController.Instance.PlayerPosition.x;
         data.PositionY = PlayerController.Instance.PlayerPosition.y;
         data.PositionZ = PlayerController.Instance.PlayerPosition.z;
 
         bf.Serialize(file, data);
         file.Close();
         print("Saved");
     }
 
     public static void LoadGame()
     {
         if (File.Exists(Application.persistentDataPath + "/savegame.sav"))
         {
             BinaryFormatter bf = new BinaryFormatter();
             FileStream file = File.Open(Application.persistentDataPath + "/savegame.sav",FileMode.Open);
 
             LevelData data = (LevelData)bf.Deserialize(file);
             file.Close();

             //Sets player position straight from load data.                        
             //Setting via Vector3 variable is preferable, but Roll-a-ball code handles player movement oddly.
 
             GameObject.Find("Player").transform.position = new Vector3(data.PositionX,data.PositionY,data.PositionZ);
             
             print("Loaded");
         }
 
     }
 
 }
 
 [Serializable]
 class LevelData
 {
     //store "public" variables for saving & loading
     public float PositionX, PositionY, PositionZ;    
 }


The tutorial code is kind of a pain t$$anonymous$$s way, since it lumps player movements, interactions, and the "count" all into the PlayerController class. The only script on the collectibles themselves is the Rotator that makes them spin. -I'm not adverse to moving t$$anonymous$$ngs around if needed, just saying "t$$anonymous$$s is my situation, please be clear about what I should add and where."

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by KieveKRS · Jul 05, 2016 at 08:13 AM

Posting t$$anonymous$$s here for an answer, in case anyone else had the same kind of question. Keep in mind, t$$anonymous$$s is based on a modification of the Roll-A-Ball tutorial. I feel certain there are more... elegant ways to structure the whole system.

First up, GameControl.cs

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class GameControl : MonoBehaviour
 {
     public static GameControl Instance;
     public static int TC = 12;
     public GameObject[] Pickups = new GameObject[TC];
     public bool[] Collected = new bool[TC];
     public int Count;
     public Vector3 PlayerPosition;
     
     void Awake()
     {
         if (Instance == null)
         {
             DontDestroyOnLoad(gameObject);
             Instance = t$$anonymous$$s;
         }
         else if (Instance != t$$anonymous$$s)
             Destroy(gameObject);
 
         var i = 0;
         foreach (GameObject go in GameObject.FindGameObjectsWithTag("Pick-up"))
         {
             go.SetActive(true);
             print("Pickup " + i + " initialized");
             go.name = i.ToString();
             Pickups[i] = go;
             Collected[i] = false;
             i++;
         }
     }
 
     public void InitializeNR()
     {
         var i = 0;
         foreach (GameObject go in GameObject.FindGameObjectsWithTag("Pick-up"))
         {
             go.SetActive(true);
             print("Pickup " + i + " initialized");
             Pickups[i] = go;
             Collected[i] = false;
             i++;
         }
     }
 }

GameControl is an empty game object holding t$$anonymous$$s script, w$$anonymous$$ch is meant to act as a level manager, and persists between reloads or game load/save.

  • 'TC' is 'Total Collectibles' (or 'Pick-ups' I guess, as the tutorial preferred to call them). A single static int means only having to change one number to affect the Array and For()-loop sizes.

  • Awake() initializes the collectibles in three ways. It renames each collectible to a simple integer, w$$anonymous$$ch can double as its array index value later on. It then stores the GameObject in the 'Pickups' array, and resets the 'Collected' state to false.

  • The 'InitializeNR' method is called from the pause menu's "Reset" and "Load Game" methods. It does the same t$$anonymous$$ng as the 'Awake' initialization, but omits the renaming of the game objects (No Rename).

  • GameControl also takes the scorekeeping function away from PlayerController

Next, changes to PlayerController.cs

 private int goID;
 void OnTriggerEnter(Collider other)
     {
         if (other.gameObject.CompareTag ("Pick-up"))
             {                
                 other.gameObject.SetActive(false);
                 goID = int.Parse(other.gameObject.name);
                 GameControl.Instance.Collected[goID] = true;
                 print("Got Pickup " + goID);
                 GameControl.Instance.Count = GameControl.Instance.Count + 1;
             }
     }

  • goID = "game object ID." Now that collectibles are renamed as integers, we can parse them as int values and track their array position. When the player 'collects' one, it updates the Collected[] array in GameControl, for data-saving.

Last, the MenuSaveLoad.cs

 using UnityEngine;
 using UnityEngine.SceneManagement;
 using System;
 using System.Collections;
 using System.Runtime.Serialization.Formatters.Binary;
 using System.IO;
 
 public class MenuSaveLoad : MonoBehaviour
 {
     public static void SaveGame()
     {
         BinaryFormatter bf = new BinaryFormatter();
         FileStream file = File.Create(Application.persistentDataPath + "/savegame.sav");
 
         LevelData data = new LevelData();
 
         //Write game data into save structure here
         GameControl.Instance.PlayerPosition = PlayerController.Instance.transform.position;
 
         data.PositionX = GameControl.Instance.PlayerPosition.x;
         data.PositionY = GameControl.Instance.PlayerPosition.y;
         data.PositionZ = GameControl.Instance.PlayerPosition.z;
 
         data.score = GameControl.Instance.Count;
                 
         for (int x = 0; x < GameControl.TC; x++ )
         {
             data.SaveCollected[x] = GameControl.Instance.Collected[x];
         }
         
 
         bf.Serialize(file, data);
         file.Close();
         print("Saved");
     }
 
     public static void LoadGame()
     {
     
         if (File.Exists(Application.persistentDataPath + "/savegame.sav"))
         {
             GameControl.Instance.InitializeNR(); //No Rename
             BinaryFormatter bf = new BinaryFormatter();
             FileStream file = File.Open(Application.persistentDataPath + "/savegame.sav",FileMode.Open);
 
             LevelData data = (LevelData)bf.Deserialize(file);
             file.Close();
 
             //Sets player position straight from load data.                        
             //Setting via Vector3 variable is preferable, but Roll-a-ball code handles player movement oddly.
                         
             GameControl.Instance.PlayerPosition = new Vector3(data.PositionX,data.PositionY,data.PositionZ);
             GameObject.Find("Player").transform.position = GameControl.Instance.PlayerPosition;
             GameControl.Instance.Count = data.score;
 
             //Write out "collected" flag to pickups
             for (int x = 0; x < GameControl.TC; x++)
             {
                 GameControl.Instance.Collected[x] = data.SaveCollected[x];
                 if (data.SaveCollected[x] == true)
                     {
                         GameControl.Instance.Pickups[x].SetActive(false);
                         print("Pickup " + x + " disabled");
                     }
                 else
                     {
                         GameControl.Instance.Pickups[x].SetActive(true);
                         print("Pickup " + x + " enabled");
                     }
                 
             }
 
             print("Loaded");
         }
     }
 }
 
 [Serializable]
 class LevelData
 {
     //store "public" variables for saving & loading
     public float PositionX, PositionY, PositionZ;
     public int score;
     public bool[] SaveCollected = new bool[GameControl.TC];
 }

With the structures in place, saving & loading is fairly straightforward. Because of Roll-a-Ball's method of moving the player (AddForce, rather than direct translation of coordinates), I've opted to set the player's position directly on load. Ideally, I should have reset the vector forces as well - currently, reloading causes the ball to continue rolling in whatever direction it was when you paused, rather than stay still or resume its direction at the moment of saving.

Note that LoadGame() is the only place where GameControl's "Pickups[]" game-object array comes into play. Initializing them into an array made it simpler to handle the SetActive calls, and ensures the correct collectibles get disabled.

There is one significant bug with the above method (...at least, one I know about) The first load works fine, but if the player continues to reload (without resetting the scene first), more collectibles randomly disable themselves. That will be the subject of my next question, however.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image KieveKRS · Jul 05, 2016 at 08:44 AM 0
Share

For anyone tracking this, the question concerning the LoadGame glitch is here.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

59 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to save and load a player position, health, rotation in a particular scene. 0 Answers

Cannot deserialize JSON to new instances of type 'X' 1 Answer

Save location of created UI elements to JSON so it can load later 0 Answers

Save created child object to empty GameObject in GameBuild ( Not Editor ) - Save to XML? 1 Answer

Debugging Save / Load Glitches? (Serialization) 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges