Exploring Defold and TypeScript

The Defold game engine has been in my bookmarks folder for months now, but I’ve only just recently started to make things with the engine.

Defold fits a number of my criteria for a great game engine:

  • It exports to the web, and performance in the browser is actually good!
  • It effortlessly exports to all other platforms too
  • It’s well suited for 2D games (the editor can plop down objects at nice round X and Y coordinates)
  • It’s free and open-source (technically there are limitations in the Defold license, but nothing that affects making games)
  • It’s extremely well maintained with monthly updates

However, I was initially put off by the use of Lua as its scripting language. I had never used Lua, so that was a barrier to entry. It’s also a dynamically typed language, which is a pain to debug.

Then I discovered the TypeScript community extensions (@ts-defold) for Defold. These tools allow using Defold with TypeScript, a strongly typed language that I already know and enjoy.

It turns out I’m more productive working on tools than games, so here’s what I’ve done so far:

  • Created a TypeScriptToLua (TSTL) plugin that will strip the last extension from files that have multiple extensions. This is used to handle Defold’s specific file extensions, so a file name like `player.script.ts` is output to `player.script` instead of the incorrect `player.script.lua`. This plugin can replace the patch file that come with @ts-defold, so you’re no longer locked to early versions of TSTL.
  • Tweaked the type definitions for the Defold game engine from @ts-defold/types. I’ve been slowly describing more of the types that were left as unknown in the original output. I’m not sure how useful this is to the average developer, but I find it satisfying.
  • Created type definitions for @britzl’s new boom framework.
  • Created a project template that includes all of the above.
  • Created type definitions for @thejustinwalsh’s xmath framework. The comments are copied from Defold’s vmath library, so I’m not sure they’re 100% accurate to xmath’s implementation, but they seem pretty close.

I also played around with using Chat-GPT to generate some native extensions. My C++ knowledge is limited, but I was able to cajole the AI into creating something that actually works.

There are already community extensions that cover these use cases, so I doubt my libraries will see much use. Still, it’s fun to see how easy it is to extend the engine, despite being a complete newcomer.

Clones of Vampire Survivors are in the zeitgeist now, so I’m currently trying to create a really simple one of those. I’ll open-source the whole project when it’s done.

Development Tools

I’m preparing to release my first game soon.

There’s a particular tool that I’m waiting to reach stable release before I use it in production. In the meantime, I’ve been improving my own release workflow.

Build process

I now have a fully automated build process, running on Github Actions. With one click, the build process will take care of the following tasks:

  • Minify JSON files
  • Concatenate and minify JS files with Terser (preserving comments with author/license information)
  • Delete development files not needed in release
  • Delete plugin files that are included but turned off
  • Separate pathways for preparing files for the web versus the local version, so it will delete web-only files if the build is intended for installation, and vice-verse
  • Add the path to every game asset to a precache in the service worker
  • Build the app for Windows and Linux installation with Tauri

Tauri

All my games run in the browser – but some people prefer a download they can run offline.

Enter Tauri. Tauri is a tool written in Rust that will bundle all the assets required to run your web app with a browser engine, allowing it to be installed and run locally.

Unlike NWJS or Electron, competing tools that ship static browser versions, Tauri runs on the most recent webview available to your OS. So you’re always shipping on the fastest, most secure platform possible. Granted, browser engines can frequently introduce new bugs with each release, so I wouldn’t necessarily recommend this approach for sensitive, commercial software. But for a hobbyist like me, it fits.

By default, RPG Maker MV ships with NWJS, so it expects access to Node and its file system module to save and load files. Accommodating Tauri required a few changes to my code.

Tauri Store is a simple to use plugin that allows Tauri apps to save local data. It can store similar data types to a browser’s local storage, the technology that RPG Maker uses in its web version. The major difference is that Tauri’s commands are always asynchronous. Luckily I had already written a new save manager that interfaces with web workers, which are asynchronous as well. So it took less than a hundred new lines of code to get it working with Tauri instead.

I also ended up including Tauri Window State in my build. It’s another easily integrated plugin that saves the app’s window position and fullscreen status, so the next time the app is launched, it starts in the same position as when it exited.

Overall my experience with Tauri has been positive. I’m new to Rust as a language, so I appreciate the official plugins having an extremely simple API that only takes a couple lines of code to run.

