Yes the player is still in the core prefab. The message that the playercontroller is missing comes at the end after the scene is loaded and saved when the script tries to enable it again. Theres is no persisten object or saving prefab in the hierarchy in any scene when the game isnt loading. But after checking the console a bit closer with print statements I can see that the scene is loaded many times that’s why the UI flickers. Before the code that enables the new playerController is runned, the scene is loaded and saved many times. The player is also not spawning inside any of the portals as the spawning point is outside of the portal, but maybe thats whats happening? Maybe something is wrong with the saving wrapper or saving system after introducing the new asset pack? I put in my script with the print statements and the console messages, i had to upload the console messages as images as i couldnt find a way to copy them directly.
I also see that after saving the debug log in the awake function in the fighter script prints out the names of the enemies in the first scene, scene 0. I have no enemies in the second scene, scene 1, so they should not be printed right? Maybe scene 0 and scene 1 is loaded many times? But from the print statements I can only see scene 1 loaded many times.
I also tried printing out the newPlayerController throughout the script and it is found many times as the scene is saved and loaded but right before it tries to enable it after, yield return fader.FadeIn(fadeInTime);, the newPlayerController is null.
after the scene has loaded i have 4 portals from the first scene, scene 0, under dont destroy on load in the next scene.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.AI;
using RPG.Control;
using GameDevTV.Saving;
namespace RPG.SceneManagement
{
public class Portal : MonoBehaviour
{
// set it to -1 so we get an error if we have not set it up in unity.
[SerializeField] int sceneToLoad = -1;
[SerializeField] Transform spawnPoint;
[SerializeField] float fadeOutTime = 0.5f;
[SerializeField] float fadeInTime = 0.5f;
[SerializeField] float fadeWaitTime = 1f;
// set letters on our portals so we spawn at the correct one.
enum DestinationIdentifier
{
A,B,C,D,E
}
[SerializeField] DestinationIdentifier destination;
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.tag == "Player")
{
// this co-routine will run between scenes. makes it possible to transfer
// information into the new scene.
StartCoroutine(Transition());
print("Portal triggered");
}
}
private IEnumerator Transition()
{
// If we forgott to set the scene build index.
if (sceneToLoad < 0)
{
Debug.LogError("Scene to load not set.");
// exits function.
yield break;
}
// must keep the portal alive after next scene has
// loaded, until we have finished the co-routine. so
// the gameobject and this script dont dissapear.
print("DONT DESTROY GAMEOBJECT");
DontDestroyOnLoad(gameObject);
// save current level. saves for all scenes.
SavingWrapper wrapper = FindObjectOfType<SavingWrapper>();
// fade out from scene.
Fader fader = FindObjectOfType<Fader>();
// remove controll from the player so we dont run in another portal when this transition is running and create two
// transitions running at the same time creating a race condition. if not we could portal to an arbitrary location.
PlayerController playerController = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>();
print("DISABLING PLAYER CONTROLLER");
playerController.enabled = false;
// with yield return the IEnumerator is run as a coroutine
// and code after the yield return waits for it to finish.
yield return fader.FadeOut(fadeOutTime);
wrapper.Save();
// we use the buildindex to load our scene. The courutine runs until this point
// and loads the scene. It will then return the async operation to unity. Then
// unity will call this co-routune again when the scene has finished loading
// and finish the rest of the code.
print("LOADING NEW SCENE " + sceneToLoad );
yield return SceneManager.LoadSceneAsync(sceneToLoad);
print("SCENE LOADED");
// disable player again when we load the scene. this is a new player so must find the player again.
PlayerController newPlayerController = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>();
print("DISABLING NEW PLAYER CONTROLLER");
newPlayerController.enabled = false;
print("LOADING STATES");
// load current level.
wrapper.Load();
print("GETTING PORTAL");
// get hold of the portal in the scene we enter. we want to spawn here.
Portal otherPortal = GetOtherPortal();
// then we update the player, place him at the portal.
print("UPDATING PLAYER TO PORTAL");
UpdatePlayer(otherPortal);
// we save again after we placed the player so if we quite the game
// we will load into this spot in this scene.
print("SAVE STATE");
wrapper.Save();
// wait a bitt for camera etc to stabilaze.
yield return new WaitForSeconds(fadeWaitTime);
// fade into scene. must do this after portal and player is set.
yield return fader.FadeIn(fadeInTime);
print("ENABLING PLAYERCONTROLLER");
// restore controll to player.
newPlayerController.enabled = true; // error points here.
print("DESTROY GAMEOBJECT");
Destroy(gameObject);
}
// return portal in the new scene not the one this script is attached to.
// must spawn at the right location.
private Portal GetOtherPortal()
{
foreach (Portal portal in FindObjectsOfType<Portal>())
{
// return the portal with the correct enum. dont return
// portal from the scene we came from.
if (portal == this) continue;
if (this.destination == portal.destination)
{
return portal;
}
}
// if we dont find a portal
return null;
}
// set position and rotation of player based on the spawn point.
private void UpdatePlayer(Portal otherPortal)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
// since we load in our player position above in wrapper.load we must disable the navmesh agent before
// we place our player or we can run into a bug.
player.GetComponent<NavMeshAgent>().enabled = false;
// use warp to set position. must use warp or else if we have multiple terrains in a
// scene we could be warped to a wrong location.
player.GetComponent<NavMeshAgent>().Warp(otherPortal.spawnPoint.position);
player.transform.rotation = otherPortal.spawnPoint.rotation;
player.GetComponent<NavMeshAgent>().enabled = true;
}
}
}