FNAF 06 - Night Manager
In this video, we build out the Night Timer system — the heartbeat of every FNAF-style game. The clock now ticks from 12AM to 6AM, complete with night intro text, a working HUD clock, and the iconic 5AM to 6AM rollover where the screen fades, cheering plays, and you advance to the next night. This system ties everything we’ve made so far into a real survival loop that feels just like the original games.
This is the sixth episode in our series where we’re building a complete 2D FNAF-style game from scratch using real programming and logic — no shortcuts, just solid Unity skills.
You can copy and paste the complete code directly from the tutorial below, or…
Just want the Unity Package with everything pre-built? Become a Patreon member at the Creator Level to download the entire Unity package:
NightManager.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class NightManager : MonoBehaviour
{
[Header("References")]
public TMPro.TMP_Text nightIntroText;
public CanvasGroup nightIntroCG;
public NightTimer nightTimer;
public WinSequence winSequence;
public UnityEngine.UI.Image panelBackground;
[Header("Timing")]
public float fadeDuration = 1.2f;
public float holdDuration = 2.0f;
private int currentNight = 0;
private void Awake()
{
}
void Start()
{
currentNight = PlayerPrefs.GetInt("Night", 1);
StartCoroutine(PlayNightIntro());
nightTimer.OnNightEnd += this.OnNightEnd;
}
public void StartNight()
{
StopAllCoroutines();
winSequence?.Reset();
nightTimer?.Reset();
StartCoroutine(PlayNightIntro());
}
private void OnNightEnd()
{
winSequence.PlayWinSequence();
}
public void EnableBlackBackground()
{
panelBackground.gameObject.SetActive(true);
Color temp_color = panelBackground.color;
panelBackground.color = new Color(temp_color.r, temp_color.g, temp_color.b, 1.0f);
}
public void DisableBlackBackground()
{
panelBackground.gameObject.SetActive(false);
Color temp_color = panelBackground.color;
panelBackground.color = new Color(temp_color.r, temp_color.g, temp_color.b, 1.0f);
}
private IEnumerator PlayNightIntro()
{
nightIntroText.text = $"Night {currentNight}";
nightIntroCG.alpha = 1.0f;
nightIntroCG.gameObject.SetActive(true);
EnableBlackBackground();
//Start full darkness so we don't see the screen beneath.
//yield return Fade(0.0f, 1.0f);
yield return new WaitForSeconds(holdDuration);
yield return Fade(1.0f, 0.0f);
nightIntroCG.gameObject.SetActive(false);
DisableBlackBackground();
nightTimer.BeginNight();
}
private IEnumerator Fade(float start, float end)
{
float t = 0.0f;
nightIntroCG.alpha = start;
while( t < 1.0f)
{
t += Time.unscaledDeltaTime / fadeDuration;
nightIntroCG.alpha = Mathf.Lerp(start, end, t);
yield return null;
}
nightIntroCG.alpha = end;
}
public void AdvanceNight()
{
currentNight++;
PlayerPrefs.SetInt("Night", currentNight);
PlayerPrefs.Save();
StartNight();
}
public int GetCurrentNight()
{
return currentNight;
}
}
NightTimer.cs
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class NightTimer : MonoBehaviour
{
[Header("References")]
[SerializeField] private NightManager nightManager;
[SerializeField] TMPro.TMP_Text textClockHUD;
[Header("Time")]
[SerializeField] private float hourLength = 60f; //sixty seconds per hour.
public event Action OnNightBegin;
public event Action OnHourChanged;
public event Action OnNightEnd;
private int hour;
private float time;
private bool running;
private string[] hour_labels = { "12:00 AM", "01:00 AM", "02:00 AM", "03:00 AM", "04:00 AM", "05:00 AM", "06:00 AM" };
public void BeginNight()
{
hour = 0;
time = 0f;
running = true;
textClockHUD.gameObject.SetActive(true);
UpdateHUD();
OnNightBegin?.Invoke();
}
public void Reset()
{
running = false;
hour = 0;
time = 0;
UpdateHUD();
}
private void UpdateHUD()
{
textClockHUD.text = hour_labels[Mathf.Clamp(hour, 0, hour_labels.Length - 1)];
}
// Update is called once per frame
void Update()
{
if (!running) return;
time += Time.deltaTime;
if (time >= hourLength)
{
time = 0f;
hour++;
OnHourChanged?.Invoke(hour);
if (hour >= 6)
{
running = false;
OnNightEnd?.Invoke();
this.textClockHUD.gameObject.SetActive(false);
}
else
{
UpdateHUD();
}
}
}
}
WinSequence.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
public class WinSequence : MonoBehaviour
{
[Header("References")]
[SerializeField] private NightManager nightManager;
[SerializeField] private Animator rollOverAnimtor;
[SerializeField] private CanvasGroup fadeGroup;
[SerializeField] private AudioSource cheering;
[Header("Timing")]
[SerializeField] private float preRollHold = 0.5f;
[SerializeField] private float postRollHold = 0.75f;
[SerializeField] private float fadeDuration = 2.0f;
private bool running = false;
public void Reset()
{
if(fadeGroup)
fadeGroup.alpha = 0.0f;
running = false;
rollOverAnimtor.ResetTrigger("Roll");
rollOverAnimtor.Play("Idle");
rollOverAnimtor.Update(0.0f);
}
public void PlayWinSequence()
{
fadeGroup.gameObject.SetActive(true);
StartCoroutine(WinRoutine());
}
private IEnumerator WinRoutine()
{
if (running)
yield return null;
//Fade In
float t = 0.0f;
while (t < 1.0f)
{
t += Time.deltaTime / fadeDuration;
fadeGroup.alpha = Mathf.Clamp01(t);
yield return null;
}
//Hold Before Roll
yield return new WaitForSeconds(preRollHold);
//Roll over starts and wait a frame for animation to trigger
if (rollOverAnimtor)
{
rollOverAnimtor.SetTrigger("Roll");
yield return null;
}
//Wait for the rollover to finish
if (cheering != null)
cheering.Play();
AnimatorStateInfo stateInfo = rollOverAnimtor.GetCurrentAnimatorStateInfo(0);
yield return new WaitForSeconds(stateInfo.length);
//Hold on rollout
yield return new WaitForSeconds(postRollHold);
nightManager.EnableBlackBackground();
//Fade out
t = 0.0f;
while(t < 1.0f)
{
t += Time.deltaTime / fadeDuration;
fadeGroup.alpha = Mathf.Clamp01(1.0f - t);
yield return null;
}
nightManager.AdvanceNight();
}
}
NightEditorTools.cs
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class NightEditorTools
{
[MenuItem("FNAF/Clear Night Progress")]
public static void ClearNightProgress()
{
PlayerPrefs.DeleteKey("Night");
Debug.Log("Cleared Player Prefs key: Night");
}
}