

- Lanthera -
Project Details
Project Length: 4 Weeks
Team Size: 13 people
Engine: Unity
Planning: Miro, Jira & Discord
Project Length: 4 Weeks
Team Size: 13 people
Engine: Unity
Planning: Miro, Jira & Discord
Project Length: 4 Weeks
Team Size: 13 people
Engine: Unity
Planning: Miro, Jira & Discord
My Roles: PO & Programmer
Save System
Checkpoint System
Lantern Interactions
My Roles: PO & Programmer
Save System
Checkpoint System
Lantern Interactions
My Roles: PO & Programmer
Save System
Checkpoint System
Lantern Interactions
Save System
An extra challenge during this project was to make a save system for our game.
After looking at a few tutorials online for how to setup the system and save data to a file I created a save manager and an interface for save-ables that the prefabs that we wanted to save data from would be able to partake of to facilitate the saving process.
For saving and loading to a file I have a file handler that takes care of reading and writing to the save file.


Save Manager Code
using System.Collections; using UnityEngine; public class CS_SaveManager : MonoBehaviour { #region Singleton public static CS_SaveManager instance; private void Awake() { if (instance != null) { Debug.LogError("[SaveManager] There are multiple Singletons!"); } instance = this; } #endregion [Header("File Storage Config")] private string fileName = "Save"; public CS_GameData gameData; public delegate void SaveDelegate(); public static event SaveDelegate SaveEvent; public static event SaveDelegate LoadEvent; private CS_FileHandler fileHandler; void Start() { fileHandler = new CS_FileHandler(Application.persistentDataPath, fileName); LoadGame(); } public void SaveGame() { if (SaveEvent != null) { SaveEvent(); //Update Files with Filehandler (Update GameData to File) fileHandler.Save(gameData); } } public void LoadGame() { //Update Files with Filehandler (File to GameData) if (LoadEvent != null) { gameData = fileHandler.Load(); if (gameData == null) { NewGame(); } StartCoroutine(nameof(Loadwait)); } } private IEnumerator Loadwait() { yield return new WaitForSeconds(.001f); LoadEvent(); } public void NewGame() { gameData = new CS_GameData(); SaveGame(); } private void OnApplicationQuit() { SaveGame
Save Manager Code
using System.Collections; using UnityEngine; public class CS_SaveManager : MonoBehaviour { #region Singleton public static CS_SaveManager instance; private void Awake() { if (instance != null) { Debug.LogError("[SaveManager] There are multiple Singletons!"); } instance = this; } #endregion [Header("File Storage Config")] private string fileName = "Save"; public CS_GameData gameData; public delegate void SaveDelegate(); public static event SaveDelegate SaveEvent; public static event SaveDelegate LoadEvent; private CS_FileHandler fileHandler; void Start() { fileHandler = new CS_FileHandler(Application.persistentDataPath, fileName); LoadGame(); } public void SaveGame() { if (SaveEvent != null) { SaveEvent(); //Update Files with Filehandler (Update GameData to File) fileHandler.Save(gameData); } } public void LoadGame() { //Update Files with Filehandler (File to GameData) if (LoadEvent != null) { gameData = fileHandler.Load(); if (gameData == null) { NewGame(); } StartCoroutine(nameof(Loadwait)); } } private IEnumerator Loadwait() { yield return new WaitForSeconds(.001f); LoadEvent(); } public void NewGame() { gameData = new CS_GameData(); SaveGame(); } private void OnApplicationQuit() { SaveGame
Save Manager Code
using System.Collections; using UnityEngine; public class CS_SaveManager : MonoBehaviour { #region Singleton public static CS_SaveManager instance; private void Awake() { if (instance != null) { Debug.LogError("[SaveManager] There are multiple Singletons!"); } instance = this; } #endregion [Header("File Storage Config")] private string fileName = "Save"; public CS_GameData gameData; public delegate void SaveDelegate(); public static event SaveDelegate SaveEvent; public static event SaveDelegate LoadEvent; private CS_FileHandler fileHandler; void Start() { fileHandler = new CS_FileHandler(Application.persistentDataPath, fileName); LoadGame(); } public void SaveGame() { if (SaveEvent != null) { SaveEvent(); //Update Files with Filehandler (Update GameData to File) fileHandler.Save(gameData); } } public void LoadGame() { //Update Files with Filehandler (File to GameData) if (LoadEvent != null) { gameData = fileHandler.Load(); if (gameData == null) { NewGame(); } StartCoroutine(nameof(Loadwait)); } } private IEnumerator Loadwait() { yield return new WaitForSeconds(.001f); LoadEvent(); } public void NewGame() { gameData = new CS_GameData(); SaveGame(); } private void OnApplicationQuit() { SaveGame
File Handler Code
using UnityEngine; using System; using System.IO; public class CS_FileHandler { private string dataDirPath = ""; private string dataFileName = ""; public CS_FileHandler(string dataDirPath, string dataFileName) { this.dataDirPath = dataDirPath; this.dataFileName = dataFileName; } public CS_GameData Load() { string fullPath = Path.Combine(dataDirPath, dataFileName); CS_GameData loadedData = null; if (File.Exists(fullPath)) { try { string dataToLoad = ""; using (FileStream stream = new FileStream(fullPath, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { dataToLoad = reader.ReadToEnd(); } } loadedData = JsonUtility.FromJson<CS_GameData>(dataToLoad); } catch (Exception e) { Debug.LogError("[FileHandler] Error occurred when trying to load data from file: " + fullPath + "\n" + e); } } return loadedData; } public void Save(CS_GameData data) { string fullPath = Path.Combine(dataDirPath, dataFileName); Debug.Log("[CS_FileHandler]: " + fullPath); try { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); string dataToStore = JsonUtility.ToJson(data, true); using (FileStream stream = new FileStream(fullPath, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(dataToStore); } } } catch (Exception e) { Debug.LogError("[FileHandler] Error occured when trying to save data to file: " + fullPath + "\n" + e
File Handler Code
using UnityEngine; using System; using System.IO; public class CS_FileHandler { private string dataDirPath = ""; private string dataFileName = ""; public CS_FileHandler(string dataDirPath, string dataFileName) { this.dataDirPath = dataDirPath; this.dataFileName = dataFileName; } public CS_GameData Load() { string fullPath = Path.Combine(dataDirPath, dataFileName); CS_GameData loadedData = null; if (File.Exists(fullPath)) { try { string dataToLoad = ""; using (FileStream stream = new FileStream(fullPath, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { dataToLoad = reader.ReadToEnd(); } } loadedData = JsonUtility.FromJson<CS_GameData>(dataToLoad); } catch (Exception e) { Debug.LogError("[FileHandler] Error occurred when trying to load data from file: " + fullPath + "\n" + e); } } return loadedData; } public void Save(CS_GameData data) { string fullPath = Path.Combine(dataDirPath, dataFileName); Debug.Log("[CS_FileHandler]: " + fullPath); try { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); string dataToStore = JsonUtility.ToJson(data, true); using (FileStream stream = new FileStream(fullPath, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(dataToStore); } } } catch (Exception e) { Debug.LogError("[FileHandler] Error occured when trying to save data to file: " + fullPath + "\n" + e
File Handler Code
using UnityEngine; using System; using System.IO; public class CS_FileHandler { private string dataDirPath = ""; private string dataFileName = ""; public CS_FileHandler(string dataDirPath, string dataFileName) { this.dataDirPath = dataDirPath; this.dataFileName = dataFileName; } public CS_GameData Load() { string fullPath = Path.Combine(dataDirPath, dataFileName); CS_GameData loadedData = null; if (File.Exists(fullPath)) { try { string dataToLoad = ""; using (FileStream stream = new FileStream(fullPath, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { dataToLoad = reader.ReadToEnd(); } } loadedData = JsonUtility.FromJson<CS_GameData>(dataToLoad); } catch (Exception e) { Debug.LogError("[FileHandler] Error occurred when trying to load data from file: " + fullPath + "\n" + e); } } return loadedData; } public void Save(CS_GameData data) { string fullPath = Path.Combine(dataDirPath, dataFileName); Debug.Log("[CS_FileHandler]: " + fullPath); try { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); string dataToStore = JsonUtility.ToJson(data, true); using (FileStream stream = new FileStream(fullPath, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(dataToStore); } } } catch (Exception e) { Debug.LogError("[FileHandler] Error occured when trying to save data to file: " + fullPath + "\n" + e
Saving Code Example
using System.Collections.Generic; using System.Linq; using UnityEngine; public class CS_DoorState : MonoBehaviour, CS_ISaveable { [SerializeField] public List<CS_SimpleDoor> doors = new List<CS_SimpleDoor>(); public static CS_DoorState Instance { get; private set; } private void Awake() { #region Singleton if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; #endregion } private void OnEnable() { Subscribe(true); } private void OnDisable() { Subscribe(false); } public void LoadGame() { for (int i = 0; i < doors.Count; i++) { doors[i].IsSolved = CS_SaveManager.instance.gameData.doorStates[i]; if (doors[i].IsSolved) { doors[i].OpenDoor(); } } } public void SaveGame() { for (int i = 0; i < doors.Count; i++) { while (doors.Count > CS_SaveManager.instance.gameData.doorStates.Count) { CS_SaveManager.instance.gameData.doorStates.Add(false); } CS_SaveManager.instance.gameData.doorStates[i] = doors[i].IsSolved; } } public void Subscribe(bool add) { if (add) { CS_SaveManager.SaveEvent += SaveGame; CS_SaveManager.LoadEvent += LoadGame; } else { CS_SaveManager.SaveEvent -= SaveGame; CS_SaveManager.LoadEvent -= LoadGame
Saving Code Example
using System.Collections.Generic; using System.Linq; using UnityEngine; public class CS_DoorState : MonoBehaviour, CS_ISaveable { [SerializeField] public List<CS_SimpleDoor> doors = new List<CS_SimpleDoor>(); public static CS_DoorState Instance { get; private set; } private void Awake() { #region Singleton if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; #endregion } private void OnEnable() { Subscribe(true); } private void OnDisable() { Subscribe(false); } public void LoadGame() { for (int i = 0; i < doors.Count; i++) { doors[i].IsSolved = CS_SaveManager.instance.gameData.doorStates[i]; if (doors[i].IsSolved) { doors[i].OpenDoor(); } } } public void SaveGame() { for (int i = 0; i < doors.Count; i++) { while (doors.Count > CS_SaveManager.instance.gameData.doorStates.Count) { CS_SaveManager.instance.gameData.doorStates.Add(false); } CS_SaveManager.instance.gameData.doorStates[i] = doors[i].IsSolved; } } public void Subscribe(bool add) { if (add) { CS_SaveManager.SaveEvent += SaveGame; CS_SaveManager.LoadEvent += LoadGame; } else { CS_SaveManager.SaveEvent -= SaveGame; CS_SaveManager.LoadEvent -= LoadGame
Saving Code Example
using System.Collections.Generic; using System.Linq; using UnityEngine; public class CS_DoorState : MonoBehaviour, CS_ISaveable { [SerializeField] public List<CS_SimpleDoor> doors = new List<CS_SimpleDoor>(); public static CS_DoorState Instance { get; private set; } private void Awake() { #region Singleton if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; #endregion } private void OnEnable() { Subscribe(true); } private void OnDisable() { Subscribe(false); } public void LoadGame() { for (int i = 0; i < doors.Count; i++) { doors[i].IsSolved = CS_SaveManager.instance.gameData.doorStates[i]; if (doors[i].IsSolved) { doors[i].OpenDoor(); } } } public void SaveGame() { for (int i = 0; i < doors.Count; i++) { while (doors.Count > CS_SaveManager.instance.gameData.doorStates.Count) { CS_SaveManager.instance.gameData.doorStates.Add(false); } CS_SaveManager.instance.gameData.doorStates[i] = doors[i].IsSolved; } } public void Subscribe(bool add) { if (add) { CS_SaveManager.SaveEvent += SaveGame; CS_SaveManager.LoadEvent += LoadGame; } else { CS_SaveManager.SaveEvent -= SaveGame; CS_SaveManager.LoadEvent -= LoadGame
Lantern Interactions
Other than the save system I worked on making the interactions the player can have with the lantern and lantern related objects.
Those objects were: The lantern, Lantern Pedestal & its switch variant and the Lantern Retriever.
They way the system is set up, when the player interacts with one of these objects the objects then communicate with the lantern and then the lantern performs it associated action based on the current conditions.
The Lantern retriever also acts as a pseudo checkpoint and saves your game on top of retrieving the players lantern.

Lantern Code
using System.Collections; using UnityEngine; //The Lantern has energy and a function for refilling it called "ChargeLantern" //as well as a function for changing its parent and moving it self to its new parents location //The function for this is named "ChangeOwner". public class CS_Lantern : MonoBehaviour { private bool atPlayer; private bool allowPositionStay; private Transform lanternSpot; public void Awake() { } public void Update() { if (atPlayer && allowPositionStay) { transform.position = lanternSpot.position; } } public void ChargeLantern() { } public void ChangeOwner(CS_PlayerInteraction playerRef, bool invState, Transform newParent, Vector3 offset) { if (lanternSpot == null) lanternSpot = playerRef.GetComponent<CS_AnimationPlayerAlt>().lanternSpot; this.transform.SetParent(newParent, true); if(invState) StartCoroutine(MoveFromAToB(this.transform, lanternSpot, Vector3.zero)); else StartCoroutine(MoveFromAToB(this.transform, newParent, offset)); atPlayer = invState; playerRef.gameObject.GetComponent<CS_Inventory>().hasLamp = invState; } IEnumerator MoveFromAToB(Transform lanternOBJ, Transform breakPoint, Vector3 offset) { float timeSinceStarted = 0f; while (true) { allowPositionStay = false; timeSinceStarted += (2 * Time.deltaTime); lanternOBJ.position = Vector3.Lerp(lanternOBJ.position, breakPoint.position + offset, timeSinceStarted); if (lanternOBJ.position == breakPoint.position + offset) { allowPositionStay = true; yield break; } yield return null
Lantern Code
using System.Collections; using UnityEngine; //The Lantern has energy and a function for refilling it called "ChargeLantern" //as well as a function for changing its parent and moving it self to its new parents location //The function for this is named "ChangeOwner". public class CS_Lantern : MonoBehaviour { private bool atPlayer; private bool allowPositionStay; private Transform lanternSpot; public void Awake() { } public void Update() { if (atPlayer && allowPositionStay) { transform.position = lanternSpot.position; } } public void ChargeLantern() { } public void ChangeOwner(CS_PlayerInteraction playerRef, bool invState, Transform newParent, Vector3 offset) { if (lanternSpot == null) lanternSpot = playerRef.GetComponent<CS_AnimationPlayerAlt>().lanternSpot; this.transform.SetParent(newParent, true); if(invState) StartCoroutine(MoveFromAToB(this.transform, lanternSpot, Vector3.zero)); else StartCoroutine(MoveFromAToB(this.transform, newParent, offset)); atPlayer = invState; playerRef.gameObject.GetComponent<CS_Inventory>().hasLamp = invState; } IEnumerator MoveFromAToB(Transform lanternOBJ, Transform breakPoint, Vector3 offset) { float timeSinceStarted = 0f; while (true) { allowPositionStay = false; timeSinceStarted += (2 * Time.deltaTime); lanternOBJ.position = Vector3.Lerp(lanternOBJ.position, breakPoint.position + offset, timeSinceStarted); if (lanternOBJ.position == breakPoint.position + offset) { allowPositionStay = true; yield break; } yield return null
Lantern Code
using System.Collections; using UnityEngine; //The Lantern has energy and a function for refilling it called "ChargeLantern" //as well as a function for changing its parent and moving it self to its new parents location //The function for this is named "ChangeOwner". public class CS_Lantern : MonoBehaviour { private bool atPlayer; private bool allowPositionStay; private Transform lanternSpot; public void Awake() { } public void Update() { if (atPlayer && allowPositionStay) { transform.position = lanternSpot.position; } } public void ChargeLantern() { } public void ChangeOwner(CS_PlayerInteraction playerRef, bool invState, Transform newParent, Vector3 offset) { if (lanternSpot == null) lanternSpot = playerRef.GetComponent<CS_AnimationPlayerAlt>().lanternSpot; this.transform.SetParent(newParent, true); if(invState) StartCoroutine(MoveFromAToB(this.transform, lanternSpot, Vector3.zero)); else StartCoroutine(MoveFromAToB(this.transform, newParent, offset)); atPlayer = invState; playerRef.gameObject.GetComponent<CS_Inventory>().hasLamp = invState; } IEnumerator MoveFromAToB(Transform lanternOBJ, Transform breakPoint, Vector3 offset) { float timeSinceStarted = 0f; while (true) { allowPositionStay = false; timeSinceStarted += (2 * Time.deltaTime); lanternOBJ.position = Vector3.Lerp(lanternOBJ.position, breakPoint.position + offset, timeSinceStarted); if (lanternOBJ.position == breakPoint.position + offset) { allowPositionStay = true; yield break; } yield return null
Lantern Pedestal Switch Code
using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; //The LanternPedastal on interact checks if it has a lantern already and then checks the player for a lantern on them //if any of them have the lantern object childed it will run the "ChangeOwner" script on the lantern swapping the parents //and set the "hasLantern" bool appropriately. public class CS_LanternPedestalSwitch : MonoBehaviour, CS_IInteractable { public UnityEvent onLeaveTorch; public UnityEvent onPickUpTorch; [SerializeField] AK.Wwise.Event pickupLanternSound; [SerializeField] AK.Wwise.Event leaveLanternSound; [SerializeField] public bool hasLantern = false; [SerializeField] private Vector3 lanternPosition = new Vector3(0,3,0); public void Interact(CS_PlayerInteraction interactionSource) { if (hasLantern) { foreach (CS_Lantern c_lantern in transform.GetComponentsInChildren<CS_Lantern>()) { c_lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); hasLantern = false; DeactivateSomething(); pickupLanternSound.Post(gameObject); } } else { foreach (CS_Lantern c_lantern in interactionSource.transform.GetComponentsInChildren<CS_Lantern>()) { if (!hasLantern) { c_lantern.ChangeOwner(interactionSource, false, this.transform, lanternPosition); hasLantern = true; ActivateSomething(); leaveLanternSound.Post(gameObject); } } } } public void lanternRetrieverUpdate() { DeactivateSomething(); } public string InteractLabel() { return "Lantern Pedestal Switch"; } public void ActivateSomething() { onLeaveTorch?.Invoke(); } public void DeactivateSomething() { onPickUpTorch?.Invoke
Lantern Pedestal Switch Code
using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; //The LanternPedastal on interact checks if it has a lantern already and then checks the player for a lantern on them //if any of them have the lantern object childed it will run the "ChangeOwner" script on the lantern swapping the parents //and set the "hasLantern" bool appropriately. public class CS_LanternPedestalSwitch : MonoBehaviour, CS_IInteractable { public UnityEvent onLeaveTorch; public UnityEvent onPickUpTorch; [SerializeField] AK.Wwise.Event pickupLanternSound; [SerializeField] AK.Wwise.Event leaveLanternSound; [SerializeField] public bool hasLantern = false; [SerializeField] private Vector3 lanternPosition = new Vector3(0,3,0); public void Interact(CS_PlayerInteraction interactionSource) { if (hasLantern) { foreach (CS_Lantern c_lantern in transform.GetComponentsInChildren<CS_Lantern>()) { c_lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); hasLantern = false; DeactivateSomething(); pickupLanternSound.Post(gameObject); } } else { foreach (CS_Lantern c_lantern in interactionSource.transform.GetComponentsInChildren<CS_Lantern>()) { if (!hasLantern) { c_lantern.ChangeOwner(interactionSource, false, this.transform, lanternPosition); hasLantern = true; ActivateSomething(); leaveLanternSound.Post(gameObject); } } } } public void lanternRetrieverUpdate() { DeactivateSomething(); } public string InteractLabel() { return "Lantern Pedestal Switch"; } public void ActivateSomething() { onLeaveTorch?.Invoke(); } public void DeactivateSomething() { onPickUpTorch?.Invoke
Lantern Pedestal Switch Code
using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; //The LanternPedastal on interact checks if it has a lantern already and then checks the player for a lantern on them //if any of them have the lantern object childed it will run the "ChangeOwner" script on the lantern swapping the parents //and set the "hasLantern" bool appropriately. public class CS_LanternPedestalSwitch : MonoBehaviour, CS_IInteractable { public UnityEvent onLeaveTorch; public UnityEvent onPickUpTorch; [SerializeField] AK.Wwise.Event pickupLanternSound; [SerializeField] AK.Wwise.Event leaveLanternSound; [SerializeField] public bool hasLantern = false; [SerializeField] private Vector3 lanternPosition = new Vector3(0,3,0); public void Interact(CS_PlayerInteraction interactionSource) { if (hasLantern) { foreach (CS_Lantern c_lantern in transform.GetComponentsInChildren<CS_Lantern>()) { c_lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); hasLantern = false; DeactivateSomething(); pickupLanternSound.Post(gameObject); } } else { foreach (CS_Lantern c_lantern in interactionSource.transform.GetComponentsInChildren<CS_Lantern>()) { if (!hasLantern) { c_lantern.ChangeOwner(interactionSource, false, this.transform, lanternPosition); hasLantern = true; ActivateSomething(); leaveLanternSound.Post(gameObject); } } } } public void lanternRetrieverUpdate() { DeactivateSomething(); } public string InteractLabel() { return "Lantern Pedestal Switch"; } public void ActivateSomething() { onLeaveTorch?.Invoke(); } public void DeactivateSomething() { onPickUpTorch?.Invoke
Lantern Retriever Code
using System.Collections; using UnityEngine; //The LanternRetriever on interact finds the lantern in the scene and if its not on the player it will //set the "haslantern" bool on its current parrent and run the "ChangeOwner" script on the lantern //moving and parrenting it to the player. public class CS_LanternRetriever : MonoBehaviour, CS_IInteractable { CS_Lantern lantern; [SerializeField] AK.Wwise.Event pickupLanternSound; public void Interact(CS_PlayerInteraction interactionSource) { CS_PlayerRespawn.Instance.SetRespawn(gameObject); if (lantern == null)lantern = FindAnyObjectByType<CS_Lantern>(); if (lantern.transform.parent != interactionSource.transform) { if (lantern.transform.parent.TryGetComponent<CS_LanternPedestal>(out CS_LanternPedestal lanternPedestal)) { lanternPedestal.hasLantern = false; } else if (lantern.transform.parent.TryGetComponent<CS_LanternPedestalSwitch>(out CS_LanternPedestalSwitch lanternPedestalSwitch)) { lanternPedestalSwitch.hasLantern = false; lanternPedestalSwitch.lanternRetrieverUpdate(); } lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); pickupLanternSound.Post(gameObject); } } public string InteractLabel() { return "Lantern Retriever"
Lantern Retriever Code
using System.Collections; using UnityEngine; //The LanternRetriever on interact finds the lantern in the scene and if its not on the player it will //set the "haslantern" bool on its current parrent and run the "ChangeOwner" script on the lantern //moving and parrenting it to the player. public class CS_LanternRetriever : MonoBehaviour, CS_IInteractable { CS_Lantern lantern; [SerializeField] AK.Wwise.Event pickupLanternSound; public void Interact(CS_PlayerInteraction interactionSource) { CS_PlayerRespawn.Instance.SetRespawn(gameObject); if (lantern == null)lantern = FindAnyObjectByType<CS_Lantern>(); if (lantern.transform.parent != interactionSource.transform) { if (lantern.transform.parent.TryGetComponent<CS_LanternPedestal>(out CS_LanternPedestal lanternPedestal)) { lanternPedestal.hasLantern = false; } else if (lantern.transform.parent.TryGetComponent<CS_LanternPedestalSwitch>(out CS_LanternPedestalSwitch lanternPedestalSwitch)) { lanternPedestalSwitch.hasLantern = false; lanternPedestalSwitch.lanternRetrieverUpdate(); } lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); pickupLanternSound.Post(gameObject); } } public string InteractLabel() { return "Lantern Retriever"
Lantern Retriever Code
using System.Collections; using UnityEngine; //The LanternRetriever on interact finds the lantern in the scene and if its not on the player it will //set the "haslantern" bool on its current parrent and run the "ChangeOwner" script on the lantern //moving and parrenting it to the player. public class CS_LanternRetriever : MonoBehaviour, CS_IInteractable { CS_Lantern lantern; [SerializeField] AK.Wwise.Event pickupLanternSound; public void Interact(CS_PlayerInteraction interactionSource) { CS_PlayerRespawn.Instance.SetRespawn(gameObject); if (lantern == null)lantern = FindAnyObjectByType<CS_Lantern>(); if (lantern.transform.parent != interactionSource.transform) { if (lantern.transform.parent.TryGetComponent<CS_LanternPedestal>(out CS_LanternPedestal lanternPedestal)) { lanternPedestal.hasLantern = false; } else if (lantern.transform.parent.TryGetComponent<CS_LanternPedestalSwitch>(out CS_LanternPedestalSwitch lanternPedestalSwitch)) { lanternPedestalSwitch.hasLantern = false; lanternPedestalSwitch.lanternRetrieverUpdate(); } lantern.ChangeOwner(interactionSource, true, interactionSource.transform, new Vector3(0, 3, 0)); pickupLanternSound.Post(gameObject); } } public string InteractLabel() { return "Lantern Retriever"
Reflections
While the project may have had some issues with its scope I believe that the final product managed to deliver most if not all the things we wanted to highlight, as the product over I assisted in a lot of tother things that were not directly related to programming such as iterating designs with the designers and trying to cut down on the scope when we wanted to add more features.
During one point of development we wanted to have a combat system and have the player be able to die, but quickly decided that that would be a detriment to the overall game experience.
Game Trailer

Smooth Scroll
This will hide itself!
This will hide itself!