Architecting Pokémon Combat: Advanced Data Structures and Patterns in C++
Created byNick Gutierrez
49 views0 downloads

Architecting Pokémon Combat: Advanced Data Structures and Patterns in C++

College/UniversityComputer Science17 days
This university-level project challenges students to architect a professional-grade, turn-based combat engine in C++ within the Unreal Engine environment. By moving away from monolithic "God Objects," students implement advanced software patterns such as Finite State Machines for turn logic and the Observer pattern to decouple gameplay logic from the UI. The curriculum emphasizes data-driven design and scalability, requiring students to utilize polymorphism for extensible move systems and efficient data structures like TMaps for complex type-effectiveness matrices.
Software ArchitectureDesign PatternsUnreal EngineData StructuresFinite State MachinesPolymorphismGame Development
Want to create your own PBL Recipe?Use our AI-powered tools to design engaging project-based learning experiences for your students.
📝

Inquiry Framework

Question Framework

Driving Question

The overarching question that guides the entire project.How can we architect a scalable and decoupled turn-based combat engine in Unreal Engine that utilizes C++ design patterns and data structures to manage the complex logic of a Pokémon-style battle system?

Essential Questions

Supporting questions that break down major concepts.
  • How can architectural patterns like Model-View-Controller (MVC) or Delegate patterns be used to decouple combat logic from the Unreal Engine UI/Visuals?
  • Which data structures (TMaps, TArrays, or Structs) provide the most efficient way to manage a complex 'Type Effectiveness' matrix and a move database?
  • How can a Finite State Machine (FSM) be implemented in C++ to handle the sequential phases of turn-based combat (e.g., Input, Calculation, Animation, Resolution)?
  • How can we utilize polymorphism and inheritance to create a flexible 'Move' system that allows for diverse effects (damage, buffs, status changes) without hard-coding specific logic for every individual move?
  • In what ways does using Data Assets in Unreal Engine improve the scalability of our software architecture compared to hard-coding Pokémon stats directly into C++ classes?
  • How do we manage complex state dependencies—such as priority brackets, speed ties, and held items—using efficient sorting algorithms and conditional logic?

Standards & Learning Goals

Learning Goals

By the end of this project, students will be able to:
  • Implement a robust Finite State Machine (FSM) in C++ to manage discrete turn-based combat phases (Input, Calculation, Animation, Resolution).
  • Architect a decoupled game system using the Model-View-Controller (MVC) or Delegate pattern to separate core combat logic from Unreal Engine's UI and visual components.
  • Develop a scalable data management system utilizing Unreal Engine Data Assets, TMaps, and Structs to handle complex type-effectiveness matrices and move databases.
  • Apply advanced Object-Oriented Programming (OOP) principles, specifically polymorphism and inheritance, to create an extensible 'Move' system that supports diverse gameplay effects without code duplication.
  • Design and execute efficient sorting algorithms to resolve turn order based on complex state dependencies such as speed stats, priority brackets, and held items.

CSTA K-12 Computer Science Standards

3B-AP-12
Primary
Construct solutions to problems using student-created components, such as procedures, modules and/or objects.Reason: The project requires students to design a modular combat engine using C++ classes and objects, adhering to professional software architecture principles.
3B-AP-14
Primary
Compare and contrast the performance of different data structures for a given problem.Reason: Students must evaluate and implement various data structures (TMaps vs. TArrays) to optimize the type-effectiveness matrix and move databases.

ACM/IEEE CS2023 Curricula (Software Development Fundamentals)

SDF-OOD-01
Primary
Design and implement a class hierarchy that includes inheritance and polymorphism.Reason: The core of the Move system relies on inheritance and polymorphism to handle different types of move effects within the combat engine.
SDF-OOD-04
Secondary
Represent and implement a state machine to solve a problem with discrete states.Reason: The project utilizes a Finite State Machine (FSM) as the foundational architecture for managing the turn-based nature of the combat system.

ACM/IEEE CS2023 Curricula (Software Engineering)

SE-ARCH-01
Supporting
Describe the role of architectural patterns in software design, such as MVC or layering.Reason: The inquiry framework specifically asks students to decouple logic from visuals using MVC or Delegate patterns, aligning with high-level software engineering practices.

Entry Events

Events that will be used to introduce the project to students

The 'God Object' Intervention

Students are handed a single, 4,000-line C++ file called 'BattleMaster.cpp' that contains everything from damage math to UI button clicks and sound triggers. Their task is to add a single 'Paralyze' status effect. As they struggle with the massive 'if-else' chains and spaghetti dependencies, they must identify how to 'shatter' the class into smaller, decoupled components like a 'DamageCalculator', a 'StateController', and a 'UIManager'.

