Skip to content

Commit

Permalink
final work for article
Browse files Browse the repository at this point in the history
  • Loading branch information
Naphier committed Nov 24, 2016
1 parent 115498d commit 5df4ca8
Show file tree
Hide file tree
Showing 46 changed files with 3,548 additions and 805 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ sysinfo.txt
# Builds
*.apk
*.unitypackage
Assets/saves/
4 changes: 2 additions & 2 deletions Assets/_Scenes.meta → Assets/1_PlayerPrefs_Example.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Assets/saves.meta → Assets/1_PlayerPrefs_Example/Audio.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
22 changes: 22 additions & 0 deletions Assets/1_PlayerPrefs_Example/Audio/Space Cadet.ogg.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions Assets/1_PlayerPrefs_Example/DataService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using UnityEngine;

namespace EX1
{
/// <summary>
/// Singleton - There should only ever be one DataService and it should persist
/// between scene loads.
/// This class is responsible for loading/saving data.
/// </summary>
public class DataService : MonoBehaviour
{
private static DataService _instance = null;
public static DataService Instance
{
get
{
// If the instance of this class doesn't exist
if (_instance == null)
{
// Check the scene for a Game Object with this class
_instance = FindObjectOfType<DataService>();

// If none is found in the scene then create a new Game Object
// and add this class to it.
if (_instance == null)
{
GameObject go = new GameObject(typeof(DataService).ToString());
_instance = go.AddComponent<DataService>();
}
}

return _instance;
}
}

public PlayerPrefsHandler prefs { get; private set; }

// When the scene first runs ensure that there is only one
// instance of this class. This allows us to add it to any scene and
// not conflict with any pre-existing instance from a previous scene.
private void Awake()
{
if (Instance != this)
{
Destroy(this);
}
else
{
DontDestroyOnLoad(gameObject);

prefs = new PlayerPrefsHandler();
prefs.RestorePreferences();
// In Unity 5.4 OnLevelWasLoaded has been deprecated and the action
// now occurs through this callback.
#if UNITY_5_4_OR_NEWER
SceneManager.sceneLoaded += OnLevelWasLoaded;
#endif
}
}

/// <summary>
/// Ensure that the player preferences are applied to the new scene.
/// </summary>
// In Unity 5.4 OnLevelWasLoaded has been deprecated and the action
// now occurs through 'SceneManager.sceneLoaded' callback.
void OnLevelWasLoaded()
{
prefs.RestorePreferences();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
84 changes: 84 additions & 0 deletions Assets/1_PlayerPrefs_Example/PlayerPrefsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using UnityEngine;

/// <summary>
/// Handles the saving, recalling, and applying of all PlayerPrefs for the application.
/// </summary>
public class PlayerPrefsHandler
{
/// <summary>
/// Storing the PlayerPrefs keys in constants is a good practice!
/// This saves you from having to make multiple changes in your code should you change the key value,
/// allows you to make use of Intellisense for typing out the key (intead of mistyping the actual string),
/// and since it is public and const you can access it anywhere without needing an instance of this class
/// (i.e. by typing PlayerPrefsHandler.MUTE_INT).
/// I like to append my PlayerPrefs keys with the type of the pref (i.e. _INT, _STR, _F)
/// </summary>
#region PlayerPrefs keys
public const string MUTE_INT = "mute";
public const string VOLUME_F = "volume";
#endregion

private const bool DEBUG_ON = true;

/// <summary>
/// This method should call all other methods that will apply saved or default preferences.
/// We should call this as soon as possible when loading our application.
/// </summary>
public void RestorePreferences()
{
SetMuted(GetIsMuted());
SetVolume(GetVolume());
}

/// <summary>
/// Sets the AudioListener to be (un)muted and saves the value to player prefs.
/// </summary>
/// <param name="muted">Whether we should mute or not.</param>
public void SetMuted(bool muted)
{
// Set the MUTE_INT key to 1 if muted, 0 if not muted
PlayerPrefs.SetInt(MUTE_INT, muted ? 1 : 0);

// Pausing the AudioListener will disable all sounds.
AudioListener.pause = muted;

if (DEBUG_ON)
Debug.LogFormat("SetMuted({0})", muted);
}

/// <summary>
/// Reads from PlayerPrefs to tell us if we should mute or not.
/// </summary>
/// <returns>Whether the MUTE_INT pref has been set to 1 or not.</returns>
public bool GetIsMuted()
{
// If the value of the MUTE_INT key is 1 then sound is muted, otherwise it is not muted.
// The default value of the MUTE_INT key is 0 (i.e. not muted).
return PlayerPrefs.GetInt(MUTE_INT, 0) == 1;
}

/// <summary>
/// Sets the volume on the AudioListener and saves the value to PlayerPrefs.
/// </summary>
/// <param name="volume">A value between 0 and 1</param>
public void SetVolume(float volume)
{
// Prevent values less than 0 and greater than 1 from
// being stored in the PlayerPrefs (AudioListener.volume expects a value between 0 and 1).
volume = Mathf.Clamp(volume, 0, 1);

PlayerPrefs.SetFloat(VOLUME_F, volume);
AudioListener.volume = volume;
}


/// <summary>
/// Retrieves the stored or default (1) volume from PlayerPrefs
/// and ensures it is no less than 0 and no greater than 1
/// </summary>
/// <returns>The volume setting between 0 and 1</returns>
public float GetVolume()
{
return Mathf.Clamp(PlayerPrefs.GetFloat(VOLUME_F, 1), 0, 1);
}
}
File renamed without changes.
File renamed without changes.
38 changes: 38 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/MuteToggleHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using UnityEngine;
using UnityEngine.UI;
using DataService = EX1.DataService;
// We'll switch to this in part 2
//using DataService = EX2.DataService;

/// <summary>
/// Handles the initial setting of the UI Toggle value
/// and assigns the onValueChanged listener to the UI Toggle component.
/// </summary>
// RequireComponent ensures that when we can only add this component to a UI Toggle
// It also ensures that when we attempt to GetComponent<Toggle> that it exists.
[RequireComponent(typeof(Toggle))]
public class MuteToggleHandler : MonoBehaviour
{
void Start()
{
// Get the reference to the attached toggle component.
Toggle toggle = GetComponent<Toggle>();

// Set the initial value that was stored in player prefs.
toggle.isOn = DataService.Instance.prefs.GetIsMuted();

// Set up the onValueChanged listener
// This is done here instead of in the inspector for a few reasons:
// - DataService contains the PlayerPrefsHandler reference and Unity won't let us
// access that through the inspector.
// - DataService is a singleton and may or may not be in the scene so we can't always
// assign it to via the inspector.
// - This makes the script completely self-contained. No other script needs access to this script.
// The only fallback is that this class is not extensible, but it really doesn't need to be.
toggle.onValueChanged.AddListener(
(bool value) =>
{
DataService.Instance.prefs.SetMuted(value);
});
}
}
11 changes: 11 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/ReloadSceneButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using UnityEngine;
using UnityEngine.SceneManagement;

public class ReloadSceneButton : MonoBehaviour
{
public void ReloadScene()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}

}
12 changes: 12 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/ReloadSceneButton.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/SaveSlotButtonHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using UnityEngine.SceneManagement;
using EX2;

/// <summary>
/// Attach this to any game object. I like to attach it to the canvas containing the buttons that will
/// load profiles. That way it's in a 'logical' place and easy to find.
/// </summary>
public class SaveSlotButtonHandler : MonoBehaviour
{
/// <summary>
/// Assign each of the button labels here. They should be in order of their appearance (top to bottom).
/// </summary>
public Text[] buttonLabels;

/// <summary>
/// This is the text that will display when
/// </summary>
private const string EMPTY_SLOT = "New Game";
private const string USED_SLOT = "Load Save ";

void Start()
{
SetButtonLabels();
}

/// <summary>
/// Sets the label on each button to indicate whether we're loading an empty slot or
/// loading an actual profile.
/// </summary>
void SetButtonLabels()
{
if (buttonLabels.Length != DataService.MAX_NUMBER_OF_PROFILES)
{
Debug.LogError(
"Incorrect number of button labels. Must be exactly " +
DataService.MAX_NUMBER_OF_PROFILES);
}
else
{
// For every possible profile number.
for (int i = 0; i < DataService.MAX_NUMBER_OF_PROFILES; i++)
{
// If the profile file exists,
// Then set the label to say the profile exists (i.e. 'Load Save 1')
if (File.Exists(DataService.Instance.GetSaveDataFilePath(i + 1)))
{
buttonLabels[i].text = USED_SLOT + (i + 1).ToString();
}
else
{
// Otherwise set the label to just say 'New Game" indicating it is an empty slot.
buttonLabels[i].text = EMPTY_SLOT;
}
}
}
}

// This should be assigned to each button via the inspector.
// The parameter in the inspector's on click event will be 1,2, or 3
/// <summary>
/// Called from the OnClick methods for buttons.
/// </summary>
/// <param name="profileNumber"></param>
public void LoadGame(int profileNumber)
{
// Load the save data file
DataService.Instance.LoadSaveData(profileNumber);
// Load the last level the player was in
SceneManager.LoadScene(DataService.Instance.SaveData.lastLevel);
}
}

20 changes: 20 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/VolumeSliderHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using UnityEngine;
using UnityEngine.UI;
using DataService = EX1.DataService;
// We'll switch to this in part 2
//using DataService = EX2.DataService;

[RequireComponent(typeof(Slider))]
public class VolumeSliderHandler : MonoBehaviour
{
void Start()
{
Slider slider = GetComponent<Slider>();
slider.value = DataService.Instance.prefs.GetVolume();
slider.onValueChanged.AddListener(
(float value) =>
{
DataService.Instance.prefs.SetVolume(value);
});
}
}
12 changes: 12 additions & 0 deletions Assets/1_PlayerPrefs_Example/UI/VolumeSliderHandler.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5df4ca8

Please sign in to comment.