Avoid common pitfalls (hiding messy code, too many facades)
Create maintainable, simplified interfaces for complex systems
Developer's Note: "Never Panic Early"
โ ๏ธ What to Expect During Implementation
As we implement this pattern, we'll be creating multiple files in a specific order.
You will see errors in Unity and your IDE until all files are complete.
How to Handle Development Errors
Don't Ignore: Note the errors - they're telling you something
Don't Panic: These are expected until all files are created
Stay Calm: Follow the implementation order and errors will resolve
As Apollo 13's Fred Haise said: "Never Panic Early"
This is a critical skill for software development. Note problems, but stay focused on the implementation path.
๐ Real-World Analogy
Starting Your Car
Think about what happens when you turn the ignition key:
Behind the Scenes (Complex):
Battery sends electrical current to starter motor
Starter motor cranks the engine
Fuel pump activates, sending gas to cylinders
Spark plugs ignite fuel-air mixture
Pistons start moving, crankshaft rotates
Cooling system activates to prevent overheating
Oil pump circulates lubricant
What You Do (Simple):
Turn the key. ๐
The Pattern: The ignition system is a facade that hides complex engine subsystems behind a simple interface
๐ Understanding the Facade Pattern
Core Concept
Provide a unified, simplified interface to a set of interfaces in a subsystem, making the subsystem easier to use.
Key Characteristics:
Single class acts as simplified interface
Wraps complex subsystem interactions
Client doesn't know about subsystem details
Reduces coupling between client and subsystem
Doesn't prevent direct subsystem access (optional)
The Problem It Solves
Your game has multiple complex, interacting systems. Clients need to use these systems but shouldn't need to understand their intricate interactions.
Pattern Type: Structural - concerned with composing classes into larger structures
โ๏ธ Facade vs Adapter Pattern
Aspect
Facade Pattern
Adapter Pattern
Intent
Simplify complex interface
Convert incompatible interface
Interface
Creates NEW simplified interface
Adapts EXISTING interface to match target
Number of Classes
Wraps multiple subsystems
Usually adapts one class
Goal
Ease of use
Compatibility
Key Difference: Facade establishes a new interface; Adapter adapts an old interface
Behavior: Burns fuel every second, stops engine when depleted
๐ป CoolingSystem Subsystem (1/2)
๐ File Structure Note - PRODUCTION CODE
Create a new file: Assets/Scripts/Subsystems/CoolingSystem.cs
This subsystem manages engine cooling for the bike. โ ๏ธ This code goes into your Unity project for Blade Racer.
using UnityEngine;
using System.Collections;
publicclass CoolingSystem : MonoBehaviour
{
public BikeEngine engine;
public IEnumerator coolEngine;
privatebool _isPaused;
voidStart()
{
coolEngine = CoolEngine();
}
publicvoidPauseCooling()
{
_isPaused = !_isPaused;
}
publicvoidResetTemperature()
{
engine.currentTemp = 0.0f;
}
๐ป CoolingSystem Subsystem (2/2)
๐ Continuing CoolingSystem.cs
Add this coroutine method to the CoolingSystem class from the previous slide.
Create a new file: Assets/Scripts/Subsystems/TurboCharger.cs
This subsystem manages turbo boost functionality for the bike. โ ๏ธ This code goes into your Unity project for Blade Racer.
Create a new file: Assets/Scripts/Testing/ClientFacade.cs
This script tests your Facade pattern implementation with GUI buttons. โ ๏ธ This is temporary testing code - you can remove it after testing your implementation.
using UnityEngine;
publicclass ClientFacade : MonoBehaviour
{
private BikeEngine _bikeEngine;
voidStart()
{
_bikeEngine = gameObject.AddComponent();
}
voidOnGUI()
{
if (GUILayout.Button("Turn On"))
_bikeEngine.TurnOn();
if (GUILayout.Button("Turn Off"))
_bikeEngine.TurnOff();
if (GUILayout.Button("Toggle Turbo"))
_bikeEngine.ToggleTurbo();
}
}
๐ก Alternative: When you have multiple test scripts with overlapping GUI buttons,
consider using TestPanel.cs to combine all test controls into one draggable window.
See the Event Bus lecture for the unified TestPanel implementation.
Beauty: Client has NO knowledge of FuelPump, CoolingSystem, or TurboCharger!
๐ฎ Updating TestPanel for Facade Pattern
๐ Evolving TestPanel.cs
Add Facade Pattern keyboard shortcuts and section to your TestPanel.cs.
// Add to TestPanel fieldsprivate bool _facadeExpanded = true;
private BikeEngine _bikeEngine;
voidStart()
{
// ... existing code ...
_bikeEngine = FindFirstObjectByType<BikeEngine>();
}
// Add to Update() - Facade Pattern shortcutsif (_bikeEngine != null)
{
if (Input.GetKeyDown(KeyCode.O)) // O = On
_bikeEngine.TurnOn();
if (Input.GetKeyDown(KeyCode.I)) // I = off (I looks like |)
_bikeEngine.TurnOff();
if (Input.GetKeyDown(KeyCode.U)) // U = tUrbo
_bikeEngine.ToggleTurbo();
}
// Add DrawFacadeSection() methodvoidDrawFacadeSection()
{
if (_bikeEngine == null) return;
GUI.backgroundColor = new Color(0.8f, 0.4f, 0f); // Brown
_facadeExpanded = GUILayout.Toggle(
_facadeExpanded, "โผ Facade Pattern", "button");
GUI.backgroundColor = Color.white;
if (_facadeExpanded)
{
GUILayout.BeginVertical("box");
if (GUILayout.Button("Turn On (O)"))
_bikeEngine.TurnOn();
if (GUILayout.Button("Turn Off (I)"))
_bikeEngine.TurnOff();
if (GUILayout.Button("Toggle Turbo (U)"))
_bikeEngine.ToggleTurbo();
GUILayout.EndVertical();
}
}
Keymap Update: Add to DrawKeymapWindow(): O = Turn On, I = Turn Off, U = Toggle Turbo
๐ฏ The Power of Abstraction
Without Facade:
// Client needs to know EVERYTHINGvar fuelPump = gameObject.AddComponent();
var coolingSystem = gameObject.AddComponent();
var turboCharger = gameObject.AddComponent();
fuelPump.engine = this;
coolingSystem.engine = this;
turboCharger.engine = this;
StartCoroutine(fuelPump.burnFuel);
StartCoroutine(coolingSystem.coolEngine);
// ... and on and on ...
With Facade:
// Client just uses the engine
_bikeEngine.TurnOn();
// Only create subsystems when neededprivate FuelPump _fuelPump;
public FuelPump FuelPump
{
get
{
if (_fuelPump == null)
_fuelPump = gameObject.AddComponent();
return _fuelPump;
}
}
โ Facade pattern provides simplified interface to complex subsystems
โ Hides complexity, not functionality
โ Different from Adapter (simplify vs adapt)
โ Implemented realistic bike engine with 3 subsystems
โ Client code reduced from 20+ lines to 1 line
โ Easy to extend with new subsystems
Key Takeaways:
Facade creates NEW simplified interface
Subsystems remain accessible if needed
Don't use to hide bad code - use to organize good code
Beware of too many manager classes (Singleton + Facade)
Perfect for layered architecture
Excellent for refactoring legacy systems
Gaming History Moment ๐น๏ธ
PlayStation Origin Story: The Failed Partnership (1991-1994)
In 1988, Sony and Nintendo partnered to create a CD-ROM add-on for the SNES called the "Play Station." At 1991 CES, Sony announced the partnership. The next day, Nintendo publicly announced they'd partnered with Philips instead, humiliating Sony on the world stage. Sony executives wanted to abandon gaming entirely, but engineer Ken Kutaragi convinced them to proceed alone.
The result was the PlayStation (1994). Its secret weapon? A simple, unified development environment. While Saturn and N64 had complex multi-chip architectures requiring assembly code, PlayStation offered a clean C library facade. Developers called simple functions like LoadTexture() or PlayAudio(), and Sony's SDK handled the messy hardware details. This facade made development so easy that third-party devs flocked to PlayStation, giving it 102 million sales.
Connection to Facade Pattern
PlayStation's SDK was a Facade Pattern triumph! Behind the scenes, the hardware had complex subsystems - graphics chips, sound processors, CD-ROM controllers - each with intricate APIs. Sony's SDK provided a facade: simple, high-level functions that hid this complexity. Just like our BikeEngineFacade wraps FuelSystem, CoolingSystem, and TurboSystem into one simple interface, PlayStation's SDK wrapped GPU, SPU, and CD subsystems. The facade didn't limit power - experts could still access low-level APIs - it just made common tasks simple!