Personal Project
The Duel Masters Trading Card Game is a two-player or two vs. two team collectible card game (CCG) jointly developed by Wizards of the Coast and Takara Tomy (itself an affiliate of Hasbro, which owns WotC). The card game is part of the Duel Masters franchise.
For me, the significance of this game transcends mere entertainment. It served as the catalyst for forging enduring bonds with my closest circle of friends during our formative school years, fostering friendships that have withstood the test of time. Thus, when contemplating my inaugural personal project, I felt compelled to pay homage to this cherished aspect of my past.
Much akin to the digital adaptation of Magic: The Gathering through MTG: Arena, my endeavor aims to translate the essence of Duel Masters into the digital realm. Through this undertaking, I seek to capture the essence and nostalgia of the original card game while introducing it to a new generation of enthusiasts, ensuring its legacy endures in the digital era.
The building blocks of a TCG are its cards, and a TCG is only as good as the cards which make it. Consequently, it was only fitting that this is where I initiated the implementation of my project.
Before diving into the card structure, I got the art for all the Duelmaster cards from Pranjal Bisht. Next, in Krita, I picked cards I liked, mainly considering their resolution. I edited them to make frames for the cards, making it easy to overlay them onto the actual card art in my program.
I aimed for the card art only, excluding any text, to create a modular system for setting up each card. This involves utilizing ScriptableObjects with the relevant data embedded within them, upon which I expand further below.
The card functions as a canvas in the world space, positioned above a scaled-down cube. I wanted a 2.5D design to add some depth to my project aesthetic, influencing my choice of this approach. The hierarchy begins with the 'Artwork Image,' representing the card image sourced from the database. Above it is the corresponding 'Frame Image' based on the card type. Additionally, there are UI Text objects displaying essential card information. The Creature Layout includes extra text specific to creature cards.
The Mana and Battle Cards serve as simplified renditions of the Card Layouts, specifically tailored for the Mana and Battle Zones, respectively.
The final card representations seamlessly integrate all distinct card layouts into a cohesive whole. This setup allows for effortless editing of individual layouts as needed, eliminating concerns about unintentionally disrupting other functionalities. The incorporation of prefab variants serves to underscore and enhance this design choice. Hence, this method establishes a decoupled prefab configuration following the component pattern, fostering an optimized modular structure and a more streamlined workflow.
The keyword prefabs further emphasise the modular nested prefab structure for the card representations.
My original implementation of this section of the project was much more straightforward, essentially comprising a few monolithic classes with large sets of member variables to store every type of information that may be required, ever.
However, after setting that up, I found myself unsatisfied with the architecture of my project. As a self-taught solo developer, I struggled for a decent amount of time, wondering how I could make the entire system and the processes within it cleaner.
That is when I came across, in my research, a YouTube video titled "Clean Code - Uncle Bob." I watched the lecture in one sitting, and although by the end my brain was swimming with all the information, my heart felt reinvigorated. Robert Cecil Martin had introduced me to the world of the SOLID principles and the beauty of clean code architectures.
And off I went into a huge refactoring of my entire Card Data pipeline, which broke a bunch of code in my actual gameplay processing systems that I have not yet gotten around to cleaning. Someday I hope to get back to this project in earnest, but until then, the tools I picked up through this refactoring attempt will keep helping me in making systems better, and dare I say, more beautiful.
The 'CardData' is a ScriptableObject and the basis of the entire card serialization pipeline. Apart from the members that store the information regarding the card itself, the most important element of this class is the list of 'EffectData' objects, which store the data specific to each effect that a card may have.
The EffectData itself is a simple class with two Booleans that further define the way an effect might work. The core functionality is described within the 'EffectCondition' and 'EffectFunctionality' classes.
The 'EffectCondition' class is responsible for serializing any conditions that need to be validated before the relevant effect takes place.
The 'EffectFunctionality' class is responsible for the serializing of the pertinent data regarding the working of the effect itself.
Then for both of these classes, the 'ConnectorType' enum denotes how the effect connects (either "and" or "or") to any sub-conditions or sub-functionalities the effect may have.
The '_subCondition' and '_subFunctionality' variables, along with the connector described above, allow for a nested effect structure designed to enable the structuring of complex effect behaviors using the same basic building blocks.
Hence, this approach enables a flexible and highly modular setup that is easy to extend. The hierarchical setup also means that classes at the top do not need to worry about the details regarding the classes at the bottom. Adding any new Condition or Functionality to the system simply requires adding the type to the appropriate enum and establishing a new class inheriting from the relevant abstract parent. There is no need to modify any code elsewhere.
One could possess the best-designed architecture for a serialization pipeline in the world, but all of that becomes irrelevant if entering the data is cumbersome. As such, the next logical progression for me was to establish a custom editor, enabling the setup of card effects through an intuitive interface.
To showcase this, I will use the following two cards as examples: Aqua Hulcus, which is a Water Creature, and Aura Blast, a Nature Spell.
The interface for setting up this effect is as follows,
And this is how it looks once you're finished editing,
The interface for setting up this effect is as follows,
And this is how it looks once you're finished editing,
While I was proud of the serialization pipeline and the editor I had constructed at the time, I now recognize that the process of setting up effects is not as intuitive as I had hoped. If given the opportunity to implement it again, I would leverage all the knowledge I've gained from this project and my subsequent work to create a system that is not only more decoupled and extensible in scope, but also features a cleaner and more intuitive editing interface.
There are two main types of manager classes pertinent to the logical processing of the system: the Game State Managers and the Zone Managers. Additionally, there is a PlayerManager that does not fit within either of these categories.
The Game State Managers, as implied by their name, are responsible for processing the central game states.
The ZoneManagers comprise classes such as the BattleZoneManager, DeckManager, HandManager, etc., which are responsible for the behaviors pertaining to their respective zones. They assume complete ownership of the zones under their jurisdiction. For instance, if a card effect involves searching for a card in the deck and then shuffling the deck, the CardEffectsManager will retrieve the card from the DeckManager's list of managed cards, prompt the relevant PlayerManager to transfer the card to its corresponding HandManager for processing its addition to the hand, and subsequently, the shuffling logic will be executed in the DeckManager.
The PlayerManager holds references to each of the ZoneManagers associated with its respective player. It is also responsible for delegating the movements of cards to and from each of the zones.