How We Built Purrfect Code: A Puzzle Game for Developers

AUG 08, 2024

Link to Youtube Video (visible only when JS is disabled)

"Purrfect Code" is a new box-pushing programming puzzle game, powered by Flutter, Dart, and the Flame game engine and designed to challenge developers to solve puzzles by writing code.


Game Overview

In "Purrfect Code," players are tasked with updating the programming of a janitor robot on Google's new space station headquarters where a shipment of cats has mistakenly arrived. The goal of the game is to help the robot safely round up boxes containing the cats and push them onto teleporter plates so they can get home. Players write JavaScript to control the robots movements while trying to discover efficient solutions to navigate through grid-based puzzles.

The game is a fun way to explore coding, using your intelligence and creativity. Each level is focused on a programming concept and gently increases in complexity over the course of the five levels in the game.

The game loop is as follows:

  1. Players write JavaScript code to move the robot (up, down, left, or right)

2. They then run their code and watch as the robot attempts to solve the level

3. The level is completed when all boxes containing cats are pushed to the teleporter plates

4. Players are scored on the efficiency of their solutions including the number of spaces moved and the brevity of their code

Building the Game with Flutter, Dart, and Flame

We chose to build "Purrfect Code" using Flutter and Dart, as they provide a powerful and flexible framework for creating multi-platform applications and games. Flutter's widget system and reactive programming model allowed us to create a responsive user interface for a variety of screen sizes in the browser. Dart's strongly-typed and object-oriented programming features made it easy to structure our code and keep our codebase clean. The Flame game engine, built on top of Flutter, offered a nice foundation of basic features needed for game development, allowing us to focus on our gameplay logic and the unique aspects of the game. Flutter and Flame were a clear choice for this project due to Purrfect Code’s nature as a unique game/app hybrid with both an IDE inspired UI and a game view with animated sprites and sound.


Chrome and JavaScript: Leveraging Built-in Browser Support

Once we settled on the programming concept for the game, we needed to choose what programming language players would use. We wanted something familiar and popular among devs. Initially we considered using Python as the in-game programming language. However after some consideration, we decided to go with JavaScript to leverage the built-in JavaScript support provided by Chrome. By utilizing JavaScript, we could tap into the browser's native capabilities without the need to load an additional language interpreter. This decision not only simplified our development process, but also ensured a seamless experience for players, as the game could load quickly with minimal dependencies.

Flutter’s 3.22 release at Google I/O this year introduced stable support for WebAssembly (WASM) which allowed us to optimize performance-critical parts of the game. By compiling certain game logic to WASM, we ensured that "Purrfect Code" runs efficiently in the browser, delivering a smooth and responsive gaming experience without compromising on performance.

var dir = [moveEast,moveNorth,moveWest,moveSouth];
for(i=0;i<4;i++){
    for(j=0;j<5;j++)dir[i]();
}

Flame Game Engine: Modular and Efficient Game Development

To bring "Purrfect Code" to life, we utilized the Flame Game Engine, an open source modular game engine built on Flutter that provides many of the commonly needed features for game development. Flame leverages the robust infrastructure of Flutter while simplifying the code required to build our project. It offers a simple yet effective game loop implementation and a wide range of essential functionalities, such as audio playback, sprite management, animation features, collision, and the Flame Component System (FCS). The engine's component-based architecture, sprite rendering, and animation support enabled us to create visually appealing graphics, smooth animations, and interactive gameplay elements without having to reinvent the wheel. Leveraging Flame, made the development process more efficient with essential features ready at hand, allowing us to focus on creating an engaging gaming experience for our players.


Implementing Game Features with Flame

Flame offers a simple yet effective game loop implementation and a wide range of essential functionalities, such as audio playback, sprite management, animation features, collision, and the Flame Component System (FCS).

Flame's sprite rendering and animation system allowed us to bring the game's characters and environments to life without having to write graphics code. We could create sprite sheets, define animation sequences, and smoothly animate the character's movements and special effects. We used Flame’s priority system to write a visual sorting system for our “top down” game perspective. Our artist built levels with many overlapping features to enhance the feeling of depth with the sprites and give the game view a less “grid like” appearance. We needed to make sure that when the robot would move behind them that it would be occluded correctly. Flame’s priority system allowed us to assign priorities to different visual elements, ensuring that they were drawn in the correct order and overlapped properly. This system was flexible enough for us to include a shadow system where individual components could feature animated shadows that mirrored object actions and gave the art a feeling of depth that made scenes both more vibrant and easier to visually comprehend.

