Welcome to the 2D UI-based Memory Game project! This project is a simple yet engaging memory game developed using C# and the Unity engine. This ReadMe will guide you through the project's purpose, technologies used, and the software architecture and design patterns implemented.
- Clone this repository.
- Download Unity 2021.3.44f1 into your computer.
- Open the project in Unity.
- Switch to Android platform.
- Open the SampleScene and run it to start playing!
- [OPTIONAL] If you want to see the data changes in the database in realtime and/or modify said data straight in the database too, get permission from this repository's owner, Renelie Salazar. Anyway, the database link is: https://treat-memory-game-3a13b-default-rtdb.asia-southeast1.firebasedatabase.app/
The 2D Memory Game challenges players to match pairs of cards as quickly as possible. The game is designed with a scalable and maintainable architecture, ensuring smooth integration of new features and efficient collaboration among developers.
- Firebase Integration: Saves and loads players' high scores to the cloud.
- Crisp, Responsive and Animated UI: Leveraging TextMeshPro for sharp text and DOTween for runtime animations.
- Reactive and Async Programming: Simplified using UniRx.
- Dependency Injection: Achieved with Zenject for better modularity.
The project follows a reactive MVP architecture, which emphasizes scalability, maintainability, and loose coupling between components:
The Model serves as the data manager and the core of the application. It uses Interfaces to hide its concrete implementation (e.g., ScriptableObject, MonoBehaviour, or a regular class). Like a cashier in real life — you, as a store customer, only interact with the cashier to handle transactions without worrying about their personal details like age, address and civil status.
Model Interfaces are divided into two types:
- Readers: For accessing (reading) data only.
- Writers: For both reading and modifying data.
Responsible for handling view or UI-related logic and interactions. Operates independently, using Readers to fetch data it needs to display and coldly reacts to any data changes when signal regarding the update is received.
Acts as mini "brains" of the system, orchestrating higher-level logic than Views. Uses Writers to interact with and update the Model, and may manage Views when necessary.
- NOTE: In my architecture, when a Presenter makes use of 5 or more Model Writers, I promote the class by calling it a Manager instead of a Presenter. This is my way of signaling to all team members (at first glance of the script name) that the class is very important as it handles a LOT of logic.
- Object Pooling: for the CardViews on the grid
- Bridge: separates interfaces from their implementation
- Private Class Data: restricts accessor and mutator access to Models
- Observer: users of Readers react coldly to changes to Model data that they're subscribed to
- Separation of Concerns: Each component (Model, View, Presenter) has a clearly defined role.
- Loose Coupling: Models, Views, and Presenters are designed to function independently, ensuring that changes in one component don't cascade to others.
- Scalability: Adding more features, especially brand new ones separate from what's in the project already, is seamless — no need to modify existing components.
- Maintainability: Clear interface definitions make it easier for developers to understand and extend functionality.
- Mockability / debuggability of scripts
- By isolating logic in Models, Views, and Presenters, debugging is more straightforward.
- Dependency injection via Zenject allows for effortless testing and swapping of components, fostering adaptability to future requirements.
- Heavy use of Interfaces: leads to boilerplate. Can be tedious to work with.
- Scripts are loosely coupled: May lead to the project having lots of scripts.
- Not everyone appreciates and/or knows how to use Dependency inversion / injection. Instead, most coders just go the easy way around with Singletons.
The following plugins and libraries are used:
- Firebase Database: For cloud data storage.
- Zenject: For dependency injection.
- UniRx: For reactive programming.
- TextMeshPro: For enhanced text rendering.
- DOTween (HOTWeen): For runtime animations.
Contributions are welcome! Please fork this repository, create a feature branch, and submit a pull request.