FNAF 10 - Bugs & The End
In this final installment, we bring the FNAF 2D series to its conclusion by adding the essential "boring" work that makes a game actually feel complete. We tackle a massive spreadsheet of bug fixes, from managing audio playheads so background music stays seamless between rooms, to fixing the power-down logic to ensure you can't sneak out a win during a blackout. We also dive into the technical overhaul of the Night Manager using Scriptable Objects—creating a modular system that allows for unique animatronic behavior and custom configurations for every single night.
To wrap everything up, we walk through the creation of the end-game win screen in Photoshop, including a hidden secret message for the eagle-eyed viewers. Whether you’re here for the technical Unity breakdown or to see if I can actually survive the "impossible" Night 5 playtest, this finale has it all.
You can copy and paste the complete code directly from the tutorial below, or…
Just want the full Unity package with everything pre-built? The entire Unity project package, including all scripts, modular systems, and final assets seen in this video, is available for a one-time purchase of $20 on my Patreon account: https://www.patreon.com/glecakes/shop
EndScreenManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class EndScreenManager : MonoBehaviour
{
[SerializeField]
UnityEngine.UI.Image blackFade;
[SerializeField]
CanvasGroup winGraphics;
[SerializeField]
float fadeInTime = 1.0f;
[SerializeField]
float holdOnEndTime = 8.0f;
[SerializeField]
float fadeOutTime = 1.0f;
//Debugging
float debugTimer = 3.0f;
Coroutine endSequence = null;
//Singals when the end sequence has finished playing.
public event Action OnWinScreenFinished;
// Start is called before the first frame update
void Start()
{
blackFade.color = new Color(0f, 0f, 0f, 0f);
winGraphics.alpha = 0.0f;
}
public void PlayEndSequence()
{
endSequence = StartCoroutine(CR_EndSequence());
}
IEnumerator CR_EndSequence()
{
yield return CR_BlackFadeIn();
yield return CR_WinGraphicsFadeIn();
yield return new WaitForSeconds(holdOnEndTime);
yield return CR_WinGraphicsFadeOut();
OnWinScreenFinished?.Invoke();
}
IEnumerator CR_BlackFadeIn()
{
float alpha = 0.0f;
float fadeRate = 1.0f / fadeInTime;
while (alpha < fadeInTime)
{
alpha += Time.deltaTime * fadeRate;
blackFade.color = new Color(0f, 0f, 0f, alpha);
yield return null;
}
alpha = 1.0f;
blackFade.color = new Color(0f, 0f, 0f, alpha);
}
IEnumerator CR_BlackFadeOut()
{
float alpha = 0.0f;
float fadeRate = 1.0f / fadeOutTime;
while (alpha < fadeOutTime)
{
alpha += Time.deltaTime * fadeRate;
blackFade.color = new Color(0f, 0f, 0f, 1.0f - alpha);
yield return null;
}
alpha = 0.0f;
blackFade.color = new Color(0f, 0f, 0f, alpha);
}
IEnumerator CR_WinGraphicsFadeIn()
{
float alpha = 0.0f;
float fadeRate = 1.0f / fadeInTime;
while (alpha < fadeInTime)
{
alpha += Time.deltaTime * fadeRate;
winGraphics.alpha = alpha;
yield return null;
}
alpha = 1.0f;
winGraphics.alpha = alpha;
}
IEnumerator CR_WinGraphicsFadeOut()
{
float alpha = 0.0f;
float fadeRate = 1.0f / fadeOutTime;
while (alpha < fadeOutTime)
{
alpha += Time.deltaTime * fadeRate;
winGraphics.alpha = 1.0f - alpha;
yield return null;
}
alpha = 0.0f;
winGraphics.alpha = alpha;
}
// Update is called once per frame
void Update()
{
if (debugTimer > 0.0f)
{
debugTimer -= Time.deltaTime;
if (debugTimer <= 0.0f && endSequence == null)
{
PlayEndSequence();
}
}
}
}
AnimatronicNightConfig.cs
using System;
using UnityEngine;
[Serializable]
public class AnimatronicNightConfig
{
public AnimatronicSystem.Animatronics animatronic;
[Header("Starting Point")]
public RoomConfig.RoomID start;
[Header("Movement")]
public int startingHour;
public float timerVariability;
public float transitionDuration;
public float timeToRoomChange;
[Header("Core Rooms")]
public float prob_stage;
public float prob_hall;
public float prob_supply;
public float prob_westDoor;
public float prob_eastDoor;
public float prob_office;
}
NightConfig.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "FNAF/Night Config")]
public class NightConfig : ScriptableObject
{
[Header("Night Number")]
public int nightNumber = 1;
[Header("Intro Audio")]
public AudioClip introClip;
[Header("Starting Power")]
public float startingPower = 100f;
[Header("Animatronics")]
public List animatronics = new();
}
NightConfigTable.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "FNAF/Night Config Table")]
public class NightConfigTable : ScriptableObject
{
[Tooltip("Index 0 = Night 1, index 1 = Night 2, etc...")]
public List nights = new();
public NightConfig GetNight(int night)
{
if(night > nights.Count)
{
return null;
}
return nights[night-1];
}
}