The BoxShadow class in our codebase is a good example of this and shows how we created dynamic and interactive shadows for the game's moving box objects. By extending the SpriteAnimationComponent and implementing the GridElement and HasVisibility mixins, we were able to load sprite sheets for the box shadow and box teleport animations, define multiple animations for open, closed, idle and the teleporting states and integrate them into the grid-based layout. The onLoad method loads the animations and sets the initial position and priority of the component based on its grid position, while the update method ensures that the component's priority updates dynamically if the box moves in front of or behind an occluding object. Using Flame’s priority and animation systems this way enabled us to create shadow effects which always help users visually understand virtual spaces and make them more believable and leads to a more integrated visual presentation.

Excerpt from box_shadow.dart, view the full class on GitHub to learn more.

@override
  Future<void> onLoad() async {
    await _loadAnimations().then((_) => {animation = _boxClosed});


    position.add(Vector2(
        ((gridPosition.x * gridPixelDimensions.x) + gridPixelOffset.x),
        ((gridPosition.y * gridPixelDimensions.y) + gridPixelOffset.y)));
    priority = getLayeredGridValue();
  }
  @override
  void update(double dt) async {
    super.update(dt);


    if (getLayeredGridValue() != priority) {
      priority = getLayeredGridValue();
    }
  }

Google Developer Program and Badges

Because we wanted to keep the scope for the game small, and we wanted to be able to deploy it as a simple web page we wanted to avoid setting up a backend for the game. But we did want some way for developers to be rewarded for their progress, similar to achievement systems in popular gaming platforms. Instead of building a separate backend system for tracking player progress and achievements, we integrated with Google Developer Program by allowing players to click a link and collect a badge on their profile once they’d completed a level, providing a sense of accomplishment and recognition within the developer community.

Purrfect Code Google Developer Program profile badges

Project IDX: A Streamlined Development Environment

During the development of "Purrfect Code," we leveraged Google’s Project IDX, an AI-assisted workspace for full-stack, multi platform app development in the cloud. Since we were already comfortable working in VS Code, Project IDX provided a familiar environment for coding, debugging, and testing our game and allowed us to get up and running quickly. With Flutter and Dart already set up and ready to go in the browser, we could dive straight into development without the hassle of local environment configuration. The intelligent code completion, real-time error checking, and integrated debugging tools offered by Project IDX helped us keep our productivity high. If you’re curious, Project IDX is a great way to quickly try out Purrfect Code and explore its code directly from your browser. Click this link to open the project directly in IDX and run the project yourself. Make sure to check the box that asks if this is a Flutter app.


Firebase for Fast Secure Hosting

We chose Firebase Hosting to ensure the secure and efficient global delivery of Purrfect Code. The platform's zero-configuration SSL guarantees content is served over HTTPS, enhancing security. Furthermore, its support for modern web frameworks and automated builds from our GitHub repository enabled rapid deployment of updates. The Firebase CLI, local emulation, and preview URLs streamlined our testing and collaboration processes. These features, coupled with the potential for future game evolutions leveraging the Gemini sample templates for AI integration, made Firebase Hosting the ideal choice for launching our game.


Firebase Configuration With WASM

Purrfect Code uses Web Assembly which requires some additional steps during deployment. In our firebase.json configuration we add a predeploy command that enables our web build to be built with WASM. The “--no-strip-wasm” argument prevents the code from running through one last minimization step which makes errors more difficult to read and debug. WASM also requires a cross-origin opener policy and a cross origin embedder policy for multi-threading and memory sharing.

"hosting": {
      "predeploy": "flutter build web --wasm",
      "public": "build/web",
      "ignore": [
        "firebase.json",
        "**/.*"
      ],
      "headers": [
        {
          "source": "**/*",
          "headers": [
            {
              "key": "cross-origin-opener-policy",
              "value": "same-origin"
            },
            {
              "key": "cross-origin-embedder-policy",
              "value": "require-corp"
            }
          ]
        }
      ]
    }

Learning and Resources

Throughout the development of "Purrfect Code," we relied on various resources and drew inspiration from existing projects, a body of work we hope that this project can add to. Here are some key learning resources and references we found valuable:


Reference Projects

We used the Super Dash and I/O Flip games as references for best practices and implementation ideas. Both projects provided valuable insights into structuring a Flutter game, handling game states, and implementing game mechanics. Super Dash was directly relevant because it was simple and didn’t require backend services, like our game. I/O Flip was bigger and does support a backend, as well as generative AI features so may be of interest to devs building games that require those features. Both are valuable resources if you’re interested in making games in Flutter.


Conclusion

We hope that "Purrfect Code" not only provides an enjoyable gaming experience but also serves as a learning resource for developers interested in game development with Flutter and Flame. The combination of Flutter, Flame and Chrome proved to be a great fit for our game/app hybrid, providing us with a solid foundation for UI development, graphics rendering, sound management, and more. We encourage you to explore the game's source code and experiment with extending it further. There are numerous possibilities for adding new features, levels, and gameplay mechanics. Jump into the codebase in Project IDX, check the box that asks if this is a Flutter app, and let your creativity run wild!