I experimented with including Rust crates used for compression, so I could compress data before writing it to Tauri Store. But it was a bit of a mess – Rust is very strict about data types, only serialize-able data can be transferred between the Tauri backend and the webview. I’m sure it’s do-able with a few more weeks of practice, but it’s daunting for a Rust newcomer.

Good luck to the Tauri team, who will hopefully be releasing their stable 1.0 version any day now. I’ll be publishing apps with it soon after.

Update

I’m adding this addendum some months later.

Tauri has reached stable. Congratulations to their team and all the hard work they put into the project.

Ultimately, I decided against using Tauri, and instead built a template around Electron.

There are two deciding factors that ultimately made me favour Electron.

  1. Electron’s WebGL performance is pretty good on all platforms, whereas Tauri uses a different engine for each platform, and my testing on Linux was pretty slow with WebGL content.
  2. Electron allows exporting to raw files without an installer, which is Itch.io’s preferred format.

Stardew Valley Thoughts

Originally written as feedback to the podcast Watch Out For Fireballs.

When I initially played Stardew, I dated the washed-up football player Alex with my male farmer. There’s a touching moment at the end of his romance arc where he admits he never expected himself to fall in love with a guy–but he’s glad it happened. Those themes are barely explored beyond that one line, but the implication that Alex has been struggling with his sexuality always stuck with me. His grandfather also expresses prejudice against your (same-gender) marriage, so you can imagine there’s a deeper story about this family left untold.

The second time I played, I dated the fan-favourite Shane. There’s a memorable moment where you go on a date with him to see a football game, and when your team scores, Shane gets so excited that he spontaneously kisses you. I’m sure it’s a cute scene regardless of your gender, but for two men to kiss in a traditionally hyper-masculine setting, it feels risky and subversive.

That said, I’m getting tired of games portraying all romance options as player-sexual. If you befriend these characters, there’s no hint that they might be gay or bisexual; you have to “opt-in” by initiating a same-gender romance. By siloing all queer content, it reinforces the idea that everyone is straight by default, and wholly-optional queer moments only exist to placate queer audiences. It’s a bummer.

Here’s hoping for more from Haunted Chocolatier.

The Club 0.9.5

This is likely the last pre-release build. I’ll primarily be working on graphical materials to support the launch from now on.

Updates

  • Updated fflate 0.7.1 to 0.7.2
  • Updated PixiJs from 6.1.1 to 6.1.3
  • Created a custom build of PixiJs that excludes some modules for a smaller build size
  • Created a custom build of PixiJs Filters, excluding unused filters
    • Added enhanced scanline mode, featuring the PixiJs scanline filter
  • Revamped saving and loading files completely
    • Save files are now compressed and saved (and decompressed and loaded) in a web worker, so as to not bog down regular gameplay
    • In nwjs installs, the files are written to disk using node.js modules in a worker
    • In the browser, files are stored in indexdb using idb-keyval
  • Added additional sounds and map-zoom when talking with NPCs
  • Added a unique description to each object on the map
  • Prevented NPCs from using emotes randomly while talking to you, to avoid confusion
    • They still randomly emote while not interacting with the player
    • Adjusted position of emotes so they’re not as high
  • Added custom mouse cursor

Tactical RPGs and Exploration

Over on the official RPG Maker forums, user Frostorm asked this question: why do tactical RPGs rarely feature aspects of dungeon crawling and exploration?

I decided to weigh in on the discussion.

While it’s not impossible to combine dungeon crawling and tactics, I think each genre tends to have a different approach with different consequences.

Traditional RPGs and Tactical RPGs must have different densities of encounters.

Tactical RPGs have long, challenging battles. If they didn’t pressure you to consider your actions, they wouldn’t really be tactical.

Traditional RPGs tend to have short, simple battles that you can button-mash through. They use dungeons as an excuse to throw dozens of (often random) battles at the player. If their battles were as long and mentally intensive as a Tactical RPG, this would quickly exhaust and frustrate players.

These different approaches often result in different consequences for the player’s resource management.

Traditional RPGs tend to challenge the player during dungeon crawls by slowly whittling down their resources over dozens of battles that are individually inconsequential.

Tactical RPGs tend to make each individual battle–and even each individual action in that battle–consequential. Tactical RPGs often fully heal you after battle, because the focus is on the individual encounter and not on the long-haul.

Divinity Original Sin 2 has both exploration and combat. However, I would argue it achieves success with this formula through expertly crafted game design and a number of tightly integrated systems that are difficult to replicate in RPG Maker.

