DEV Community

Cover image for GameMaker setup with Typescript and VS Code
Oleksandr Demian
Oleksandr Demian

Posted on

GameMaker setup with Typescript and VS Code

I wrote my first line of code back in 2014, when I tried Unity 3D to create a simple game for fun. I didn't have any programming experience at the time, but I loved video games. I made a couple of them back then, nothing commercial, just some simple games I could build in 4-5 days to play with friends for a few hours before moving on.

I eventually got into programming professionally and landed a job as a Software Engineer, which meant putting game dev aside and, plot twist, focusing on work.

Twelve years have passed since then (time flies), and the other day I was reading a book about the video game industry. Once I finished it, I decided to try making a game again. I didn't want to use Unity or Godot, as they felt like "too much" for what I could realistically make. Instead, I leaned towards GameMaker, which helped limit any unrealistic scope of a "super insane 3D game" I might get tempted by later.

GameMaker was perfect for a simple 2D game (plus some of my favorite games are made in GM). I managed to put together a playable top-down shooter in just one evening. Since it is super focused on 2D development, everything is intuitive and easy to use. After spending a decade working as a full-stack dev doing hundreds of different things (writing code, optimizing SEO, CI/CD, DB migrations, analytics, scalability...), I fell in love with GameMaker. I could finally write code for fun again.

However, there were some trade-offs: GML.

GML is the programming language used in GameMaker. If I had to briefly describe it, I would say: "It's an archaic version of JavaScript". While I believe it is great for people who first start writing code, but for me there are some big no-go:

  • Typesafety: there are no types, you can use comments similarly to JSDoc, but I don't think it is good.
  • Built-in code editor: this is the biggest issue for me personally. It is eons behind VS Code and **JetBrains **products in terms of DX experience.

As a certified "**TypeScript **lover" I said to myself: I can fix her this.

The idea was simple: "In and out, 20 minute adventure." I would take TypeScript code, run it through a transpiler, and post-process whatever needed fixing (for example: thisdoes not exist in GML; it uses self).

Obviously, things weren't that simple. But after a weekend (and a sleepless night), I managed to pull it off. I was finally able to write TypeScript code and make it work in GameMaker.

My initial PoC used a script in watch mode to compile TS code on the fly, but an incredible coincidence happened: two days after I started working on this tool, GameMaker released a new version of their IDE. It added support for build hooks, which allowed me to get rid of the "watcher" and simply transpile the code the moment you hit the "Run" button.

The actual thing

What I came up with is a CLI tool (open source, NPM) that can be installed globally.

Setup

Install the package globaly (-g):

npm i -g @odemian/gamemaker-typescript
Enter fullscreen mode Exit fullscreen mode

It has 2 commands:

  • setup: Prepares the GameMaker project you are currently in to use TypeScript (creates tsconfig.json, links types, and configures pre_project_step to automatically transpile).
  • compile: Manually compiles the project. This is useful if you've added assets like sprites or sounds, as this command will generate their respective types.

Project configuration and the TS first object

After the library has been installed, open a GameMaker project in VS Code (or whatever code editor) and run:

gmts setup
Enter fullscreen mode Exit fullscreen mode

Create an object:

Create GameMaker object

This will create a file at objects/obj_player/obj_player.yy. The .yy file contains the metadata for the object. Place your script in the same folder and give it any name (I call mine code.ts):

Write typescript code

You can think of objects/obj_player as folder-based routing: each object is a folder in the objects directory, and you write your code right there.

Here is the actual code if you want to copy it:

class Player extends GMObject {
  _movement_speed: number;

  // inside of the defineObject you can declare all of your events, like onCreate, onStep, onDraw etc...
  onCreate() {
    // use "this." keyword to access object properties in a safe fully typed way
    this._movement_speed = 2;
  }

  onStep() {
    var _hspd = keyboard_check(vk_right) - keyboard_check(vk_left);
    var _vspd = keyboard_check(vk_down) - keyboard_check(vk_up);

    if (_hspd != 0 || _vspd != 0) {
        var _dir = point_direction(0, 0, _hspd, _vspd);
        this.x = this.x + lengthdir_x(this._movement_speed, _dir);
        this.y = this.y + lengthdir_y(this._movement_speed, _dir);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

If you've ever used GameMaker, you might wonder: "Wait, a Player class?" Yes, OOP baby.

Most other game engines (e.g., Unity, Godot) use this approach where each object's code is encapsulated in a class. I experimented with different approaches, but I eventually settled on classes. They feel like the most natural way to write game code. In the ten years I've been working with TypeScript, I've almost never used the class keyword, it rarely feels right in modern front-end or back-end development. But for a game, it's a perfect match.

The class inherits all the standard events from GMObject:

  • Create -> onCreate
  • Step -> onStep
  • Draw -> onDraw
  • and so on, you get the idea

Run it

After creating the Player object and writing the code, we can run it (I added a sprite to my obj_player behind the scenes):

Run game

How GameMaker understands TS

If you are into GameMaker, you might wonder if it uses the newest GMRT runtime. The short answer is no. As of the time of writing, GMRT is still in beta, and it is not clear when it will be released or in what form.

What currently happens is:

  • When you set up a project using gmts setup, it configures a pre_project_step script to run gmts compile before building the project.
  • When you press "Run", it looks for all script and object resources and compiles any .ts files it finds in the resource folder.
  • Events like onCreate and onStep are extracted into Create_0.gml and Step_0.gml (scripts that GML understands). Other methods are then assigned to the instance in the Create event.
  • GameMaker builds the project by reading all of the generated .gml files.

The cool thing about this setup is that if you start your project with TS but at some point change the idea, you can simply remove .ts files, and keep .gml (they remain in readable form).

Top comments (0)