The 'Headless' Combat Challenge

Students are given a monolithic Pokémon battle class that is tightly coupled to Unreal Engine’s visual components. They are challenged to extract the core mathematical logic so it can run as a 'headless' (no graphics) C++ console application. This forces the separation of 'Logic' from 'Presentation,' requiring the breakdown of one giant class into reusable, engine-agnostic modules that can be tested independently.

The Refactoring Bounty

Students are presented with a 'Spaghetti Code' version of a Pokémon battle engine where a single class is responsible for 15 different tasks. Each 'sub-system' they successfully extract into a standalone, reusable class or struct (like a 'TypeTable' module or a 'MoveLibrary' handler) earns them 'bounty' points, gamifying the process of identifying and fixing class bloat through refactoring.
📚

Portfolio Activities

Portfolio Activities

These activities progressively build towards your learning goals, with each submission contributing to the student's final portfolio.
Activity 1

Data Architect: Building the Elemental Matrix

Before writing combat logic, students must architect the data layer. In this activity, students will design a scalable 'Type System' using Unreal Engine C++ Structs and TMaps. They will move away from hard-coded 'if-else' checks for damage multipliers and instead create a data-driven lookup table that can handle any number of elemental types (Fire, Water, Grass, etc.).

Steps

Here is some basic scaffolding to help students complete the activity.
1. Define a USTRUCT for 'FTypeEffectiveness' that contains a TMap of EElementalType to a float (the multiplier).
2. Create a Primary Data Asset class in C++ to store Pokémon base stats (HP, Attack, Defense, etc.) and their types.
3. Implement a static 'DamageLibrary' class with a function that takes an attacker type and defender type, performing a TMap lookup to return the effectiveness multiplier.
4. Populate a Data Asset in the Unreal Editor with at least 5 types and 3 Pokémon to test the lookup efficiency.

Final Product

What students will submit as the final product of the activityA 'TypeChart' Data Asset and a 'PokémonBase' Data Asset that allow designers to define stats and elemental weaknesses without touching code.

Alignment

How this activity aligns with the learning objectives & standardsAligns with CSTA 3B-AP-14 (Compare and contrast the performance of different data structures) and 3B-AP-12 (Construct solutions using student-created components). It specifically addresses the inquiry framework question regarding using TMaps and Structs for a Type Effectiveness matrix.
Activity 2

Move Polymorphism: The Action Factory

Students will create a flexible 'Move' system using C++ inheritance. Instead of one giant 'Move' class with a switch statement for every effect, students will create a base 'BattleMove' class and extend it into specialized subclasses. This allows 'Thunder Bolt' (Damage) and 'Tail Whip' (Stat Debuff) to share a common interface while executing different internal logic.

Steps

Here is some basic scaffolding to help students complete the activity.
1. Create a base C++ class 'UBattleMove' with a virtual 'ExecuteMove' function that takes a reference to the Attacker and Defender.
2. Implement 'UDamageMove' which overrides ExecuteMove to apply health reduction formulas.
3. Implement 'UStatusMove' which overrides ExecuteMove to apply a 'State' (like Burn or Paralyze) to the target.
4. Refactor the code to use a 'MoveEffect' array within the base class, allowing a single move to have multiple polymorphic effects (e.g., damage + a 10% burn chance).

Final Product

What students will submit as the final product of the activityAn extensible Move Hierarchy consisting of a base class and at least three functional subclasses (DamageMove, StatusMove, and BuffMove).

Alignment

How this activity aligns with the learning objectives & standardsAligns with ACM/IEEE SDF-OOD-01 (Design and implement a class hierarchy that includes inheritance and polymorphism). This activity directly tackles the 'Move' system essential question by using OOP to avoid code duplication.
Activity 3

The Battle Conductor: State Machine Logic

Turn-based games are driven by states. In this activity, students will implement a Finite State Machine (FSM) to manage the flow of a battle. This prevents logic errors like players attacking during an animation or moves being processed before input is received. Students will define states such as Selection, Action, Calculation, and Resolution.

Steps

Here is some basic scaffolding to help students complete the activity.
1. Define an Enum 'EBattleState' representing the various phases of a Pokémon turn.
2. Create a 'BattleController' class that holds the current state and a 'TransitionToState' function.
3. Use a Switch/Case structure or the State Pattern to handle 'Tick' logic or 'OnEnter' logic for each state (e.g., the Selection state waits for UI input).
4. Implement a 'TurnSequence' loop that automatically moves from Calculation to Resolution once move logic is complete.

Final Product

