UnityDesignPatternsReference
A tiny retro action RPG implementation made applying Software Design Patterns to serve as a guide of reusable solutions that can be applied to common problems.
Install / Use
/learn @JoanStinson/UnityDesignPatternsReferenceREADME
Retro RPG Patterns
A tiny retro action RPG implementation made applying Software Design Patterns to serve as a guide of reusable solutions that can be applied to common problems.
<p align="center"> <a> <img alt="Made With Unity" src="https://img.shields.io/badge/made%20with-Unity-57b9d3.svg?logo=Unity"> </a> <a> <img alt="License" src="https://img.shields.io/github/license/JoanStinson/RetroRPGPatterns?logo=github"> </a> <a> <img alt="Last Commit" src="https://img.shields.io/github/last-commit/JoanStinson/RetroRPGPatterns?logo=Mapbox&color=orange"> </a> <a> <img alt="Repo Size" src="https://img.shields.io/github/repo-size/JoanStinson/RetroRPGPatterns?logo=VirtualBox"> </a> <a> <img alt="Downloads" src="https://img.shields.io/github/downloads/JoanStinson/RetroRPGPatterns/total?color=brightgreen"> </a> <a> <img alt="Last Release" src="https://img.shields.io/github/v/release/JoanStinson/RetroRPGPatterns?include_prereleases&logo=Dropbox&color=yellow"> </a> </p>- 🔊 Behavioral Patterns
- Define a concrete communication scheme between objects.
- 🐣 Creational Patterns
- Create objects, rather than instantiating them directly.
- ✂️ Decoupling Patterns
- Split dependencies to ensure that changing a piece of code doesn't require changing another one.
- 🛠️ Optimization Patterns
- Speed up the game by pushing the hardware to the furthest.
- ⏰ Sequencing Patterns
- Invent time and craft the gears that drive the game's great clock.
- 🧬 Structural Patterns
- Use inheritance to compose interfaces and define ways to compose objects to obtain new functionality.
🔊 Behavioral Patterns
Define a concrete communication scheme between objects.
<details> <summary><b>🔊 Bytecode</b></summary>Bytecode
Give a behavior the flexibility of data by encoding it as instructions for a virtual machine.
</details> <details> <summary><b>🔊 Chain of Responsibility</b></summary>Unity has this pattern already built-in in its own Visual Scripting System (previously named 'Bolt') and in its Shader Graph System. Unreal has this pattern already built-in too in its Blueprint Visual Scripting System.
Chain of Responsibility
Delegates commands to a chain of processing objects.

Command
Creates objects that encapsulate actions and parameters.

public class InputHandler : MonoBehaviour
{
private Invoker _invoker;
private BikeController _bikeController;
private Command _turnLeftCommand;
private Command _turnRightCommand;
private Command _toggleTurboCommand;
private bool _isReplaying;
private bool _isRecording;
private void Awake()
{
_invoker = gameObject.AddComponent<Invoker>();
_bikeController = FindObjectOfType<BikeController>();
_turnLeftCommand = new TurnLeft(_bikeController);
_turnRightCommand = new TurnRight(_bikeController);
_toggleTurboCommand = new ToggleTurbo(_bikeController);
}
private void Update()
{
if (!_isReplaying && _isRecording)
{
if (Input.GetKeyUp(KeyCode.A))
{
_invoker.ExecuteCommand(_turnLeftCommand);
}
if (Input.GetKeyUp(KeyCode.D))
{
_invoker.ExecuteCommand(_turnRightCommand);
}
if (Input.GetKeyUp(KeyCode.W))
{
_invoker.ExecuteCommand(_toggleTurboCommand);
}
}
}
private void OnGUI()
{
if (GUILayout.Button("Start Recording"))
{
_bikeController.ResetPosition();
_isReplaying = false;
_isRecording = true;
_invoker.Record();
}
if (GUILayout.Button("Stop Recording"))
{
_bikeController.ResetPosition();
_isRecording = false;
}
if (!_isRecording && GUILayout.Button("Start Replay"))
{
_bikeController.ResetPosition();
_isRecording = false;
_isReplaying = true;
_invoker.Replay();
}
}
}
public class BikeController : MonoBehaviour
{
public enum Direction
{
Left = -1,
Right = 1
}
private bool _isTurboOn;
private const float _distance = 1f;
public void ToggleTurbo()
{
_isTurboOn = !_isTurboOn;
}
public void Turn(Direction direction)
{
if (direction == Direction.Left)
{
transform.Translate(Vector3.left * _distance);
}
else if (direction == Direction.Right)
{
transform.Translate(Vector3.right * _distance);
}
}
public void ResetPosition()
{
transform.position = Vector3.zero;
}
}
public class Invoker : MonoBehaviour
{
private SortedList<float, Command> _recordedCommands = new SortedList<float, Command>();
private bool _isRecording;
private bool _isReplaying;
private float _replayTime;
private float _recordingTime;
public void ExecuteCommand(Command command)
{
command.Execute();
if (_isRecording)
{
_recordedCommands.Add(_recordingTime, command);
}
Debug.Log("Recorded Time: " + _recordingTime);
Debug.Log("Recorded Command: " + command);
}
public void Record()
{
_recordingTime = 0.0f;
_isRecording = true;
}
public void Replay()
{
_replayTime = 0.0f;
_isReplaying = true;
if (_recordedCommands.Count <= 0)
{
Debug.LogError("No commands to replay!");
}
_recordedCommands.Reverse();
}
private void FixedUpdate()
{
if (_isRecording)
{
_recordingTime += Time.fixedDeltaTime;
}
if (_isReplaying)
{
_replayTime += Time.fixedDeltaTime;
if (_recordedCommands.Any())
{
if (Mathf.Approximately(_replayTime, _recordedCommands.Keys[0]))
{
Debug.Log("Replay Time: " + _replayTime);
Debug.Log("Replay Command: " + _recordedCommands.Values[0]);
_recordedCommands.Values[0].Execute();
_recordedCommands.RemoveAt(0);
}
}
else
{
_isReplaying = false;
}
}
}
}
public abstract class Command
{
public abstract void Execute();
}
public class TurnLeft : Command
{
private readonly BikeController _controller;
public TurnLeft(BikeController controller)
{
_controller = controller;
}
public override void Execute()
{
_controller.Turn(BikeController.Direction.Left);
}
}
public class TurnRight : Command
{
private readonly BikeController _controller;
public TurnRight(BikeController controller)
{
_controller = controller;
}
public override void Execute()
{
_controller.Turn(BikeController.Direction.Right);
}
}
public class ToggleTurbo : Command
{
private readonly BikeController _controller;
public ToggleTurbo(BikeController controller)
{
_controller = controller;
}
public override void Execute()
{
_controller.ToggleTurbo();
}
}
</details>
<details>
<summary><b>🔊 Interpreter</b></summary>
Interpreter
Implements a specialized language.

</details> <details> <summary><b>🔊 Iterator</b></summary>Similar to the Bytecode pattern, Unity has this pattern already built-in in its own Visual Scripting System (previously named 'Bolt') and in its Shader Graph System. Unreal has this pattern already built-in too in its Blueprint Visual Scripting System.
Iterator
Accesses the elements of an object sequentially without exposing its underlying representation.

Mediator
Allows loose coupling between classes by being the only class that has detailed knowledge of their methods.
