Build together, debug together. Join the community on Discord.→

10 Days of Game Development with Uno Platform, WebAssembly, and C#

A few months ago, we highlighted an unconventional use of Uno Platform, in which Asadullah Refat created a 2D Space Shooter game. With the release of Uno Platform 4.7, he chose to leverage its advancements to rebuild his 2D game engine from the ground up. In a series of 10 LinkedIn posts, he shared his experience in developing a cross-platform 2D game, harnessing the capabilities of the Uno Platform, WebAssembly, and C#.

We stitched his series together that covers topics from creating a 2D game engine and implementing keyframe animations to developing responsive game design and optimizing performance using AOT compilation. In addition, he shows us how he tackles challenges such as creating custom audio solutions, implementing post-processing effects, and using object containers for improved rendering. 

Day 1: Recreating a 2D Game Engine with Uno Platform 4.7

Uno Platform 4.7 features an updated solution structure, including a class library project, simplifying asset and font management while reducing build times. The game engine comprises three primary classes: Scene, Construct, and Generator.

  • Scene: Used to add and animate game elements or Constructs.

  • Construct: A 2D game element with two main functions – Animate and Recycle, determining animation and recycling of game elements.

  • Generator: Initializes Constructs, and generates game elements as defined by the developer.

Additionally, a Controller class has been created to take input from touchscreen, mouse, and keyboard, enabling customization of the main character’s movements and interactions.

The game engine is written purely in C# and can be used to create games for Android, iOS, and web platforms, including Progressive Web Apps (PWAs), all thanks to the Uno Platform and WebAssembly.

Day 2: Character Keyframe Animation

Character keyframe animation is a way to define different frames for characters to perform specific actions or movements. In this game, characters have animations for various events, such as dropping crackers on honking cars, getting hit by a UFO, or cheering when a boss is defeated.

The game also includes a range of visual effects like fade, pop, explode, and shrink that can be applied to game elements. These effects can be seen in action on the title screen and throughout the game. In summary, the post showcases the capabilities of the Uno Platform and WebAssembly for creating engaging 2D games with smooth animations and visual effects. 

Read the original post here.

Day 3: Scene and Gameplay Loop

Day 3 discusses the implementation of a Scene and Gameplay loop for a 2D game, including the choice of Timer, rendering elements efficiently, and using an object pool mechanism to improve performance.

The Scene class is built on a Canvas element and utilizes the System.Threading.PeriodicTimer introduced in .NET 6. This Timer offers an asynchronous WaitForNextTickAsync() function, which avoids memory leaks compared to event subscription-based Tick raise methods. Refat tested various timers and found that event-based Timers lose milliseconds on each Tick, affecting frame rate consistency. Hence, the PeriodicTimer was chosen for its accuracy.

An object pool mechanism was developed to render numerous elements in the Canvas without causing memory issues or lag. This mechanism stores all game elements (Constructs) in the Scene but keeps them invisible until needed. Then, when a Construct is required, its coordinates are updated, and the Scene class handles the animation.

This approach eliminates the need to constantly add and remove elements from the Scene, resulting in a significant performance boost.

Read the original post here.

Day 4: Building Audio Functionality

Since there is no built-in solution for playing audio files in Uno WebAssembly, Asadullah created a custom solution using JS interoperability.

With the introduction of JSImport and JSExport functionality in Uno Platform from .NET 7, he utilized the HtmlElement attribute to create a custom FrameworkElement. This element is an HTML audio tag encapsulated within a C# class. Next, functions were written to initialize the custom audio element with a source, volume, loop, and a C# event to intercept the “ended” event of the audio tag. This approach allows for seamless interaction between JavaScript events and C# code.

Other functions were developed to play, pause, resume, and stop the audio. To enhance the game experience, the engine randomizes audio playback for specific sound types when multiple audio files are available. This method ensures clean code and self-management of audio files through AudioTuple instances.

Read the original post here.

Day 5: Creating a Thumbstick Control

This post explains the process of creating a Thumbstick control. A Thumbstick control is a UI element that allows players to move in different directions by dragging a circle on the screen.

To create the control, a canvas element was used, containing four rectangles and a circle. The rectangles represent the cardinal directions (up, down, left, and right), while the circle serves as a neutral zone for no movement. Additionally, event handlers were added for pointer pressed, pointer moved, and pointer released events on the canvas.

The Thumbstick control logic is as follows: when the pointer is pressed on the circle, it becomes active and tracks the pointer position. If the circle intersects any rectangle, a flag for that direction is set to true. The game engine reads these flags and moves the player accordingly.

Handling diagonal movements required overlapping rectangles at the corners. When the circle touches two rectangles simultaneously, both flags are set to true, enabling diagonal movement.

Read the original post here.

Day 6: Creating a Responsive Game with a Dynamic Game Viewport

In this post, Asadullah discusses creating a responsive 2D game using the Uno Platform and WebAssembly that dynamically adjusts its graphics and gameplay to fit any screen size. Traditional fixed-width game viewports have limitations, such as not utilizing available space on larger screens and potential distortion or cropping on smaller screens.

