-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,3 +28,4 @@ sysinfo.txt | |
# Builds | ||
*.apk | ||
*.unitypackage | ||
Assets/saves/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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.
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); | ||
} | ||
} |
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); | ||
}); | ||
} | ||
} |
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); | ||
} | ||
|
||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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); | ||
} | ||
} | ||
|
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); | ||
}); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.