I was working on the game in the “Core Combat Creator” course and had finished all the lessons up to “Configurable Cursors”. Now, though, when I click “play” and try to go to the next scene, it kicks me back to the first one and displays the following warning message in the console:
“Failed to create agent because it is not close enough to the NavMesh”.
After that, it kicks the game back to the first scene and sets it back to its original state as if I didn’t move around or anything. That’s when I stop the game to figure out what’s wrong. After that, I try to start it again, but it freezes and I’m forced to end the program with the task manager.
I noticed that when I tried to enter the second scene, it created a save file based in there anyway even though it wouldn’t allow me to go in, and when I try to play it freezes the game when it tries to load the character into that scene. I’ve had a number of issues related to the saving system and the second scene that I’ve tried to squish, but this one is the latest and worst.
In case it might help, below are several files that I’ve had trouble with.
SaveableEntity:
using System;
using System.Collections.Generic;
using RPG.Core;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
namespace RPG.Saving
{
[ExecuteAlways]
public class SaveableEntity : MonoBehaviour
{
[SerializeField] string uniqueIdentifier = "";
static Dictionary<string, SaveableEntity> globalLookup = new Dictionary<string, SaveableEntity>();
public string GetUniqueIdentifier()
{
return uniqueIdentifier;
}
public object CaptureState()
{
Dictionary<string, object> state = new Dictionary<string, object>();
foreach (ISaveable saveable in GetComponents<ISaveable>())
{
state[saveable.GetType().ToString()] = saveable.CaptureState();
}
return state;
}
public void RestoreState(object state)
{
Dictionary<string, object> stateDict = (Dictionary<string, object>)state;
foreach (ISaveable saveable in GetComponents<ISaveable>())
{
string typeString = saveable.GetType().ToString();
if (stateDict.ContainsKey(typeString))
{
saveable.RestoreState(stateDict[typeString]);
}
}
}
#if UNITY_EDITOR
private void Update()
{
if (Application.IsPlaying(gameObject)) return;
if (string.IsNullOrEmpty(gameObject.scene.path)) return;
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty property = serializedObject.FindProperty("uniqueIdentifier");
if (string.IsNullOrEmpty(property.stringValue) || !IsUnique(property.stringValue))
{
property.stringValue = System.Guid.NewGuid().ToString();
serializedObject.ApplyModifiedProperties();
}
globalLookup[property.stringValue] = this;
}
#endif
private bool IsUnique(string candidate)
{
if (!globalLookup.ContainsKey(candidate)) return true;
if (globalLookup[candidate] == this) return true;
if (globalLookup[candidate] == null)
{
globalLookup.Remove(candidate);
return true;
}
if (globalLookup[candidate].GetUniqueIdentifier() != candidate)
{
globalLookup.Remove(candidate);
return true;
}
return false;
}
}
}
SavingSystem:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace RPG.Saving
{
public class SavingSystem : MonoBehaviour
{
public IEnumerator LoadLastScene(string saveFile)
{
Dictionary<string, object> state = LoadFile(saveFile);
int buildIndex = SceneManager.GetActiveScene().buildIndex;
if (state.ContainsKey("lastSceneBuildIndex"))
{
buildIndex = (int)state["lastSceneBuildIndex"];
}
yield return SceneManager.LoadSceneAsync(buildIndex);
RestoreState(state);
}
public void Save(string saveFile)
{
Dictionary<string, object> state = LoadFile(saveFile);
CaptureState(state);
SaveFile(saveFile, state);
}
public void Load(string saveFile)
{
RestoreState(LoadFile(saveFile));
}
private void SaveFile(string saveFile, object state)
{
string path = GetPathFromSaveFile(saveFile);
print("Saving to " + path);
using (FileStream stream = File.Open(path, FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, state);
}
}
private Dictionary<string, object> LoadFile(string saveFile)
{
string path = GetPathFromSaveFile(saveFile);
if (!File.Exists(path))
{
return new Dictionary<string, object>();
}
using (FileStream stream = File.Open(path, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
return (Dictionary<string, object>)formatter.Deserialize(stream);
}
}
private void CaptureState(Dictionary<string, object> state)
{
foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
{
state[saveable.GetUniqueIdentifier()] = saveable.CaptureState();
}
state["lastSceneBuildIndex"] = SceneManager.GetActiveScene().buildIndex;
}
private void RestoreState(Dictionary<string, object> state)
{
foreach (SaveableEntity saveable in FindObjectsOfType<SaveableEntity>())
{
string id = saveable.GetUniqueIdentifier();
if (state.ContainsKey(id))
{
saveable.RestoreState(state[id]);
}
}
}
private string GetPathFromSaveFile(string saveFile)
{
return Path.Combine(Application.persistentDataPath, saveFile + ".sav");
}
public void Delete(string saveFile)
{
string path = GetPathFromSaveFile(saveFile);
print("Deleting save at " + path);
File.Delete(GetPathFromSaveFile(saveFile));
}
}
}
SavingWrapper:
using System;
using System.Collections;
using System.Collections.Generic;
using RPG.Saving;
using UnityEngine;
namespace RPG.SceneManagement
{
public class SavingWrapper : MonoBehaviour
{
const string defaultSaveFile = "save";
[SerializeField] float fadeInTime = 0.2f;
private void Awake()
{
StartCoroutine(LoadLastScene());
}
private IEnumerator LoadLastScene()
{
yield return GetComponent<SavingSystem>().LoadLastScene(defaultSaveFile);
Fader fader = FindObjectOfType<Fader>();
fader.FadeOutImmediate();
yield return fader.FadeIn(fadeInTime);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
Save();
}
if (Input.GetKeyDown(KeyCode.L))
{
Load();
}
if (Input.GetKeyDown(KeyCode.Delete))
{
Delete();
}
}
public void Save()
{
GetComponent<SavingSystem>().Save(defaultSaveFile);
}
public void Load()
{
GetComponent<SavingSystem>().Load(defaultSaveFile);
}
public void Delete()
{
GetComponent<SavingSystem>().Delete(defaultSaveFile);
}
}
}
Portal:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.AI;
using RPG.Saving;
namespace RPG.SceneManagement
{
public class Portal : MonoBehaviour
{
enum DestinationIdentifier
{
A, B, C, D, E, F, G
}
[SerializeField] int sceneToLoad = -1;
[SerializeField] Transform spawnPoint;
[SerializeField] DestinationIdentifier destination;
[SerializeField] float fadeOutTime = 1f;
[SerializeField] float fadeInTime = 2f;
[SerializeField] float fadeWaitTime = 0.5f;
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
StartCoroutine(Transition());
}
}
private IEnumerator Transition()
{
if (sceneToLoad < 0)
{
Debug.LogError("Scene to load not set.");
yield break;
}
DontDestroyOnLoad(gameObject);
Fader fader = FindObjectOfType<Fader>();
SavingWrapper savingWrapper = FindObjectOfType<SavingWrapper>();
yield return fader.FadeOut(fadeOutTime);
savingWrapper.Save();
yield return SceneManager.LoadSceneAsync(sceneToLoad);
savingWrapper.Load();
Portal otherPortal = GetOtherPortal();
UpdatePlayer(otherPortal);
savingWrapper.Save();
yield return new WaitForSeconds(fadeWaitTime);
yield return fader.FadeIn(fadeInTime);
Destroy(gameObject);
}
private void UpdatePlayer(Portal otherPortal)
{
GameObject player = GameObject.FindWithTag("Player");
player.GetComponent<NavMeshAgent>().enabled = false;
//player.GetComponent<NavMeshAgent>().Warp(otherPortal.spawnPoint.position);
player.transform.position = otherPortal.spawnPoint.position;
player.transform.rotation = otherPortal.spawnPoint.rotation;
player.GetComponent<NavMeshAgent>().enabled = true;
}
private Portal GetOtherPortal()
{
foreach (Portal portal in FindObjectsOfType<Portal>())
{
if (portal == this) continue;
if (portal.destination != destination) continue;
return portal;
}
return null;
}
}
}
Sorry that was lengthy, but I can’t figure out what’s wrong. If there’s any other file you need to see, let me know.