What students will submit as the final product of the activityA C++ BattleStateMachine component that transitions between combat phases and logs state entries/exits to the Unreal Output Log.

Alignment

How this activity aligns with the learning objectives & standardsAligns with ACM/IEEE SDF-OOD-04 (Represent and implement a state machine to solve a problem with discrete states). It provides the core 'brain' for the turn-based nature of the project.
Activity 4

Headless Hero: Decoupling Logic with Delegates

To ensure the combat engine is 'scalable' and 'headless,' students will use the Delegate pattern (Observer Pattern) to decouple the logic from the UI. The core C++ combat classes should never 'know' about a Progress Bar or a Button; instead, they will broadcast events that the UI 'listens' for.

Steps

Here is some basic scaffolding to help students complete the activity.
1. Declare Dynamic Multicast Delegates in C++ for events like 'OnHealthChanged', 'OnMoveExecuted', and 'OnBattleStateChanged'.
2. Remove all direct UI references (headers) from your core BattleLogic classes.
3. In the UI (UMG/Blueprints), 'Bind' events to these C++ delegates to update health bars and combat text.
4. Test the system by running a 'Battle Script' that executes a full turn and verify the UI updates correctly without the logic class knowing the UI exists.

Final Product

What students will submit as the final product of the activityA 'Headless' combat simulation where the game logic runs entirely in the background, communicating with the UI via Broadcast Delegates.

Alignment

