Hello there,
in my last projects it has always been enough to safe player data in the player prefs like level, gold etc. but for my new project i need to find another way to safe data.
It’s a survival game project and here is what i need to safe:
everything that get’s crafted and placed
a tree or plant that was farmed should not be available after a game restart
So the player can change the world by himself but what is a good and simple way to safe all the datas and new position without making a playerpref with a transform.position safe for each crafted object?
Other games like minecraft, ark, rust, skyrim and many many more are doing it too.
Search for terms like “serialization” and “deserialization” and “JSON.” Yes, many games do it and there are tons of really awesome tutorials immediately available on Google and Youtube.
Hello Kurt, thank you. I just watched a whole live training about serialization and deserialization. It’s now clear for me how to save single floats/int.
Now i need to think about how to save all the objects that got crafted and their position.
So should i make a list and do a for each?
Didn’t found a reference on how to save created items but i’ll keep on searching
Well step one is too use a generic saver/loader so that you can save multiple types with the same piece of code (check the one in my signature, it uses generics.
Next to that you can make data structs (or classes) in which you store the data that describes the object, and serialize those objects. For example. for a game I made some time ago, I saved objects that represent the levels in the game, together with whether the player finished the level before, what his best time was, etc.
using System;
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// The level model component class.
/// </summary>
[Serializable]
public class Level : ILevelInfo
{
[SerializeField]
private string _sceneName;
[SerializeField]
private bool _finishedLevel;
[SerializeField]
private int _bestTime;
[SerializeField]
private int _targetTime;
[SerializeField]
private int _lives = 3;
[SerializeField]
private bool _enabled = false;
public string SceneName
{
get { return _sceneName; }
}
public bool FinishedLevel
{
get { return _finishedLevel; }
}
public int BestTime
{
get { return _bestTime; }
}
public int TargetTime
{
get { return _targetTime; }
}
public int Lives
{
get { return _lives; }
}
public bool HasTimeAchievement
{
get { return _bestTime <= _targetTime; }
}
public bool Enabled { get { return _enabled; } }
/// <summary>
/// Sets the score.
/// </summary>
/// <param name="finished">if set to <c>true</c> [finished].</param>
/// <param name="jewels">The jewels.</param>
/// <param name="time">The time.</param>
public void SetScore(bool finished, int time)
{
_finishedLevel = finished;
_bestTime = time <= _bestTime || _bestTime == 0 ? time : _bestTime;
}
public void SetEnabled(bool value)
{
_enabled = value;
}
}
And the level manager where levels are saved and loaded.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>
/// The level manager component class
/// </summary>
public class LevelManager : MonoBehaviour
{
#region Inspector fields
[SerializeField]
private List<Level> _levels;
[SerializeField]
private BaseMenu _pauseMenu;
[SerializeField]
private BaseMenu _gameOverMenu;
[SerializeField]
private BaseMenu _levelFinishedMenu;
[SerializeField]
private BaseMenu _userInterface;
#endregion
#region Private fields
private bool _paused = false;
private Level _currentLevel;
private int _lives = 0;
private string _nextLevel;
private float _time;
private List<LightPointCollectible> _points;
private Finish _finish;
private MenuManager _mm;
#endregion
#region Static fields
private static LevelManager _levelManager;
#endregion
#region Properties
public int Lives
{
get { return _lives; }
set { _lives = value; }
}
public int LevelTime
{
get { return (int)_time; }
}
public string CurrentLevel
{
get { return _currentLevel.SceneName; }
}
public string NextLevel
{
get { return _nextLevel; }
}
public int LevelTargetTime
{
get { return _currentLevel.TargetTime; }
}
public bool TimeAchievment
{
get { return _currentLevel.HasTimeAchievement; }
}
public static LevelManager Instance
{
get { return _levelManager; }
}
public List<Level> Levels
{
get { return _levels; }
}
#endregion
#region Unity Methods
/// <summary>
/// Awakes this instance.
/// </summary>
private void Awake()
{
if (_levelManager == null)
{
DontDestroyOnLoad(gameObject);
_levelManager = this;
}
else if (_levelManager != this)
{
Destroy(gameObject);
}
// Mutes the audio when running in the Unity editor
#if UNITY_EDITOR
var audio = GetComponent<AudioSource>();
audio.mute = true;
#endif
var data = DataManager.LoadData<List<Level>>("levels");
if (data != null)
{
_levels = data;
}
}
/// <summary>
/// Starts this instance.
/// </summary>
private void Start()
{
_points = FindObjectsOfType<LightPointCollectible>().ToList();
_finish = FindObjectOfType<Finish>();
_mm = MenuManager.Instance;
}
/// <summary>
/// Updates this instance.
/// </summary>
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape) && !_paused)
{
PauseGame(true);
}
else if (Input.GetKeyDown(KeyCode.Escape) && _paused)
{
PauseGame(false);
}
_time += Time.deltaTime;
}
/// <summary>
/// Updates this instance at the end of the update stack.
/// </summary>
private void LateUpdate()
{
if (_finish)
_finish.ActivateLights(_points.Count(p => p.Active));
else
_finish = FindObjectOfType<Finish>();
}
/// <summary>
/// Called when [level was loaded].
/// </summary>
/// <param name="level">The level.</param>
private void OnLevelWasLoaded(int level)
{
_points = FindObjectsOfType<LightPointCollectible>().ToList();
_finish = FindObjectOfType<Finish>();
}
#endregion
#region Public Methods
/// <summary>
/// Loads the level.
/// </summary>
/// <param name="name">The name.</param>
public void LoadLevel(string name)
{
_currentLevel = _levels.FirstOrDefault(l => l.SceneName.Equals(name));
_lives = _currentLevel.Lives;
_nextLevel = Levels.NextLevel(_currentLevel.SceneName);
PauseGame(false);
_mm.SetMenu(_userInterface);
_time = 0;
Application.LoadLevel(_currentLevel.SceneName);
}
/// <summary>
/// Kills the player.
/// </summary>
/// <param name="player">The player.</param>
public void KillPlayer(Player player)
{
StartCoroutine(KillPlayerCo(player));
}
/// <summary>
/// Finishes the level.
/// </summary>
public void FinishLevel()
{
Debug.Log(_nextLevel);
_currentLevel.SetScore(true, (int)_time);
var nextLevel = _levels.FirstOrDefault(l => l.SceneName.Equals(_nextLevel));
nextLevel.SetEnabled(true);
_levels.Update(new[] { _currentLevel, nextLevel });
DataManager.SaveData<List<Level>>(_levels, "levels");
_mm.SetMenu(_levelFinishedMenu);
}
/// <summary>
/// Pauses the game.
/// </summary>
/// <param name="value">if set to <c>true</c> [value].</param>
public void PauseGame(bool value)
{
if(value)
{
Time.timeScale = 0;
_mm.SetMenu(_pauseMenu);
_paused = true;
}
else
{
Time.timeScale = 1;
_mm.SetMenu(_userInterface);
_paused = false;
}
}
#endregion
#region CoRoutines
/// <summary>
/// Kills the player CoRoutine.
/// </summary>
/// <param name="player">The player.</param>
/// <returns></returns>
private IEnumerator KillPlayerCo(Player player)
{
if (_lives > 0)
{
_lives--;
player.Kill();
yield return new WaitForSeconds(2);
if (_lives >= 0)
player.Respawn();
else
{
_mm.SetMenu(_gameOverMenu);
}
}
else
{
player.Kill();
_mm.SetMenu(_gameOverMenu);
}
}
#endregion
}