
Dungeon Crawler
This is an overview of the Dungeon Crawler we have worked on throughout the semester.
We are Team #18 consisted of college students at Georgia Tech taking a class called CS2340, Objects and Design. Throughout the semester, we have been creating and implementing a dungeon crawler game with many fun aspects.
Features include customization of player name, player look, and game difficulty. Dungeon Crawler has three distinct levels each with its own enemies, powerups, and aesthetics. Dive into our project to learn more about us, our design structure, algorithms, and a fun demo!





Our Team
-
Sydney Oden - Frontend and Design:
-
3rd year Computational Media major
-
Created concept art for UI, original pixel art for characters and levels, pattern diagrams, and the website
-
-
Julia Raver - Backend:
-
3rd year Computer Science major
-
Set up GitHub, created pattern diagrams, implemented multiple aspects of gameplay
-
-
Surya Subramanian - Backend:
-
2nd year Computer Science major
-
Implemented backend functionalities for config screen, endgame screen, and HP + Score Logic
-
-
Mehul Rao - Backend:
-
2nd year Computer Science major
-
Implemented multiple aspects of gameplay
-
-
Ryan Lundqvist - Backend:
-
3rd year Computer Science major
-
Worked on building out logic for player/enemy movement and movement strategies, leaderboard functionalities, powerups, and attacks
-
Design Patterns
Singleton Structure:
Our leaderboard system follows the singleton design pattern to ensure that there is only one instance of the leaderboard that gets created and that there is one global way to access it. Our implementation involved a private constructor to prevent other classes from creating new instances, followed by a public static method to provide access to the instance. If that instance does not exist, it is created and returned. To guarantee that only one instance of a leaderboard exists, the singleton is maintained using a private static instance variable in the Leaderboard class.
MVVM Structure:
In Sprint 2, our team implemented the MVVM architecture by maintaining distinct Model, View, and ViewModel components. Our Models, Player and GameConfiguration, held the relevant data for the player, such as HP, name, and difficulty. These classes also contain getters and setters associated with the player. We made sure that whenever we were accessing this data, it was accessed from these Model classes. The ViewModel classes house the core logic and allow communication between the corresponding Model and View components. For example, the method to update the player’s score is in the GameViewModel file. The View consists of our XML layout files and Activity classes, which control the user interface and respond to interactions. EndScreenActivity, for example, displays a button on the screen that the user can click to restart the game and return to the start screen.
Strategy Pattern:
In our game's code, we've effectively put into practice the Strategy design pattern to manage various movement behaviors of the player. Notably, we use the Strategy design pattern to allow us to use multiple varied strategies for movement that can be swapped in and out, such as strategies for running and walking. This will also be extended to allow for varied movement in other scenarios (such as muddy terrain slowing the player or wind making it so that the player struggles to walk one direction). By using the Strategy design pattern we can seamlessly switch between these. By encapsulating each movement strategy withing seperate strategy classes in our strategy folder, all implementing the MovementStrategy interface, we've put into place the highly modular Strategy design pattern, where we have a collection of varied algorithms and our client can choose to put any one into place depending on the scenario.
Observer Pattern:
In our game's code, we've effectively put into practice the Observer design pattern to cleanly separate movement logic from rendering, as done in GameScreenActivity (our Game Screen). The Player class acts as the subject (observable piece), maintaining a list of observers and notifying them of state changes. The GameScreenActivity class serves as an observer, registering itself to listen for updates to the player's state. This pattern allows the game's UI and logic to be able to respond/react to changes in the player's score, position, and level. When the player's state changes, the update method in the GameScreenActivity class can be called. This use of the Observer design pattern makes it easier to add new observers and features without tying those things to the player's logic. In our implementation of the Observer Design Pattern, we ensure that all enemies are updated with the player's position whenever the player makes a movement. This is accomplished by notifying each enemy object when the player moves and providing them with the updated player's position. The key component of the pattern is the checkCollisionWithEnemies method, which monitors for direct contact between the player and enemies using their respective rectangles. Upon a collision, the player's HP is reduced, and the damage inflicted depends on the selected difficulty level. This design pattern allows for a decoupled and efficient way of notifying enemies about player movements.
Factory Design Pattern:
The Factory Design Pattern has been implemented in our code for dealing with various enemy types and allowing our subclasses to select which type of enemy should ultimately be created by the “EnemyFactory.” This follows the principles of the Factory Design Pattern and allows modular flexibility in selecting the current implementation of an enemy. The enemies possess three attributes each, always differing in at least two (health, sprite, speed). Each specific implementation of an enemy can be created by the EnemyFactory, and all enemy types extend the abstract Enemy class.
Decorator Design Pattern:
Our code effectively utilizes the Decorator design pattern to enhance the behavior of a player in a game with different power-ups. The PowerUp abstract class serves as the common interface for all power-ups, with three concrete classes (Potion, Boots, and Sword) representing specific types of enhancements. Each concrete power-up extends the PowerUp class and overrides the methods for getSpeed(), getHealth(), and attack() to introduce distinct functionalities. By adopting the Decorator pattern, these power-ups can be dynamically combined and wrapped around a basic player object, allowing the player to accumulate multiple enhancements seamlessly. The pattern also maintains flexibility, as new power-ups can be added without modifying existing code, and it follows the open/closed principle by enabling extension without altering the core player class.