How this activity aligns with the learning objectives & standardsAligns with ACM/IEEE SE-ARCH-01 (Describe the role of architectural patterns like MVC) and CSTA 3B-AP-12. This activity ensures the game logic is independent of the 'View' (Unreal's visuals).
Activity 5

The Priority Sorter: Resolving the Turn Order

In the final architectural piece, students must handle turn order. Pokémon combat uses 'Priority Brackets' and 'Speed Stats' to determine who moves first. Students will implement a custom sorting algorithm using TArrays to organize 'Action' objects before they are executed by the State Machine.

Steps

Here is some basic scaffolding to help students complete the activity.
1. Create an 'FMoveAction' struct that stores the move to be used, the user's speed, and the move's priority level.
2. Use 'TArray::Sort' with a custom Lambda predicate to sort actions first by Priority, and then by Speed.
3. Add 'Speed Tie' logic—if both priority and speed are equal, use a random number generator to decide the winner.
4. Integrate this 'Action Queue' into the Battle State Machine so that the Action state processes the queue in the sorted order.

Final Product

What students will submit as the final product of the activityA Turn-Sequencer module that correctly sorts moves based on priority, speed, and status effects (like Trick Room or Paralysis).

Alignment

How this activity aligns with the learning objectives & standardsAligns with CSTA 3B-AP-14 (Performance of data structures) and 3B-AP-12. It focuses on the specific learning goal of managing complex state dependencies and sorting.
🏆

Rubric & Reflection

Portfolio Rubric

Grading criteria for assessing the overall project portfolio

Pokémon Combat Engine: Advanced C++ Architecture Rubric

Category 1

Software Architecture & Logic Flow

Evaluates the high-level structural integrity and modularity of the software solution.
Criterion 1

Decoupling and Architectural Patterns (MVC)

Evaluation of the student's ability to separate game logic from the user interface using the Delegate/Observer pattern.

Exemplary
4 Points

The combat engine is completely 'headless' with zero dependencies on UI classes. Utilizes Dynamic Multicast Delegates effectively to broadcast state changes. The UI only 'listens' and never drives the logic. Implementation shows advanced understanding of event-driven architecture.

Proficient
3 Points

Successfully decouples core logic from the UI using delegates. Most communication between the BattleController and the UI happens via events. There are no circular dependencies, though some UI-specific data might bleed into the logic layer.

Developing
2 Points

Shows emerging understanding of decoupling. While some delegates are used, the core logic still maintains direct references to some UI elements or requires the UI to exist to function properly. Dependency management is inconsistent.

Beginning
1 Points

Logic and UI are tightly coupled. The combat engine relies on direct calls to UI widgets or hard-coded UI references. Changes to the UI would require significant rewrites of the combat logic.

Criterion 2

State Management (Finite State Machine)

Assessment of the student's implementation of a Finite State Machine (FSM) to manage turn-based flow.

Exemplary
4 Points

Implements a robust, error-proof FSM using a state pattern or highly organized switch/case logic. Transitions are clean, and illegal actions (e.g., attacking during animation) are architecturally impossible. Handles complex transitions like mid-turn faints.

Proficient
3 Points

Implements a functional State Machine that accurately tracks turn phases (Selection, Action, Resolution). Transitions are predictable and log correctly to the console. Most edge cases are handled.

Developing
2 Points

The system uses a basic state variable to track turns, but logic is prone to 'state leakage' where actions from one state can occur in another. Transitions are manually handled and somewhat brittle.

Beginning
1 Points

Turn logic is managed via scattered boolean flags and if-else chains. There is no clear centralized controller for the game state, leading to frequent logic bugs or out-of-order execution.

Category 2

Data Modeling & Algorithmic Efficiency

Focuses on the efficiency, performance, and organization of data and algorithms.
Criterion 1

Data Structure Selection & Scalability

Evaluation of the use of C++ Structs, TMaps, and Unreal Data Assets for scalable game data.

Exemplary
4 Points

Masterful use of Data Assets and TMaps (O(1) lookup). The Type Effectiveness matrix is handled via a nested TMap or efficient lookup table. Designers can add new Pokémon or Types without changing a single line of C++ code.

Proficient
3 Points

Effective use of Data Assets and Structs. Type effectiveness and stats are stored externally from the logic classes. The system is scalable, though lookup logic could be slightly more optimized.

Developing
2 Points

Data is partially externalized using Structs, but some stats or type interactions remain hard-coded. Use of TMaps is present but perhaps redundant or inefficiently structured.

Beginning
1 Points

Stats and type relationships are hard-coded into C++ classes. The system is not scalable; adding a new Pokémon type would require significant manual code updates and recompilation.

Criterion 2

Algorithmic Logic (Sorting & Turn Order)

Assessment of turn-order resolution using sorting algorithms and priority logic.

Exemplary
4 Points

Implements a sophisticated TArray sort using a custom Lambda predicate. Correctly resolves priority brackets first, then speed stats, and finally handles 'speed ties' using an RNG seed for fairness. The implementation is clean and highly efficient.

Proficient
3 Points

Correctly sorts the turn order based on priority and speed. Uses TArray::Sort or a similar algorithm effectively. Handles the majority of turn-order scenarios accurately.

Developing
2 Points

Basic sorting is implemented, but the logic may fail to account for priority brackets or only checks speed. Speed ties are not handled or result in predictable/static outcomes.

Beginning
1 Points

Turn order is determined by the order of player input or simple index iteration. No sorting algorithm is present to handle priority or speed dependencies.

Category 3

Object-Oriented Design (OOD)

Measures the depth of Object-Oriented Programming principles applied to the game systems.
Criterion 1

Inheritance & Polymorphism Implementation

Evaluation of the 'Move' system using C++ inheritance and polymorphism.

Exemplary
4 Points

Implements an elegant class hierarchy with a pure virtual base class. Move effects (Damage, Status, Buffs) are modular and can be stacked in a single move instance via a polymorphic array. Extremely easy to extend with new move types.

Proficient
3 Points

Successfully uses inheritance to create different Move types (e.g., DamageMove vs StatusMove). Overridden functions are used correctly to implement specialized behavior without code duplication in the base class.

Developing
2 Points

Uses inheritance, but some logic is still duplicated across subclasses. The base class might contain too much 'knowledge' of its children, or polymorphism is used only for simple damage variations.

Beginning
1 Points

The move system relies on a single class with a large 'switch' statement or nested 'if' blocks to determine move effects. No meaningful use of inheritance or polymorphism is present.

Reflection Prompts

End-of-project reflection questions to get students to think about their learning
Question 1

Reflecting on your implementation of the Delegate pattern, how did decoupling the combat logic from the Unreal UI (UMG) change your debugging process compared to a traditional, tightly coupled approach?

Text
Required
Question 2

How confident do you feel in your ability to choose and implement a specific C++ design pattern (such as a Finite State Machine or Observer/Delegate pattern) to solve complex gameplay logic problems in future projects?

Scale
Required
Question 3

Which architectural component of your Pokémon combat engine presented the greatest technical challenge to implement while adhering to the principle of 'Separation of Concerns'?

Multiple choice
Required
Options
Implementing Polymorphism for the Move Hierarchy
Managing complex transitions within the Finite State Machine (FSM)
Decoupling logic from presentation via Dynamic Multicast Delegates
Architecting the Type Effectiveness matrix using TMaps and Data Assets
Question 4

Discuss the trade-offs you encountered when using TMaps and Data Assets for the 'Type Effectiveness' matrix. In what ways does this data-driven approach improve or hinder the scalability of the game compared to hard-coding?

Text
Required
Question 5

To what extent did the initial 'God Object' intervention activity influence your architectural decisions when you began refactoring your code into modular components?

Scale
Required