SkillAgentSearch skills...

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/UnityDesignPatternsReference

README

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.

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.

</details> <details> <summary><b>🔊 Chain of Responsibility</b></summary>

Chain of Responsibility

Delegates commands to a chain of processing objects.

Diagram

</details> <details> <summary><b>🔊 Command</b></summary>

Command

Creates objects that encapsulate actions and parameters.

Diagram

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.

Diagram

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.

</details> <details> <summary><b>🔊 Iterator</b></summary>

Iterator

Accesses the elements of an object sequentially without exposing its underlying representation.

Diagram

</details> <details> <summary><b>🔊 Mediator</b></summary>

Mediator

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

![Diagram](https://github.com/JoanStinson/RetroRPGPatterns/blob/main/Diagra

View on GitHub
GitHub Stars21
CategoryDesign
Updated1y ago
Forks6

Languages

C#

Security Score

80/100

Audited on Feb 20, 2025

No findings