I have a script that I attach to a persistent object that observes the player’s HP to determine if the camera needs to fade to black, then perform a reload on the current scene.
The problem is, for some reason this code will behave properly on the first death, but on the second will get stuck inside the coroutine loop, “Fading the screen out…” In fact, the preceding Fader.FadeOut() method dosen’t even do anything as far as I can tell.
I’m clearly missing something here. Since it’s a Coroutine, do I have to somehow reset it to a default state or something? I’m not knowledgeable about IEnumerators, and I suspect that’s where my problem lies.
HealthSystem.cs
This behavior goes on the player.
using UnityEngine;
public class HealthSystem : MonoBehaviour
{
#region Variables / Properties
public int HP;
public int MaxHP;
#endregion Variables / Properties
// SNIP Unnecessary items to this question.
}
Fader.cs
This behavior goes on the main camera.
using UnityEngine;
public class Fader : MonoBehaviour
{
#region Constants
private const float _OpaqueEnough = 0.99f;
#endregion Constants
#region Variables / Properties
public Texture2D FadeImage;
public float FadeRate = 0.1f;
public Color Tint = new Color(0,0,0, 1);
public Color TargetTint = new Color(0,0,0, 0);
public bool ScreenHidden
{
get { return Tint.a >= _OpaqueEnough; }
}
public bool ScreenShown
{
get { return Tint.a < _OpaqueEnough; }
}
#endregion Variables / Properties
#region Engine Hooks
void OnGUI()
{
GUI.color = Tint;
GUI.depth = -1;
GUI.DrawTexture(new Rect(0,0, Screen.width,Screen.height), FadeImage);
}
void FixedUpdate()
{
Tint = Color.Lerp(Tint, TargetTint, FadeRate * Time.deltaTime);
}
#endregion Engine Hooks
#region Methods
public void FadeOut()
{
Tint = new Color(0,0,0, 0);
TargetTint = new Color(0,0,0, 1);
}
public void FadeIn()
{
Tint = new Color(0,0,0, 1);
TargetTint = new Color(0,0,0, 0);
}
#endregion Methods
}
TransitionManager.cs
This behavior goes on the persistent object.
sing UnityEngine;
using System;
using System.Linq;
using System.Collections.Generic;
public class TransitionManager : ManagerMonoBehavior
{
#region Variables / Properties
public string DebugCreateTime;
public Vector3 spawnPosition;
public Vector3 spawnRotation;
public int targetSceneID = -1;
public string targetSceneName = string.Empty;
public GameObject playerPiece;
public DateTime CreateTime { get; private set; }
public static TransitionManager Instance
{
get { return TransitionManager.FindOldestInstance(); }
}
#endregion Variables / Properties
#region Engine Hooks
#endregion Engine Hooks
#region Base Class Overrides
public override void SelfDestructIfOthersExist()
{
TransitionManager[] objects = (TransitionManager[]) FindObjectsOfType(typeof(TransitionManager));
IEnumerable<TransitionManager> destructables = objects.Where(o => o.IsInitialInstance == false);
if(DebugMode)
Debug.LogWarning(destructables.Count() + " other managers were found! Destroying them.");
foreach(TransitionManager current in destructables)
{
Destroy(current.gameObject);
}
}
#endregion Base Class Overrides
#region Methods
public static TransitionManager FindOldestInstance()
{
TransitionManager[] objects = (TransitionManager[]) FindObjectsOfType(typeof(TransitionManager));
if(objects.Length == 0)
throw new Exception("TransitionManager could not find any Transition Managers in the scene!");
TransitionManager oldest = objects.FirstOrDefault(a => a.IsInitialInstance == true);
if(oldest == default(TransitionManager))
{
oldest = objects.First();
oldest.IsInitialInstance = true;
}
return oldest;
}
public void ChangeScenes()
{
if(targetSceneID == -1)
{
Application.LoadLevel(targetSceneName);
}
else
{
Application.LoadLevel(targetSceneID);
}
}
// Information:
// -------------------------------------------------------------------------------
// The following methods are responsible for the player piece being copied to the
// destination scene when it has loaded, via the SceneSpawner script (this script
// is pre-set on the GameCamera prefab!)
//
// In the prior scene, the player's piece is acquired by the manager, which means
// a clone is saved to the class.
//
// In the target scene, when it is loaded, the SceneSpawner will Instantiate the
// piece, then lock the camera to it.
public void AcquirePlayerPiece(GameObject piece)
{
if(piece == null)
throw new ArgumentNullException("Must specify a game object to copy and transition to the new scene.");
playerPiece = (GameObject) GameObject.Instantiate(piece, piece.transform.position, piece.transform.rotation);
playerPiece.name = piece.name;
UnityEngine.Object.Destroy(piece);
UnityEngine.Object.DontDestroyOnLoad(playerPiece);
}
// Information:
// -------------------------------------------------------------------------------
// The following methods are used when the transition is ready, for instance, when
// the screen is fully faded and the player cannot see it.
//
// You give these methods the position, rotation, and scene info for the scene you
// want to perform the transition to. ChangeScene() will automatically call that
// information and perform the scene swap.
public void PrepareTransition(Vector3 targetPos, Vector3 targetRot, int sceneID)
{
if(DebugMode)
Debug.Log("Prepping a transition to: " + Environment.NewLine
+ "Location: " + targetPos + Environment.NewLine
+ "Rotation: " + targetRot + Environment.NewLine
+ "Scene ID: " + sceneID);
SetLocationRotation(targetPos, targetRot);
targetSceneID = sceneID;
targetSceneName = string.Empty;
}
public void PrepareTransition(Vector3 targetPos, Vector3 targetRot, string sceneName)
{
if(DebugMode)
Debug.Log("Prepping a transition to: " + Environment.NewLine
+ "Location: " + targetPos + Environment.NewLine
+ "Rotation: " + targetRot + Environment.NewLine
+ "Scene: " + sceneName);
SetLocationRotation(targetPos, targetRot);
targetSceneName = sceneName;
targetSceneID = -1;
}
private void SetLocationRotation(Vector3 targetPos, Vector3 targetRot)
{
spawnPosition = targetPos;
spawnRotation = targetRot;
}
#endregion Methods
}
PlayerDeathReloader.cs
This behavior goes on the persistent object.
using UnityEngine;
using System.Collections;
public class PlayerDeathReloader : MonoBehaviour
{
#region Variables / Properties
public bool DebugMode = false;
public GameObject Player;
public int LastHP;
private Fader _fader;
private HealthSystem _health;
private TransitionManager _sceneChange;
#endregion Variables / Properties
#region Engine Hooks
public void Start()
{
_sceneChange = TransitionManager.Instance;
_fader = (Fader) FindObjectOfType(typeof(Fader));
AcquirePlayer();
}
public void Update()
{
if(_health.HP == LastHP)
return;
if(DebugMode)
Debug.Log("The player's HP has changed from " + LastHP + " to " + _health.HP);
LastHP = _health.HP;
if(_health.HP > 0)
return;
if(DebugMode)
Debug.Log("The player has died!");
StartCoroutine(ReloadLevelSequence());
}
public void OnLevelWasLoaded()
{
AcquirePlayer();
}
#endregion Engine Hooks
#region Methods
private void AcquirePlayer()
{
Player = GameObject.FindWithTag("Player");
_health = Player.GetComponent<HealthSystem>();
LastHP = _health.HP;
}
private IEnumerator ReloadLevelSequence()
{
if(DebugMode)
Debug.Log("Performing level reload...");
_fader.FadeOut();
while(_fader.ScreenShown)
{
if(DebugMode)
Debug.Log("Fading the screen out...");
yield return 0;
}
_sceneChange.ChangeScenes();
}
#endregion Methods
}