Each battle has a unique composition of enemies that have been carefully placed into the world–they never feel like tedious cookie-cutter encounters meant to pad out the length of the game. Position and elevation matters in battle, so ranged characters are usually placed on the high ground, to press the advantages that gives them. If you wander into one of these battles unawares, you’ll be at a significant disadvantage.

Conversely, if you explore carefully and notice enemies before they spot you, you can spin the situation to your benefit. You’re able to strategically place each of your party members before you initiate combat, so they also start in advantageous positions. You can even prepare the battle arena ahead of time by throwing barrels of oil onto the field, and their effects persist.

This tight integration between battles and exploration rewards player ingenuity and expression.

I didn’t mention it in my forum post, but it’s also worth noting that Traditional RPGs often transport the player to a different screen when battles start. This break in continuity can make it easy to lose track of your position in the exploration parts of the game. Which direction were you heading before the battle began? If you don’t remember, you may get frustrated as you wander around trying to re-orientate yourself.

Divinity OS2 has its battles and exploration take place on the same map with no break in between, perfectly avoiding this issue.

If you do find the battles too frequent, the game has well-integrated systems to allow the player to avoid combat. Some battles can be avoided by exploring to find a path around. Other battles can be snuck past using the stealth system. Still other battles can be avoided through taking a diplomatic stance in dialogue.

Divinity is a triumph of game design, and it’s a tough act to follow. I think most games opt for a simpler approach because it is much easier to achieve. Not everyone has a studio with 250+ employees.

The Mountain Pre-Alpha 0.0.2f

Lots of back-end changes to set the stage for future development.

But we’ve also got a new title screen!

And I’ve added a new window where players are offered 3 possible choices for an item award. This seems to be a better way of managing loot awards than getting completely random stuff with no choice.

Updates

  • New Title Screen
  • Updated PixiJs from 5.3.3 to 6.1.1
  • Switched from Pako to fflate (smaller file size, faster performance)
  • Fixed issue with tooltips where they could not contain multiple lines
  • Refactored plugins I’ve authored so I have a core set I can share between projects
  • Created new “award choice” plugin
  • Various tweaks to attempt to get better performance
  • Fixed issues that popped up with Ogg sound files on Safari
  • Fixed issues with running as a Progressive Web App (PWA)
Updated Title Screen

Known Issues

  • If you start a new game, then quit before changing maps or opening the menu, then attempt to load your save game, the save game will be corrupted – To bypass this bug, just choose New Game instead of Continue.

The Club 0.9.4k

More technical updates. I’m chasing better performance on lower powered devices.

Updates

  • Updated PixiJs from 5.3.3 to 6.1.1
  • Switched from Pako to fflate (smaller file size, faster performance)
  • Fixed issue with tooltips where they could not contain multiple lines
  • Refactored plugins I’ve authored so I have a core set I can share between projects
  • Switched from pixel movement to half-tile movement in an attempt to get better performance in the map scene
  • Various code tweaks to attempt to get better performance
  • Fixed issues that popped up with Ogg sound files on Safari
  • Fixed issues with running as a Progressive Web App (PWA)

Known Issues

  • If you start a new game, then quit before the first cutscene ends, then attempt to load your save game, the save game will be corrupted – To bypass this bug, just choose New Game instead of Continue.

Fine Print

I’m trying to get my ducks in a row in terms of legal documentation for my games. I’m not a lawyer, so I’m not going to be able to produce perfect legal jargon. Instead, I’d like to try to avoid problems by making it as human-readable, clear and unambiguous as possible.

Below is what I’ve got so far, plucked and remixed from a few major software providers. Keep in mind this is a work-in-progress draft and is not definitive. The documentation that ships with the final game will supersede anything written here.


Health & Safety

Please pay attention to your health while playing, and respect your limits.

Seizures

Some portion of the population may experience seizures or blackouts triggered by flashing lights or patterns. Full-screen light flashes may be disabled in the in-game Options menu, but doing so cannot guarantee you will not experience symptoms. If you have an epileptic condition, consult a doctor before playing.

For more information about seizures, contact your doctor or local health authority.

Repetitive motion injuries and eyestrain

Interacting with video games may cause discomfort in the hands, wrists, arms, or eyes. To reduce your chance of injury, consider taking frequent breaks. If you have symptoms of a repetitive motion injury or eyestrain, don’t play until you’re feeling better. If symptoms persist, contact a doctor.

For more information about repetitive motion injuries and eyestrain, contact your doctor or local health authority.

