Our Discord community is buzzing. Come and join us!

Reimagining Pong Wars with C# and MVUX

Pong Wars has captured developer attention for years. I’m unclear why; On the surface, it’s a basic game. Yet there’s something about the core mechanic and the competition between two elements that keeps inspiring new variations. Maybe it’s the elegance of its basic design or the potential for surprisingly deep gameplay to emerge from such a simple concept.  

Beyond its entertainment value, Pong Wars has traditionally been a fantastic learning project for developers. When I came across Rosskarchner’s version and the list of alternatives, it sparked my interest in building my own cross-platform version of Pong Wars.

For this project, I opted to use C# and Uno Platform. This wasn’t just brand loyalty; Together, they offered a robust development environment, promising better performance and scalability – critical ingredients for a fast-paced game like Pong.

However, the real game-changer (pun intended!) was leveraging MVUX for state management—a pattern that enforces a one-way flow of data and uses immutable records to ensure data integrity. For the visual layer, C# was my Markup of choice. It simplified UI development and allowed me to focus on the core gameplay mechanics. And as a final touch, I wrapped all that in a nice Material theme.

Game Architecture and Class Breakdown

Visual Representation (GamePage.cs):

The GamePage class serves as the architect of the game’s visual layout and user interaction. It orchestrates the layout, binds elements to the game’s core logic, and handles color themes and player input.

Building the board: ItemsRepeater and UI Elements

Building the Pong Wars game board involved two key UI components: ItemsRepeater and UniformGridLayout.  This combination effectively manages the cell structure, the foundation of the game. 

					new ItemsRepeater()
    .ItemsSource(() => vm.Cells)
        new UniformGridLayout()
    .ItemTemplate<Cell>(cell => CellTemplate(cell));

private Grid CellTemplate(Cell cell)
    => new Grid()
            new Rectangle()
                .Fill(() => cell, cell => new SolidColorBrush(CellColor(cell))),
            new Ellipse()
                .Fill(() => cell, cell => new SolidColorBrush(PlayerColor(cell)))
                .Visibility(() => cell, cell => cell.HasBall ? Visibility.Visible : Visibility.Collapsed));

ItemsRepeater facilitates the efficient creation and management of repetitive UI elements, perfectly suited for representing the cells on the board. UniformGridLayout, on the other hand, ensures a consistent and predictable cell arrangement, mimicking the typical grid layout of Pong.  By leveraging these components together, binding individual cells to their respective positions and updating the ball positions based on gameplay logic becomes a streamlined process and the focus shifts towards updating cell data and ball positions within the predefined structure, resulting in cleaner code and easier maintainability.

Beyond the board, the UI uses a TextBlock for displaying the score (calculated directly in the GameModel) and a Slider bound to the game loop for controlling ball movement speed.

Binding the View to the Model: Dynamic Updates

The GamePage class fosters a dynamic connection between the visual elements and the underlying game logic through data binding. By binding UI elements to properties within the GameModel, the class ensures that the game’s state is reflected visually. Whenever the game state changes (e.g., score updates, ball moves), the bound UI elements automatically update.

Color Schemes and Event Handlers

The GamePage class doesn’t just define the layout; it also sets the visual tone and implements color schemes for the game’s appearance. The color scheme is customized using our own colors with ColorPaletteOverride, streamlining the customization of default Material colors. This approach centralizes color definitions in a single file, eliminating the need for extensive styling; we solely define colors for the rectangles and balls on the board.

In essence, the GamePage class defines the layout, binds the visuals to the game’s core, and handles color themes and user interactions.

Uno Pong Wars
Check out the sample and dive deeper into the code.

Managing State and Behavior (GameModel.cs):

Thanks to MVUX (Model-View-Update eXtended), the GameModel of the app can be very simple. It acts as the heart of the game’s state management and is responsible for crucial elements such as the position of cells, the current score, and the speed of gameplay, which then automagically updates based on user interactions and game logic.

When the game runs, the Game property with the whole logic is created to seamlessly integrate with MVUX’s state management mechanisms. Through this integration, GameModel efficiently accesses data from the underlying Game logic, dynamically retrieving information such as Speed, Score, and Cells. Any changes to these values inside the Game logic are instantly reflected in the GameModel without needing to call any OnPropertyChanged methods, ensuring that the user interface remains synchronized with the evolving game state.

But MVUX doesn’t just stop at simplification; it empowers GameModel with the ability to seamlessly manage the state of Pong Wars. Through its intuitive state management mechanisms, MVUX ensures that changes to the game state are efficiently propagated to the user interface, guaranteeing a fluid and responsive experience. This integration enables effortless synchronization between the game mechanics and the interface.

If you are unfamiliar with MVUX (Model-View-Update eXtended) its a powerful design pattern that keeps the game’s logic and visuals neatly separated, making it easier to understand, maintain, and test.  If you’re curious to delve deeper, I recommend reading more about it here.

In summary, GameModel.cs serves as the central hub for managing the game’s state and leverages MVUX for a clean separation between UI and game logic.

Game Logic and Immutable Records (Game.cs):

Think of Game.cs as the brain behind how balls and cells interact. It keeps track of where each ball is and to which direction it’s moving, while making sure they stay within the game’s boundaries. It also manages the different types of cells on the game board, deciding if they can be broken or not, and how they affect the balls.

When the game starts, the Initialize() method in Game.cs kicks into action. It sets up the game board by placing cells in their initial positions and assigning balls their starting directions. Each cell’s properties, such as its position, ownership, and whether it contains a ball, are configured during this setup phase. This method ensures that the game begins with a well-defined state and also adds randomness to the beginning.

Balls: The class pays close attention to each ball’s position and direction, making sure they move smoothly across the game board. It calculates how they move based on their speed and where they’re headed.

Cells: It also takes care of the cells on the game board, deciding if they can be smashed or not. Whenever a ball hits a cell, the class figures out what happens next, whether it’s breaking the cell or bouncing off it.

Moving Balls: This class is in charge of figuring out how each ball moves, considering things like its current speed and where it’s headed. It makes sure the balls zip around the board smoothly.

Collisions: Whenever a ball hits something—whether it’s a wall or a cell—this class steps in to handle the impact. It decides if the ball bounces off or breaks something.

Embracing Immutability for Data Integrity

Uno Pong Wars prioritizes data integrity by using immutability. In Game.cs, changes to the game state create new records instead of modifying existing ones. This ensures the original state remains unaltered and prevents data corruption.

In simpler terms, Game.cs acts as the core logic, handling movement and collisions and maintaining data integrity through immutability.

The Board (Cell.cs):

The Cell record serves as a model or call it a bluepring representing individual cells on the game board. It encapsulates properties such as its identifier (Id), ownership by a player (Player), and whether it contains a ball (HasBall) via boolean.

The Cell class acts as the foundation for the game board, providing information about individual cells and interacting with the core game logic for collision detection and state updates. This interaction between cells and the game logic facilitates the dynamic gameplay experience.

Next Steps

This project not only served as a nostalgic walk down memory lane but also underscored the power of C# and MVUX. I encourage you to check out all the different versions of Pon Wars and try to build your own version. If you haven’t already, install the Uno Platform extension and jump right into the code we shared.

Want to learn more? Explore our vast library of samples and tutorials to gain hands-on experience


Related Posts

Uno Platform 5.2 LIVE Webinar – Today at 3 PM EST – Watch