Categories: Devlog | Game Design | Game Development | News | Retrospective | Technical | Web Development |
  • Using Godot with C# for Blind-Friendly Text Games

  • Technical · Godot · 2023-02-23 · nightblade
  • Picture of a speaker crossed-out

    I recently found a sweet, sweet solution to using Godot to create text-games for blind-friendly players. This matters because:

    • The performance of Godot's TTS APIs can vary a lot
    • Console output easily integrates with screen readers
    • Blind players can take advantage of their preferences (voice, speech rate, etc.) and advanced screen-reader features (forward, rewind, etc.)
    • Godot ships with many strong features for audio games, such as reverb and 2D positional audio.

    The Problem

    Running Godot games spawns a console window. By default, this window displays a lot of things we wouldn't want players to see or care about, such as:

    • The version of Godot running
    • The renderer used (e.g. Vulkan)
    • Warnings generated from our game
    • Errors and stack-traces generated from our game

    The Solution

    Fortunately, the former displays on stdout, and the latter on stderr. This provides us with a simple way to run Godot and suppress all text:

    • In the Project Settings > Application > Run, check Disable stderr. This prevents warnings/errors from being printed.
    • Check Flush stdout on Print debug. This means every print statement gets immediately printed.

    All this leaves is a way to print output to the console; you can achieve this by creating a warpper method like so:

    public static void Write(string message) {
        System.Console.WriteLine(message);
        Godot.GD.Print(message);
    }
    

    The first line prints only when the game is exported. The second prints only when the game is run in-editor (e.g. debugging). If the --no-header argument is implemented, you can also add that to your shell/batch script for running your game to disable the Godot header output; --quiet does the same thing.

    User Input

    Also, you can read player input with System.Console.ReadLine and friends - but be warned that this freezes the main Godot game. Perhaps running it in a separate thread may work. This makes it simple to build an interactive text game.

    Going Beyond Text

    Of course, there's no reason to stick just to text. You can use your main Godot game window to display relevant data, stats, scenes, whatever you like. You can even add buttons and allow users to input using buttons or console text as they see fit. Plus, fancy features like reverb!

    What's Next

    While text games aren't the Bee's Knees any more, I would love to see more text-accessible games made in Godot using this approach. If you're interested, check out the Games for Blind Gamers 2 Jam, or follow along on Mastodon or Twitter as I build a blind-first game using this approach.

  • Publishing Godot Games to Steam for MacOS without Hardware

  • Technical · Godot MacOS · 2022-07-22 · nightblade
  • Godot and Mac

    Are you working on a Godot desktop game? Would you like to publish it to MacOS, without the pain of learning about MacOS-specific requirements like signing, notorization, and paying $99/year?

    The good news is, if you plan to publish your game on Steam, you can easily do this, without Mac hardware. My inspiration for this is: be the change you want to see in the world. Publish your game for Mac (and Linux), so the market grows, and more great games support these OSes.

    If you're interested in how to do this, skip the section below; if you're interested in how this all works (sort of), the next section gives a very brief overview of the problem.

    Preamble: Signing, Notarization, and DMG Files

    I don't pretend to understand how all this works. My understanding, as someone who mainly uses Unix and Windows, is the following:

    • Like Windows, Mac OS requires developers to "sign" their apps with a cryptographic key of some sort
    • Unsigned apps previously run on Mac OS with a warning; as of writing (2022 June), they no longer run at all.
    • Notarization is a more scrutenized form of signing, and requires sending your app to Apple to approve
    • DMG files are native to Mac, and internally contain an .app file

    The key thing here: the DMG contains an .app "file" (looks like an executable on MacOS), which is actually a zip file. That zip contains your actual binaries.

    MacOS and Steam

    Follow these steps:

    • Create a new export profile for Mac. Configure as you like (e.g. exclusion files)
    • On a non-Mac system, export your project for Mac. If you're using the CLI, make sure you speify the output file name as a zip file (e.g. Game-MacOS.zip)
    • Verify the zip contents: it should have a Game.app directory (with Game replaced with your game name) at the root, with a Contents subdirectory
    • Upload that zip to Steam
    • Set up your launch options. According to the current Steamworks docs, for MacOS, your Executable should be set to Game.app/Contents/MacOS/Game (replace Game with your actual game name / folder name).

    And that's it! If you launch the game on Mac, it should just work!

    Finally: I recommend you always make sure someone with Mac hardware tests your game to ensure it launches correctly. If you don't have a friend with a Mac, you can always ask me on Twitter or by email to try it out.

  • Using Feature Toggles to Keep your Demo Code Up-to-Date

  • Game Development · 2022-03-28 · nightblade
  • One of the problems we run into as game developers, is keeping our game demos up-to-date. Data suggests that keeping your demo up longer results in more wishlists and sales; but how do you manage keeping your demo up-to-date with your game as it changes (including copying over bug-fixes)?

    Most developers opt to keep a separate branch/version of demo code, and manually keep that up-to-date. As the game and demo code diverge more and more, porting over changes and bug-fixes becomes a bigger and bigger headache.

    Instead, I proprose another solution: using feature toggles to keep your demo separate. This way, you keep a single code-base for your demo and production game. (You can read about feature toggles in Fowler's execllent, comprehensive guide.)

    How this works:

    • You add a feature toggle for something like "is_demo" or "demo_content"
    • You gate as much content as possible behind the toggle
    • You publish two versions of your game: one with the feature (demo) and one without (full game)

    Benefits of this approach:

    • Main game and demo are always in synch: you can easily change what's in the demo (levels, skills, etc.)
    • Any bug-fixes in the main game, for bugs that are present in the demo, get fixed in the demo for free.
    • Requires minimally-invasive changes in your game to create the demo version

    The main drawback of this approach, is that your demo includes all the content and functionality of the actual game. While this means you can easily do things like switch which skills and levels are available to the player, it also adds the possibility of someone reverse-engineering your demo and getting full access to your game. If you're not okay with that, I suggest you look at other approaches.

    Gating Content

    Depending on your language/engine of choice, you either gate content behind pre-processor statements (e.g. #if DEBUG) or regular if-statements (e.g. if Features.IsDemoMode == true).

    I can't stress this enough: it's really important to learn from "defense in-depth" and gate as much as you can. You want to protect your game from technically apt adversaries who can decompile/reverse-engineer it and gain access to the full game.

    To do this, add checks absolutely everywhere possible. For something like Gem Worlds, this means adding checks in the skill shop, in the core game (when a level loads), on the title screen (continuing a game but you're past the end of the demo?), etc.

    For additional security, if possible, store content and data in JSON/external files, and use your editor to switch which of those files is used in the demo. (e.g. if your skills are stored in skills.json, maybe you have two versions - skills.json and skills-demo.json - and can switch which one is used in the demo build.)

    While this is game-specific, the more checks you add, the better; not only do you reduce the chance of accidentally giving players access to the full game, but you also make sure that there's no way for them to break out of the demo content.

    The other problem with gating using feature toggles, is that it really requires additional testing of the final build. Try your game, play through to the end; then put on your attacker hat, and try to circumvent the restrictions. Can you edit the save files and get inaccessible skills, or go past the last part of the demo? Make sure you test until you feel comfortable that it's bullet-proof.

    If all else fails, platforms like Steam allow you to remove/disable the binaries (and in Steam's case, it yoinks the files away from the user's system). You can always rely on that in case things go bad.

    Publishing Two Versions

    Finally, constructing the demo build. Many programming languages since C/C++ allow you to define build-time constants, and specify build profiles. This includes C/C++, C#, Java, and others. I will focus on Godot/GDScript, since that's my current stack of choice.

    As of Godot 3.4, you need to:

    • Launch the export dialog (Project > Export ...)
    • Create a second export option for each platform you support (e.g. Windows, Linux)
    • Click on the Features tab
    • Under Custom (comma-separated), add your feature, e.g. demo_content
    • Profit

    (The same steps apply to other game engines, like Unity, MonoGame, etc.)

    Once that's done, viola, you have two separate builds - demo and non-demo. Make sure you test thoroughly!

    Closing Thoughts

    This blog post illustrates one way to create demo builds, that are always in synch with the main game content. It's not the best approach or the worst, simply one approach you can choose. While it ships the full game along with the demo, with defense in-depth, you can be quite confident that players can't access anything they shouldn't.

    If you have any thoughts or alternative approaches, hit me up on Twitter or Discord, I would love to know of a better way to achieve this.

  • A Practical Guide to Make Your Own Game Engine

  • Game Development · 2021-07-23 · nightblade
  • Want to make games faster, bigger, and with better quality, while investing the same amount of time? It's possible, albeit with an unintuitive solution: a custom game engine. I'm here to tell you why you can, and should, build a custom game engine.

    It's 2021, and lots of strong, general-purpose game engines abound: from Unreal to Godot to GameMaker to Construct, tools exist for beginner and experienced developers alike. Why bother making your own game engine?

    It's a great learning tool, sure, but a better reason exists: you can customize your game engine to your own personal style and workflow.

    A great example of this is RPG Maker, which specializes in making JRPG games. Using RPG Maker, you can - very quickly, and without much coding skill - create large-scale and complex JRPGs. RPG Maker includes events for all kinds of common JRPG things (dialog, shops, inns, quest NPCs, etc.) which makes it very, very fast to build, compared to building something from scratch.

    Even if you think this isn't a great idea, I guarantee you will learn a ton by doing this. If you're convinced, read on.

    Pick a Small Target

    You're not planning to build a general-purpose, large-scale game engines. Those are difficult, and require lots of different skillls which you may not have.

    First, identify an existing game engine, framework, library, toolkit, etc. that you use and like. What's great about it? Importantly, what's not good about it? Where can you improve upon it, or what pain-points does it inflict on you that you wish you could work around?

    Once you identified your starting-point, you can even build on top of an existing game engine. This way, you create a standalone library, or a thin "layer" that makes some tasks easier. It could be as simple as scene management, or as complicated as common code for a roguelike.

    Whatever your goal, pick something small and achievable as the first step.

    Pick a Companion Game

    Now, the most important part: do not build your game engine or framework in isolation! You will end up with bloated features that are difficult to use, and code that nobody uses. Instead, design a small standalone game that you feel really excited to build.

    The key: build your game in tandem with your game engine. For example, if you need outlined text, add some code for that to your game engine, then add it to the titlescreen, options screen, or whatever location in your game that you need it.

    This keeps your code useful, streamlined, and well-tested to begin with; it greatly increases the chances that you can ship something useful at the end of your game development cycle, whether the game ships or not.

    Work on your game framework with that cycle: add code, test it, implement it in your game. Rinse, repeat.

    Use-Case

    If your game framework extends beyond a simple library, you should consider the user workflow carefully. How do users use your framework? Do they link a DLL, or copy/paste code into their project? Do you need to provide a template project with some starter code, or simply a spec for a JSON file?

    Looking at this early on makes it easier for users to eventually use your game framework.

    Testing and Quality

    Testing your game framework is really important. As it grows, you will find it increasingly likely that you accidentally break things in other places and not notice. I highly recommend writing unit tests and/or integration tests for every single line of code you write. This makes it very easy to make changes: you simply run the tests, and they identify anything broken you need to fix.

    A War Story: Oneons and Puffin

    I applied this process to a number of game frameworks over the years (mostly abandonware). One of them, Puffin, actually shipped to production (I finished it and launched the game and the framework). It didn't meet my long-term needs, but it worked well enough.

    Here's a quick summary of how that came to be:

    • Target: I really like Godot. One thing that frequently bugs me is the lack of free support for exporting your game for the Nintendo Switch. I'm also not a big fan of GDscript, the internal, Python-like scripting language.
    • Goal: I decided to build something similar to Godot - a visual IDE - albeit in C#. I chose MonoGame as my base, since I've used it before, and since it has excellent free support for porting to the Switch
    • Small Target: For the first version of Puffin, I decided to leverage my experience with CraftyJS and build a small, easy-to-use fluent API. I based it on Entity-Component System architecture. (This isn't a good fit for Godot-like games, which use nested nodes.)
    • Start Small: I build the absolute minimum code to get started: a base entity class, and some components for rendering sprites and text. That's it. I decided I would limit myself to no IDE or UI controls, as those require a lot of additional effort to build.
    • Companion Game: I started immediately on Oneons, my companion game: a tiny broughlike dungeon-crawler with 12 floors of mayhem, monsters with powers, and tactical combat. I shipped the first version of Oneons, with Puffin, in a month for the Persistent Game Jam.
    • Unit testing: Most of the Puffin code is unit-tested, and every commit builds and runs the test in Jenkins. This helped me notice when I accidentally broke things.

    Ultimately, Puffin didn't meet my needs; in particular, the choice of an ECS architecture didn't mesh well with Godot-style nested scenes and subscenes.

    I may revisit Puffin in the future, and I certainly learned a lot about development through this experience. I hope you, too, will find the time and effort to build something, big or small, to help you build games you like faster and better than whatever you're using today.

  • Web Application Translation Architecture in .NET

  • Web Development · .NET Core Architecture · 2020-08-11 · nightblade
  • globe of languages

    So you want to localize and internationalize your web application? There are a lot of considerations, but here, we briefly discuss just the translation part.

    This post covers a couple of the architectural/design options and discusses their respective trade-offs. While this is a bit specific to .NET, other languages no doubt provide similar concepts, with various levels of API support (e.g. language stored in cookies)

    Individual Translations via RESX Files

    .NET and .NET Core provide some infrastructure called "resource files" (.resx) extension). These are XML files which you can edit directly in Visual Studio; at runtime, they compile down to a binary format. They generally recommend creating a few resource files (one per back-end controller or shared module). Advantages of this approach include:

    • Easy editing of the file (in-IDE)
    • Easy versioning/history of the file (it's a text format)
    • API support for configuring the prefered language in a cookie, etc.
    • Localized changes on translation change (no need to re-test everything)
    • Efficient, with a small size at runtime

    The disadvantages of this format include:

    • Any translation change requires recompiling the entire application
    • Editing dozens of files can be very cumbersome
    • Difficult to make a translation change and see it immediately in-app (unless you're a developer)
    • You can't store any sort of metadata (e.g. notes) with translations

    Overall, I think this approach works well if you plan to update translations periodically and don't need an external translater. (If you do, and they're not a coder, you'll need to make additional tooling to export/import the strings in a format they can understand.)

    Using a Database for Translations

    One common alternative approach is to store the translations in a database (relational or otherwise) and simply load/display them at runtime. This confers some additional advantages over resource files:

    • Ability to update a translation and instantly see the change in-app
    • Ability to store meta-data (like notes) with each message
    • You can quickly query to find missing strings in various languages
    • Non-technical users can easily edit translations via a simple web UI

    However, it contains some additional downsides:

    • You need to write a web UI to allow translators to be able to view/update translations
    • Making several database calls just to load one view/page, can be costly in terms of performance
    • You can read/cache strings in memory on app-start, but then your app requires additional memory per language

    I think this approach suits situations where you absolutely must be able to see updated translations reflected immediately, or where you have non-technical translators who need an easy way to be able to update translations.

    If you know of any other architectures/designs, drop me a note on Twitter and let me know!