The solution proposed is a dynamic game viewport that responsively resizes and scales game elements to deliver a consistent experience across all screen sizes. Asadullah designed a simple function that monitors the game view’s size and determines the scaling ratio for the game, applying it to the game view.

The implementation involves a Scene class with two internal layers: one that detects the actual size of the game view and another that serves as the canvas for game elements. When the game view’s size changes, the scene height and width are adjusted, but a scale transformation is performed instead of resizing the canvas. The render transform-origin is (0,0), ensuring consistent scaling. The game algorithm generates objects based on a default game view size set internally.

This approach results in a responsive game that looks great on any screen size or device, with graphics and elements scaling according to the available space without affecting quality or performance. The gameplay remains consistent and enjoyable across various screen sizes.

Read the original post here.

Day 7: Developing Ambient Lighting and Drop Shadow Effects

The focus of day 7 is on developing ambient lighting and drop shadow effects, essential for enhancing game objects’ realism. Asadullah opted for comic book-style graphics to simplify the process. By utilizing Storyboards, double animation, and color animation classes, he achieved smooth transitions of lighting effects, enabling realistic and dynamic lighting and shadow effects within the 2D game engine.

He first created a drop shadow object that gets attached to any game object with a nonzero drop shadow distance value. As objects move across the canvas, the game engine moves the attached drop shadow objects accordingly. To mimic a light source effect, the width and height of the drop shadow object are calibrated with respect to the distance of the parent object.

He also developed day and night ambient lighting effects that impact the luminosity, opacity, and color of all game objects in the scene. For example, when night mode is toggled, all objects in view simultaneously change their color palette to render and overlay upon themselves. In addition, soft light effects from the top right and bottom left corners of the scene smoothly switch with respect to the scene’s night mode.

Read the original post here.

Day 8: Implementing Post-Processing Effects

The addition of post-processing effects results in a more realistic and immersive gaming experience, as demonstrated above.

This post discusses implementing post-processing effects in a 2D game engine. Post-processing effects are visual enhancements that improve a game’s appearance and mood. However, the Uno Platform and WebAssembly don’t support native graphics APIs like OpenGL or DirectX, relying on HTML5 Divs and CSS for rendering, which limits the availability of common post-processing effects.

To overcome this limitation, Asadullah combined JSInterop and CSS filters. JSInterop is a feature of the Uno Platform that enables C# code to call JavaScript functions and vice versa. CSS filters are functions that modify the pixels of an HTML element, such as brightness, contrast, blur, or hue.

Using JSInterop and CSS filters, Refat created simple yet effective post-processing effects for the game engine. For instance, he made a light source class that casts soft shadows by applying a blur filter and transform function. He also developed a depth-of-field effect that blurs objects based on their distance from the camera and a glow effect that uses brightness, drop shadow filters, and an animation function to make objects shine.

Read the original post here.

Day 9: Creating a Game Object Container

Day 9 consists of creating a game object container for a 2D game engine using the Uno Platform and WebAssembly. A game object container is a mechanism that groups game objects by similar animations and z-order axis, improving performance by rendering these objects simultaneously.

As the number of game objects in a scene grows, rendering them individually can impact the frame rate. To avoid this, he created a game object container class called GameObjectContainer, which inherits from the Canvas class. He added methods to add child elements to the container and then added the container to the GameView responsible for rendering the game scene.

Using game object containers, you can render similar game objects in groups, such as trees, sidewalks, and street lamps. This reduces the number of objects to loop through, significantly improving performance.

Read the original post here.

Day 10: Leveraged AOT Compilation to Optimize Payload Size

In the final post, Refat discusses how he optimized the payload size and performance of his 2D game using Ahead-of-Time (AOT) compilation with Uno Platform and WebAssembly. WebAssembly has three execution modes: Interpreter, AOT, and Mixed Mode (InterpreterAndAOT).

Interpreter mode provides flexibility during debugging but is slower and has the smallest payload size. AOT offers the best performance but increases payload size significantly. Mixed mode balances performance and payload size.

He chose mixed mode for his game and used Uno Platform’s profile-guided AOT compilation to speed up the process and save deployment time. This approach allows developers to optimize parts of their app while keeping other parts in interpreter mode, without sacrificing performance.

Read the original post here.

Conclusion

Asadullah does an incredible job demonstrating the versatility of Uno Platform in game development and provides valuable insights for developers looking to create their own 2D games using Uno Platform and Webassembly. Make sure to check out his repo for the latest source code and give his game a try.

About Uno Platform

For those new to the Uno Platform, it allows for creating pixel-perfect, single-source C# and XAML apps that run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. In addition, it offers Figma integration for design-development handoff and a set of extensions to bootstrap your projects. Uno Platform is free, open-source (Apache 2.0), and available on GitHub.

Next Steps

To upgrade to the latest release of Uno Platform, please update your packages to 4.8 via your Visual Studio NuGet package manager! If you are new to Uno Platform, following our official getting started guide is the best way to get started. (5 min to complete)

Share this post:

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