Motion sickness

Some people may experience motion sickness while playing. Several motion-related effects may be disabled in the in-game Options menu, but doing so cannot guarantee you will not experience symptoms. If you become sick, stop playing and refrain from strenuous activity until you have recovered.

Privacy Statement

This game periodically collects and sends analytics data to a remote server. This data is used to measure interest in the game, and inform changes to improve the game (such as difficulty balance.) No effort is made to uniquely identify players of the game.

What Device Data is Collected

  • Your IP address (the last two bytes are masked)
    • Your location based on IP address: country, region, city, approximate latitude and longitude
  • Date and time of the request
  • Screen resolution being used
  • Main Language of the browser being used (Accept-Language header)
  • User Agent of the browser being used (User-Agent header)
    • From the User-Agent, we detect the browser, operating system, device used (desktop, tablet, mobile, tv, cars, console, etc.), brand and model.

What Game Data is Collected

  • Version ID of the game
  • If the game is in play test mode
  • Current game play time
  • Window resolution of the game
  • The name of the troop being fought when battles are started, fled, won, or lost
  • The ID of the map being loaded
  • The stack trace when an error occurs
  • The actors in your party, their levels, classes, and equipment
  • Your count of some in-game resources, such as hours remaining

How is Data Anonymized?

  • The last 2 bytes of your IP address are masked. Geolocation will be inaccurate.
  • No information is sent about individual installs and no effort is made to differentiate individual users.

Data Retention

  • all visits and actions raw data are deleted after 24 months 10 days.
  • all aggregated reports are deleted after 2 years.

Data Erasure Requests

Because all data recorded is anonymized, it cannot be attributed to an individual person. Therefore, there is no way to request download or deletion of an individual person’s data.

EULA

By purchasing and/or downloading and using this Game, you agree, without reservation to be bound by the terms of this EULA. If you do not agree with the terms of this EULA, please do not purchase and/or download and use this Game.

Definitions

In this EULA the expressions below shall have the meaning assigned to them in this clause, unless the context requires otherwise:

  • This “Game”: the software application and/or all of the contents of the files and/or other media, including software setup files, licensed to You by the Creator.
  • The Game’s “Creator”: Nathan Bolton.
  • “Use”: the access, download, install, copy or benefit from using this Game.
  • “You”: you, the final and ultimate user of this Game or the authorized representative of a company or other legal entity that will be the final and ultimate user of this Game, and the company or other legal entity that will be the final and ultimate user of this Game, if applicable.

License

  1. The Game’s Creator grants You a non-exclusive, non-transferable, limited, revocable license to Use this Game in accordance with this EULA. The Game’s Creator reserves all rights not expressly granted to You.
  2. This Game’s Creator is and remains the owner of any intellectual property rights with respect to this Game. You shall not acquire any ownership to this Game as result of Your purchase of or Your Use of this Game.

Disclaimer

  1. You Use this Game at Your own risk and the entire risk as to satisfactory quality, performance and accuracy is with You.
  2. This Game and accompanying documentation are provided on an “as is” and “as available” basis without warranty – express or implied – of any kind, and the Creator specifically disclaims the warranty of fitness for a particular purpose. No oral or written advice given by the Game’s Creator, its dealers, distributors, agents or employees shall create a warranty or in any way increase the scope of this warranty and You may not rely upon such information or advice.

Liability limitation

This Game’s Creator and any third party that has been involved in the creation, production, or delivery of this Game are under no circumstances liable for consequential or indirect damages (including damage for loss of profit, business interruption, loss of data, and the like arising out of the use or inability to use this Game).

Resolution

I’ve made The Club 960×540 resolution. This is exactly half the common resolution 1920×1080.

I’ve made The Mountain 960×528. The game is made up of 48×48 tiles, so I’ve chosen a resolution that is a multiple of 48.

The earliest screenshots in this blog show a much larger 1024×624 resolution. I decided to switch to a smaller size because:

  1. 1024×624 isn’t a standard resolution
  2. It’s less performance-intensive to push fewer pixels
  3. RPG Maker MV doesn’t have a native way of setting a larger resolution (it must be done through 3rd-party plugins), which is a good indication that it was never designed for that use case
  4. A smaller resolution is a better fit for each of my game’s chunky, pixelated looks
  5. It’s a more interesting challenge to work around the restraint of having a small resolution, rather than increasing the size and cramming more information onto the screen to fill the space